Угловые динамические компоненты и выражениеChangedAfterItHasBeenCheckedError - PullRequest
0 голосов
/ 01 мая 2018

Я пытаюсь написать компонент, который может динамически содержать разные компоненты. Моя цель - написать статью, в которой я мог бы написать абзац или добавить твит.

Это код для DynamicArticleComponent:

@Directive({
  selector: '[dynamic-query]'
})
export class QueryDirective {
  constructor(public viewContainerRef: ViewContainerRef) {}
}

@Component({
  selector: 'app-dynamic-article',
  template: 
  `<ng-container *ngFor="let element of elements">
      <ng-template dynamic-query></ng-template>
  </ng-container>`,
  styleUrls: ['dynamic-article.component.css']
})
export class DynamicArticleComponent implements AfterViewInit {

    @Input() elements: Element[];
    @ViewChildren(QueryDirective) queryDirectives;

    constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

    ngAfterViewInit() {
      this.queryDirectives.forEach((queryDirective: QueryDirective, index) => {
        const element = this.elements[index];
        const componentFactory = this.componentFactoryResolver.resolveComponentFactory(element.component);
        const containerRef = queryDirective.viewContainerRef;
        containerRef.clear();
        const newComponent = containerRef.createComponent(componentFactory);
        (<DynamicComponent>newComponent.instance).data = element.data; 
      });
    }
}

Это другие классы, используемые в коде выше:

export class Element {
    constructor(public component: Type<any>, public data) {}
}

export interface DynamicComponent {
    data: any;
}

У меня проблемы с рендерингом <ng-templates>. Он просто отображает комментарии и не изменяется после загрузки представления. Вот что отображается: enter image description here

Элементы правильно попадают в компонент. Моя идея состоит в том, чтобы визуализировать все шаблоны, затем получить их с помощью декоратора ViewChildren и отобразить элементы там, где они должны быть. Есть ли другое решение этой проблемы?

Кроме того, вот как элементы достигают DynamicArticleComponent:

enter image description here

Заранее спасибо.

1 Ответ

0 голосов
/ 01 мая 2018

Хорошо, с моим кодом возникли две основные проблемы. Первый был довольно тупой. Я не добавил директиву в объявления модуля приложения, поэтому она была как любое другое свойство html; Angular просто не ожидал этого, поэтому не искал. Однако после добавления его в модуль приложения он выкинул ExpressionChangedAfterItHasBeenCheckedError. Эта ошибка вызвана тем, что я меняю переменные после загрузки представления. Более подробное объяснение смотрите в этом блоге .

Итак, я извлек то, что делал внутри ngAfterViewInit, в свою собственную функцию и вызвал ее из обещания. Что это делает, так это создает микрозадачу, поставленную в очередь после выполнения синхронного кода. Чтобы узнать больше о микро и макро задачах в angular, посмотрите на этот пост: Я пересмотрел Зоны (zone.js) и вот что я нашел .

Вот как закончился код:

@Directive({
  selector: '[dynamic-query]'
})
export class QueryDirective {
  constructor(public viewContainerRef: ViewContainerRef) {}
}

@Component({
  selector: 'app-dynamic-article',
  template: 
  `<ng-container *ngFor="let element of elements">
    <ng-template dynamic-query></ng-template>
  </ng-container>`,
  styleUrls: ['dynamic-article.component.css']
})
export class DynamicArticleComponent implements AfterViewInit {

    @Input() elements: Element[];
    @ViewChildren(QueryDirective) queryDirectives;

    constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

    ngAfterViewInit() {
        Promise.resolve(null).then(() => this.renderChildren());
    }

    private renderChildren() {
      this.queryDirectives.forEach((queryDirective: QueryDirective, index) => {
        const element = this.elements[index];
        const componentFactory = this.componentFactoryResolver.resolveComponentFactory(element.component);
        const containerRef = queryDirective.viewContainerRef;
        containerRef.clear();
        const newComponent = containerRef.createComponent(componentFactory);
        (<DynamicComponent>newComponent.instance).data = element.data; 
      });
    }
}

Этот код полностью работает. Надеюсь, я помогу кому-нибудь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...