2

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.

enter image description here

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

3
  • why do you insert into <ng-template #anchor? did you intent to use <ng-container? Commented May 29, 2017 at 11:39
  • @Maximus It doesn't matter (IMHO). Angular adds dynamic content AS A SIBLING. so it really doesn't matter . Commented May 29, 2017 at 11:40
  • I recommend you read Everything you need to know about change detection in Angular and also this Commented May 29, 2017 at 11:41

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

9 Comments

Maximus - thank you for your article. So Angular does not trigger change detection at each event method - Only at phases 3,7,8 ? ( In other words - in what angular-events - a change detection occurs?)
you probably have to read the entire article. all operations listed in the article are part of change detection for a particular view/component. you might have something different in mind when you say change detection, what do you mean by change detection?
Forget it :-). I will have to read it and if I'll have a question - I'll ping. (BTW - I did mention that I know the order difference of events - in my question. , the thing which I didn't know is whether angular triggers "change detection" at each event.
@Royi, yeah, read the article and follow up :). It's pretty detailed, I've spent considerable amount of time to investigate and write it. By the way, finishing another article on dynamic components. Follow me on medium to get notified :)
Sure. :-). Thanks.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.