У меня есть текущий блок кода для создания динамических компонентов и генерации оглавления после добавления компонентов в DOM (служба делает такие вещи, как захват всех элементов h3 из DOM, чтобы их можно было добавить в оглавление):
generateDynamicComponents(): void {
this.someService.pageContent$
.subscribe((page: CmsDto.Page) => {
page.groups
.filter((group: CmsDto.Group) => !isNullOrUndefined(dynamicComponentMap()[group.type]))
.forEach((group: CmsDto.Group) => {
const componentFactory = this.componentFactoryResolver
.resolveComponentFactory(dynamicComponentMap()[group.type]);
const viewHost: ViewContainerRef = this.dynamicComponentHostDirective.viewContainerRef;
const componentRef = viewHost.createComponent(componentFactory);
(componentRef.instance as DynamicArticleBaseComponent).group = group;
});
// set timeout to get around the time of components being created and added to DOM.
setTimeout(() => this.tableOfContentsService.generateTableOfContents());
});
}
Есть ли способ узнать, когда эти компоненты созданы и готовы к выбору в DOM, без необходимости использования setTimeout? Я хотел использовать что-то вроде ContentChildren, но я не могу выбрать элементы h3, используя это, и вместо этого я застрял на document.querySelectorAll('h3');, поскольку вы не можете выбрать базовые элементы HTML, которые не помечены #id.
«(служба делает такие вещи, как захват всех элементов h3 из DOM, чтобы их можно было добавить в оглавление)» - вы ошибаетесь. Вы должны использовать для этого директивы.
@DanielWStrimpel да, я заставил его работать с помощью AfterViewChecked, но одной проблемой, с которой я столкнулся, была печально известная ExpressionChangedAfterItHasBeenCheckedError, поскольку в моем tableOfContentsService я испускаю события от ReplaySubject, которые другой компонент слушает и получает обновление, и поэтому, если это произойдет в течение жизненного цикла , Angular выдает ошибку.
@ LazarLjubenović не могли бы вы рассказать об этом немного подробнее? Не уверен, для чего я должен использовать директиву вместо этого.
Есть ли причина, по которой вы вручную пытаетесь пройти через DOM, чтобы увидеть, когда отображается контент? Ваши данные должны управлять вашими шаблонами, а не наоборот
Для запроса элемента h3. Используйте директиву с селектором h3, а затем используйте ContentChildren или ViewChildren для запроса всех его экземпляров у родительского компонента. Затем используйте эту информацию для создания оглавления.
@DanielWStrimpel Причина, по которой я прочесываю dom в службе, заключается в том, что это фактически HTML, отправленный из внутреннего API, который вводится как [innerHTML]. Обычно да, я бы не хотел этого делать, но поскольку HTML возвращается из службы, я подумал, что это мой единственный выбор.
@ LazarLjubenović Нужно ли мне прикреплять эту директиву ко всем элементам h3, которые я хочу добавить в оглавление? Моя проблема в том, что весь этот HTML-код для моей страницы исходит из внутреннего API, который я ввожу с помощью [innerHTML], поэтому у меня нет возможности добавлять директивы. Возможно, я неправильно понял.
@httpNick, не могли бы вы просто сделать это с текстом, который возвращается с сервера? Не могли бы вы просто создать div и засунуть его содержимое как innerHTML и использовать этот узел DOM для поиска и поиска h3, используя для этого методы запроса? Вы можете сделать это до того, как произойдет какой-либо рендеринг, поэтому исключите эту синхронизацию
@httpNick Он будет прикреплен сам по себе, если вы используете селектор h3.
@ LazarLjubenović, к счастью для меня, мои требования изменились, и теперь я получаю заголовок, отправленный из серверной части, что означает, что я добавляю его в оглавление. Я пробовал использовать директивный подход, в котором я добавил 'h3' в качестве селектора, но он не подключался к динамически сгенерированным h3. Может я что-то упускаю? Похоже, что в методе createComponent в ViewContainerRef есть параметр модуля, я должен передать модуль, который импортирует туда мою директиву?
Динамически генерируемый контент через innerHtml или аналогичный не запускает директиву Angular. Angular компилируется во время сборки.
Хм, правильно, поэтому я не думаю, что смогу использовать директиву в случае innerHtml. Это потребовало бы использования запросов к документам, но потом я снова узнаю, когда HTML отображается правильно?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Что касается setTimeout, вы можете заменить его на
this.ngZone.onMicrotaskEmpty.first()
.subscribe(() => {...});
См. Строку 446 в Исходный код Angular Material для дальнейшего объяснения.
Must wait for the message to be painted to the tooltip so that the overlay can properly calculate the correct positioning based on the size of the text.
Я думаю, что это тот же случай, и вам тоже нужно дождаться рендеринга dom.
Вы изучали возможность использования крючка жизненного цикла
AfterViewChecked?