Angular2: Bagaimana cara memuat data sebelum merender komponen?

149

Saya mencoba memuat acara dari API saya sebelum komponen dirender. Saat ini saya menggunakan layanan API yang saya panggil dari fungsi ngOnInit komponen.

EventRegisterKomponen saya :

import {Component, OnInit, ElementRef} from "angular2/core";
import {ApiService} from "../../services/api.service";
import {EventModel} from '../../models/EventModel';
import {Router, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteConfig, RouteParams, RouterLink} from 'angular2/router';
import {FORM_PROVIDERS, FORM_DIRECTIVES, Control} from 'angular2/common';

@Component({
    selector: "register",
    templateUrl: "/events/register"
    // provider array is added in parent component
})

export class EventRegister implements OnInit {
    eventId: string;
    ev: EventModel;

    constructor(private _apiService: ApiService, 
                        params: RouteParams) {
        this.eventId = params.get('id');
    }

    fetchEvent(): void {
        this._apiService.get.event(this.eventId).then(event => {
            this.ev = event;
            console.log(event); // Has a value
            console.log(this.ev); // Has a value
        });
    }

    ngOnInit() {
        this.fetchEvent();
        console.log(this.ev); // Has NO value
    }
}

EventRegisterTemplate saya

<register>
    Hi this sentence is always visible, even if `ev property` is not loaded yet    
    <div *ngIf="ev">
        I should be visible as soon the `ev property` is loaded. Currently I am never shown.
        <span>{{event.id }}</span>
    </div>
</register>

Layanan API saya

import "rxjs/Rx"
import {Http} from "angular2/http";
import {Injectable} from "angular2/core";
import {EventModel} from '../models/EventModel';

@Injectable()
export class ApiService {
    constructor(private http: Http) { }
    get = {
        event: (eventId: string): Promise<EventModel> => {
            return this.http.get("api/events/" + eventId).map(response => {
                return response.json(); // Has a value
            }).toPromise();
        }     
    }     
}

Komponen dirender sebelum panggilan API dalam ngOnInitfungsi selesai mengambil data. Jadi saya tidak pernah bisa melihat id acara di template tampilan saya. Jadi sepertinya ini adalah masalah ASYNC. Saya mengharapkan pengikatan ev( EventRegisterkomponen) untuk melakukan beberapa pekerjaan setelah evproperti ditetapkan. Sayangnya itu tidak menunjukkan divditandai dengan *ngIf="ev"saat properti ditetapkan.

Pertanyaan: Apakah saya menggunakan pendekatan yang baik? Jika tidak; Apa cara terbaik untuk memuat data sebelum komponen mulai dirender?

CATATAN: The ngOnInitPendekatan yang digunakan dalam tutorial angular2 .

EDIT:

Dua solusi yang mungkin. Pertama adalah membuang fetchEventdan hanya menggunakan layanan API dalam ngOnInitfungsinya.

ngOnInit() {
    this._apiService.get.event(this.eventId).then(event => this.ev = event);
}

Solusi kedua. Seperti jawaban yang diberikan.

fetchEvent(): Promise<EventModel> {
    return this._apiService.get.event(this.eventId);
}

ngOnInit() {
    this.fetchEvent().then(event => this.ev = event);
}
Tom Aalbers
sumber

Jawaban:

130

memperbarui

asli

Ketika console.log(this.ev)dijalankan setelahnya this.fetchEvent();, ini tidak berarti fetchEvent()panggilan selesai, ini hanya berarti sudah dijadwalkan. Ketika console.log(this.ev)dijalankan, panggilan ke server bahkan tidak dilakukan dan tentu saja belum menghasilkan nilai.

Ubah fetchEvent()untuk mengembalikan aPromise

     fetchEvent(){
        return  this._apiService.get.event(this.eventId).then(event => {
            this.ev = event;
            console.log(event); // Has a value
            console.log(this.ev); // Has a value
        });
     }

ubah ngOnInit()untuk menunggu Promisehingga selesai

    ngOnInit() {
        this.fetchEvent().then(() =>
        console.log(this.ev)); // Now has value;
    }

Ini sebenarnya tidak akan membeli banyak untuk kasus penggunaan Anda.

Saran saya: Bungkus seluruh template Anda dalam format <div *ngIf="isDataAvailable"> (template content) </div>

dan masuk ngOnInit()

    isDataAvailable:boolean = false;

    ngOnInit() {
        this.fetchEvent().then(() =>
        this.isDataAvailable = true); // Now has value;
    }
Günter Zöchbauer
sumber
3
Sebenarnya, komentar pertama Anda bekerja dengan baik. Saya hanya menghapus seluruh fetchEventfungsi dan hanya menempatkan apiService.get.eventfungsi di ngOnInitdan itu bekerja seperti yang saya inginkan. Terima kasih! Saya akan menerima jawaban Anda segera setelah mengizinkan saya juga.
Tom Aalbers
Untuk beberapa alasan, saya menggunakan pendekatan ini pada Angular 1.x. IMHO, ini harus dianggap sebagai retasan alih-alih solusi yang tepat. Kita harus melakukan yang lebih baik di sudut 5.
windmaomao
Apa sebenarnya yang tidak Anda sukai tentang itu? Saya pikir kedua pendekatan itu baik-baik saja.
Günter Zöchbauer
57

Solusi bagus yang saya temukan adalah melakukan sesuatu di UI seperti:

<div *ngIf="isDataLoaded">
 ...Your page...
</div

Hanya jika: isDataLoaded benar halaman dirender.

pengguna2965814
sumber
3
Saya pikir solusi ini hanya untuk Angular 1 bukan Angular> = 2 karena aturan aliran data searah Angular melarang pembaruan ke tampilan setelah itu dibuat. Kedua kait ini menyala setelah tampilan komponen dibuat.
zt1983811
1
Div sama sekali tidak terpengaruh. Kami tidak bermaksud menghentikan rendering, kami ingin menundanya. Alasan itu tidak berhasil adalah karena tidak ada ikatan dua arah di angular2. @phil dapatkah Anda menjelaskan cara kerjanya untuk Anda?
ishandutta2007
1
ok saya gunakan ChangeDetectionStrategy.Push, mengubahnya ChangeDetectionStrategy.Defaultmenjadi dua cara terikat dengan template.
ishandutta2007
1
sudut 6. Bekerja.
TDP
49

Anda dapat mengambil data Anda terlebih dahulu dengan menggunakan Resolver di Angular2 + , Resolver memproses data Anda sebelum Komponen Anda dimuat sepenuhnya.

Ada banyak kasus di mana Anda ingin memuat komponen Anda hanya jika ada hal tertentu yang terjadi, misalnya menavigasi ke Dasbor hanya jika orang tersebut sudah masuk, dalam hal ini Penyelesai sangat berguna.

Lihatlah diagram sederhana yang saya buat untuk Anda untuk salah satu cara Anda dapat menggunakan resolver untuk mengirim data ke komponen Anda.

masukkan deskripsi gambar di sini

Menerapkan Resolver ke kode Anda cukup sederhana, saya membuat cuplikan agar Anda dapat melihat bagaimana Resolver dapat dibuat:

import { Injectable } from '@angular/core';
import { Router, Resolve, RouterStateSnapshot, ActivatedRouteSnapshot } from '@angular/router';
import { MyData, MyService } from './my.service';

@Injectable()
export class MyResolver implements Resolve<MyData> {
  constructor(private ms: MyService, private router: Router) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<MyData> {
    let id = route.params['id'];

    return this.ms.getId(id).then(data => {
      if (data) {
        return data;
      } else {
        this.router.navigate(['/login']);
        return;
      }
    });
  }
}

dan di modul :

import { MyResolver } from './my-resolver.service';

@NgModule({
  imports: [
    RouterModule.forChild(myRoutes)
  ],
  exports: [
    RouterModule
  ],
  providers: [
    MyResolver
  ]
})
export class MyModule { }

dan Anda dapat mengaksesnya di Komponen Anda seperti ini:

/////
 ngOnInit() {
    this.route.data
      .subscribe((data: { mydata: myData }) => {
        this.id = data.mydata.id;
      });
  }
/////

Dan di Route sesuatu seperti ini (biasanya di file app.routing.ts):

////
{path: 'yourpath/:id', component: YourComponent, resolve: { myData: MyResolver}}
////
Alireza
sumber
1
Saya pikir Anda melewatkan entri konfigurasi rute di bawah Routesarray (biasanya di file app.routing.ts ):{path: 'yourpath/:id', component: YourComponent, resolve: { myData: MyData}}
Voicu
1
@Voicu, ini tidak seharusnya mencakup semua bagian, tapi saya setuju, itu membuat jawaban lebih komprehensif, jadi saya menambahkan ... Terima kasih
Alireza
5
Hanya untuk mengoreksi bagian terakhir, parameter penyelesaian di rute harus menunjuk ke resolver itu sendiri, bukan tipe datanya, yaituresolve: { myData: MyResolver }
Chris Haines
1
Apakah itu MyData objek model yang didefinisikan di MyService?
Isuru Madusanka
1
solusi ini baik untuk mengambil data sebelum halaman dimuat tetapi tidak untuk memeriksa pengguna login atau peran pengguna. Untuk itu Anda harus menggunakan Guards
Angel Q