Доступ к вновь добавленным элементам DOM после вызова createComponent в ViewContainerRef

У меня есть текущий блок кода для создания динамических компонентов и генерации оглавления после добавления компонентов в 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.

Вы изучали возможность использования крючка жизненного цикла AfterViewChecked?

Daniel W Strimpel 10.03.2018 00:51

«(служба делает такие вещи, как захват всех элементов h3 из DOM, чтобы их можно было добавить в оглавление)» - вы ошибаетесь. Вы должны использовать для этого директивы.

Lazar Ljubenović 10.03.2018 00:53

@DanielWStrimpel да, я заставил его работать с помощью AfterViewChecked, но одной проблемой, с которой я столкнулся, была печально известная ExpressionChangedAfterItHasBeenCheckedError, поскольку в моем tableOfContentsService я испускаю события от ReplaySubject, которые другой компонент слушает и получает обновление, и поэтому, если это произойдет в течение жизненного цикла , Angular выдает ошибку.

httpNick 10.03.2018 00:55

@ LazarLjubenović не могли бы вы рассказать об этом немного подробнее? Не уверен, для чего я должен использовать директиву вместо этого.

httpNick 10.03.2018 00:56

Есть ли причина, по которой вы вручную пытаетесь пройти через DOM, чтобы увидеть, когда отображается контент? Ваши данные должны управлять вашими шаблонами, а не наоборот

Daniel W Strimpel 10.03.2018 00:58

Для запроса элемента h3. Используйте директиву с селектором h3, а затем используйте ContentChildren или ViewChildren для запроса всех его экземпляров у родительского компонента. Затем используйте эту информацию для создания оглавления.

Lazar Ljubenović 10.03.2018 00:59

@DanielWStrimpel Причина, по которой я прочесываю dom в службе, заключается в том, что это фактически HTML, отправленный из внутреннего API, который вводится как [innerHTML]. Обычно да, я бы не хотел этого делать, но поскольку HTML возвращается из службы, я подумал, что это мой единственный выбор.

httpNick 10.03.2018 01:03

@ LazarLjubenović Нужно ли мне прикреплять эту директиву ко всем элементам h3, которые я хочу добавить в оглавление? Моя проблема в том, что весь этот HTML-код для моей страницы исходит из внутреннего API, который я ввожу с помощью [innerHTML], поэтому у меня нет возможности добавлять директивы. Возможно, я неправильно понял.

httpNick 10.03.2018 01:06

@httpNick, не могли бы вы просто сделать это с текстом, который возвращается с сервера? Не могли бы вы просто создать div и засунуть его содержимое как innerHTML и использовать этот узел DOM для поиска и поиска h3, используя для этого методы запроса? Вы можете сделать это до того, как произойдет какой-либо рендеринг, поэтому исключите эту синхронизацию

Daniel W Strimpel 10.03.2018 01:14

@httpNick Он будет прикреплен сам по себе, если вы используете селектор h3.

Lazar Ljubenović 10.03.2018 10:31

@ LazarLjubenović, к счастью для меня, мои требования изменились, и теперь я получаю заголовок, отправленный из серверной части, что означает, что я добавляю его в оглавление. Я пробовал использовать директивный подход, в котором я добавил 'h3' в качестве селектора, но он не подключался к динамически сгенерированным h3. Может я что-то упускаю? Похоже, что в методе createComponent в ViewContainerRef есть параметр модуля, я должен передать модуль, который импортирует туда мою директиву?

httpNick 14.03.2018 22:26

Динамически генерируемый контент через innerHtml или аналогичный не запускает директиву Angular. Angular компилируется во время сборки.

Lazar Ljubenović 14.03.2018 22:39

Хм, правильно, поэтому я не думаю, что смогу использовать директиву в случае innerHtml. Это потребовало бы использования запросов к документам, но потом я снова узнаю, когда HTML отображается правильно?

httpNick 14.03.2018 22:41
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
13
69
1

Ответы 1

Что касается 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.

Другие вопросы по теме