Итак, у меня есть компонент, который динамически отображает несколько компонентов с помощью этого шаблона:
<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>
Дело в том, что данные, необходимые для заполнения этих компонентов, я получаю из вызова 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);
}
);
Итак, когда у меня есть данные, я запускаю сервис Observable, на который подписаны мои компоненты, и они обрабатывают собранные данные.
ПРОБЛЕМА:
Если вызов API будет сделан до загрузки всех компонентов, я буду вызывать эти сервисные методы, передавая данные, но никто не будет подписан на эти Observables..
Подход может быть следующим:
Сначала загрузить данные, и только когда данные загрузятся, я отрисую шаблон и, следовательно, отрисую все эти компоненты динамически, и только тогда я вызову эти методы обслуживания (Observables).
Я не хочу делать вызов API для каждого компонента, потому что это может быть около 60 компонентов, я скорее потеряю абстракцию fo code, но я предпочитаю сделать что-то вроде этого:
// 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
}
}
);
Чтобы сделать это, мне нужно убедиться, что this.filters
определен, и таким образом, я прихожу к выводу:
Как я могу дождаться окончания вызова API и определения this.filters
перед рендерингом моего шаблона html?.
Извините, если мой вопрос получился немного длинным, если вам нужно больше деталей, просто дайте мне знать.
Спасибо!
После изучения различных подходов, которые предлагали мне люди, я нашел решение на трубе async
. Но мне потребовалось время, чтобы понять, как его реализовать.
Решение:
// 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);
}
}
);
В шаблоне я использую трубу async
, которая требует Observable
или 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>
NOTE:
async
нуждается в Observable
или Promise
, насколько я понял, поэтому единственный способ заставить ее работать - это создать Promise
.resolver
, потому что он используется, когда вы приходите к компоненту через маршрутизацию Angular. Этот компонент является частью более крупного компонента, и он не инстанцируется через маршрутизацию, как любой другой обычный компонент. (Пробовал этот подход, немного поработал с ним, но он не помог).Вы можете использовать ``резольвер'', чтобы убедиться, что эти данные загружены (или ваши фильтры инициализированы) до активации маршрута.
https://blog.thoughtram.io/angular/2016/10/10/resolving-route-data-in-angular-2.html