Angular ExpressionChangedAfterItHasBeenCheckedError внутри динамических компонентов

Я собираюсь динамически разрешать компоненты для разных операторов.

Итак, я создал одну директиву и один компонент для нее.

import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[mcmsDynamicHost]'
})
export class DynamicHostDirective {

  constructor(
    public viewContainerRef: ViewContainerRef
  ) { }

}
import { AfterViewInit, Component, ComponentFactoryResolver, Input, ViewChild } from '@angular/core';
import { DynamicHostDirective } from '@mcms/ui-kit/dynamic-host/dynamic-host.directive';

@Component({
  selector: 'mcms-dynamic-host',
  templateUrl: './dynamic-host.component.html',
  styleUrls: ['./dynamic-host.component.scss']
})
export class DynamicHostComponent implements AfterViewInit {

  @ViewChild(DynamicHostDirective) dynamicHost: DynamicHostDirective;

  @Input() component;

  constructor(
    private componentFactoryResolver: ComponentFactoryResolver
  ) { }

  ngAfterViewInit(): void {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.component);

    const viewContainerRef = this.dynamicHost.viewContainerRef;
    viewContainerRef.clear();
    const componentRef = viewContainerRef.createComponent(componentFactory);
  }

}
// component template
<ng-template mcmsDynamicHost></ng-template>

Теперь я использую их таким образом.

<div *ngFor="let item of dashboard">
   <mcms-dynamic-host [component]="item.card"></mcms-dynamic-host>
</div>
    this.dashboard = [
      {cols: 2, rows: 3, y: 0, x: 0, dragEnabled: true, card: WelcomeCardComponent, title: 'Welcome to Municipal CMS!'},
      {cols: 1, rows: 3, y: 0, x: 2, dragEnabled: true, card: SiteResumeCardComponent, title: 'Site Resume'},
      {cols: 2, rows: 5, y: 3, x: 0, dragEnabled: true, card: TrafficActivityCardComponent, title: 'Traffic activity'},
    ];

Статические компоненты визуализируются без каких-либо проблем. Но если я попытаюсь привязать некоторые значения к шаблону внутри динамически разрешаемых компонентов, например * ngFor или даже * ngIf, все выдает ошибку - ExpressionChangedAfterItHasBeenCheckedError

Но когда я проделал этот трюк,

ngOnInit(): void {
    setTimeout(() => {
      this.data = [
        { image: 'assets/images/green-icons/page-small.svg', title: 'Pages', count: 5 },
        { image: 'assets/images/green-icons/user-small.svg', title: 'Active Users', count: 7 },
        { image: 'assets/images/green-icons/blog-small.svg', title: 'Blog Post', count: 12, pending: 3 },
        { image: 'assets/images/green-icons/comment-small.svg', title: 'Comments', count: 54, pending: 8 },
        { image: 'assets/images/green-icons/media-small.svg', title: 'Digital Assets', count: 230 },
      ];
    }, 100);
  }

ошибок нет. Для меня это немного странно, в любом случае мы можем пойти с этим, но когда я пытаюсь использовать, например, компонент @ angular / material, нет никакого способа проделать такой трюк.

Кто-нибудь сталкивался с подобной проблемой?

Вот ссылка на stackblitz - https://stackblitz.com/edit/angular-kf3pdw


person Liu Zhang    schedule 07.03.2020    source источник


Ответы (2)


Попробуйте переместить назначение данных в ловушку жизненного цикла ngAfterViewInit вместо ngOninit

Кстати, динамическую загрузку компонентов обычно следует выполнять с помощью портала CDK.

person Guerric P    schedule 07.03.2020
comment
Да, верно, я тоже переместил код в ngOnInit. Спасибо за быстрый ответ. Проголосовали. В любом случае основной проблемой был static: true флаг @ViewChild декоратора. - person Liu Zhang; 07.03.2020

В моем коде была глупая ошибка. В Angular v9 статический флаг декоратора @ViewChild по умолчанию - false. Это означает разрешение результата запроса после запуска обнаружения изменений. Он должен быть true, чтобы разрешить результат запроса до запуска обнаружения изменений.

Обновился Stackblitz с правильным ответом. Надеюсь, этот опыт поможет другим! Ваше здоровье!

https://angular.io/api/core/ViewChild#description

https://angular.io/guide/static-query-migration

person Liu Zhang    schedule 07.03.2020