Angular - Exception when adding dynamic component

混江龙づ霸主 提交于 2019-12-24 00:53:26

问题


I have a simple test code for adding dynamic components with Angular 4.

@Component({
  selector: 'component',
  template: `

    <ul><li #item *ngFor="let number of list">{{number}}</li></ul>

    <ng-template #anchor> </ng-template>


    <ng-template #template>
      <li><input type="text" [(ngModel)]="myInput"/></li>
    </ng-template>`
})
class _Component {

  @ViewChild('template')
  template: TemplateRef<any>


  @ViewChild('anchor', { read: ViewContainerRef })
  anchor: TemplateRef<any>


  @ViewChildren('item', { read: ViewContainerRef })
  items: QueryList<ViewContainerRef>
  myInput='';
  list: number[] = [0, 1, 2, 3, 4]

  ngAfterViewInit() {
    this.anchor.createEmbeddedView(this.template)
  }

}

All this code is doing is adding a dummy template at the end.

But this code throws an Exception :

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: ''. It seems like the view has been created after its parent and its children have been dirty checked. Has it been created in a change detection hook ?

It is well descriptive exception. A view has been updated after already checked for changing.

But there are few things which I don't understand here :

Question(s) :

1:

If I remove the input from the template - so now the template is :

<ng-template #template>
    <li></li>
</ng-template>

— Then I don't get an exception. Why is that ?

2:

Another solution(among many) is to replace ngAfterViewInit with ngAfterContentInit. I already know the difference between those two.

If so - can I conclude ( from the fact that the exception is gone) that at each Angualr's event - a change detection is occurred? (which make sense becuase ngAfterViewInit happens _after_ ngAfterContentInit) , so maybe Angular has detected the prev dynamic change in ngAfterViewInit ? Am I right?

PLNKR


回答1:


If I remove the input from the template - so now the template is :

The input is not causing the problem. ngModel directive causes a problem. Directives and components instances are represented as view nodes inside angular - sort of children of the current component. During each change detection cycle, Angular updates inputs for these component/directive instances. Here is the excerpt from the Everything you need to know about change detection in Angular that shows the order of operations:

  • updates input properties on a child component/directive instances (1)
  • calls AfterContentInit and AfterContentChecked lifecycle hooks on child component/directive instances (5)
  • runs change detection for a child view (repeats the steps in this list) (8)
  • calls AfterViewInit and AfterViewChecked lifecycle hooks on child component/directive instances (9)

So assume that Angular is doing change detection for the AppComponent. It runs change detection for the _Component (6). There are no directives yet so nothing to check. Then Angular calls afterViewInit hook for the _Component, where you create a child directive instance ngModel. But the change detection for the directive ngModel is never triggered! After the current change detection cycle is finished, Angular checks for changes and finds that @Input of ngModel is an empty string, but the previous values is undefined since it was never checked.

Compare that with the case when you used AfterContentInit hook. Angular calls that hook for _Component. You create a child directive there. Angular runs change detection for the _Component. Now, the directive already exists so change detection is also run for this child directive as part of change detection for the _Component. Since Angular applied empty string initial value, no error occurs next time the directive is checked.



来源:https://stackoverflow.com/questions/44241057/angular-exception-when-adding-dynamic-component

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!