Jadi, Saya memiliki komponen yang merender beberapa komponen secara dinamis, dengan templat ini:
<div [saJquiAccordion]="{active: group.value['collapsed']}" *ngFor="let group of filterGroupsTemplate | keysCheckDisplay;">
<div>
<h4>{{group.key | i18n}}</h4>
<form id="ibo-{{group.key}}" class="form-horizontal" autocomplete="off" style="overflow: initial">
<fieldset *ngFor="let field of group.value | keys">
<ng-container *ngComponentOutlet="fieldSets[field.value.template];
ngModuleFactory: smartadminFormsModule;"></ng-container>
</fieldset>
</form>
</div>
</div>
Masalahnya adalah data yang dibutuhkan untuk mengisi komponen-komponen tersebut saya dapatkan dari panggilan API:
this.getFiltersSubscription = this.getFilters().subscribe(
(filters) => {
this.filters = filters;
log.info('API CALL. getting filters');
// Sending data to fieldform components
this.iboService.updateIBOsRankList(filters['iboRank'].data);
this.iboService.updateIBOsNewsletterOptions(filters['iboNewsletter'].data);
this.iboService.updateIBOsTotalOrders(filters['iboTotalOrders'].data);
}
);
Jadi, setelah saya memiliki data saya, saya memicu layanan Observable yang menjadi langganan komponen-komponen saya, dan mereka kemudian akan memproses data yang dikumpulkan.
MASALAH:
Jika panggilan API dilakukan sebelum semua komponen dimuat, saya akan memicu metode layanan ini melewati data tetapi tidak ada yang akan berlangganan Observable tersebut.
Pendekatannya adalah dengan:
Muat data terlebih dahulu, dan hanya ketika data sudah dimuat, saya akan merender templat dan, oleh karena itu, merender semua komponen ini secara dinamis dan hanya setelah itu saya akan memicu metode layanan ini (Observables).
Saya tidak ingin membuat panggilan API untuk setiap komponen, karena bisa jadi seperti 60 komponen, saya lebih suka kehilangan abstraksi untuk kode tetapi saya lebih suka melakukan sesuatu seperti ini:
// Listens to field's init and creates the fieldset triggering a service call that will be listened by the field component
this.iboService.initIBOsFilters$.subscribe(
(fieldName) => {
if (fieldName === 'IBOsRankSelectorFieldComponent') {
log.data('inside initIBOsFilters$ subscription, calling updateIBOsFilters()', fieldName);
this.iboService.updateIBOsRankList(this.filters['iboRank'].data); // HERE I'M PASSING DATA TO THE COMPONENT RENDERED DYNAMICALY. BUT IF this.filters IS UNDEFINED, IT BREAKS
}
}
);
Untuk melakukan ini, saya perlu memastikan bahwa this.filters
didefinisikan dan dengan demikian, saya sampai pada kesimpulan:
Bagaimana saya bisa menunggu sampai panggilan API berakhir dan this.filters
didefinisikan sebelum merender html template saya?
Maaf jika pertanyaan saya agak panjang, jika Anda membutuhkan detail lebih lanjut, beri tahu saya.
Terima kasih!
Setelah mempelajari berbagai pendekatan yang diberikan orang kepada saya, saya menemukan solusi pada pipa async
. Tetapi, saya butuh waktu beberapa saat untuk memahami cara mengimplementasikannya.
Solusi:
// Declaring the Promise, yes! Promise!
filtersLoaded: Promise<boolean>;
// Later in the Component, where I gather the data, I set the resolve() of the Promise
this.getFiltersSubscription = this.getFilters().subscribe(
(filters) => {
this.filters = filters;
log.info('API CALL. getting filters');
this.filtersLoaded = Promise.resolve(true); // Setting the Promise as resolved after I have the needed data
}
);
// In this listener triggered by the dynamic components when instanced,
// I pass the data, knowing that is defined because of the template change
// Listens to field's init and creates the fieldset triggering a service call
// that will be listened by the field component
this.iboService.initIBOsFilters$.subscribe(
(fieldName) => {
if (fieldName === 'IBOsRankSelectorFieldComponent') {
log.data('inside initIBOsFilters$ subscription, calling updateIBOsFilters()', fieldName);
this.iboService.updateIBOsRankList(this.filters['iboRank'].data);
}
}
);
Di dalam template, saya menggunakan pipa async
yang membutuhkan sebuah Observable
atau sebuah Promise
<div *ngIf="filtersLoaded | async">
<div [saJquiAccordion]="{active: group.value['collapsed']}" *ngFor="let group of filterGroupsTemplate | keysCheckDisplay;">
<div>
<h4>{{group.key | i18n}}</h4>
<form id="ibo-{{group.key}}" class="form-horizontal" autocomplete="off" style="overflow: initial">
<fieldset *ngFor="let field of group.value | keys">
<ng-container *ngComponentOutlet="fieldSets[field.value.template];
ngModuleFactory: smartadminFormsModule;"></ng-container>
</fieldset>
</form>
</div>
</div>
</div>
CATATAN:
async
membutuhkan Observable
atau Promise
dari apa yang saya pahami, itulah mengapa satu-satunya cara untuk membuatnya bekerja adalah dengan membuat Promise
resolver
karena itu digunakan ketika Anda tiba di komponen melalui routing Angular. Komponen ini adalah bagian dari komponen yang lebih besar dan tidak ditanamkan melalui routing seperti komponen normal lainnya. (Mencoba pendekatan itu, bekerja sedikit dengan itu, tidak berhasil)Anda bisa menggunakan resolver
untuk memastikan data-data tersebut dimuat (atau filter anda telah diinisialisasi) sebelum rute diaktifkan.
https://blog.thoughtram.io/angular/2016/10/10/resolving-route-data-in-angular-2.html