0

I have a question related to the following code:

Child

export class ChildComponent implements OnChanges {
    public @Input() data: string[];

    ngOnChanges(changes: SimpleChanges) {
        console.log('I am here');
    }
}

Child Template

{{ data | json }} // This is not necessary. The behaviour is the same with or without it.

<div *ngFor="let item of data">
    {{ item }}
</div>

Parent

export class AppComponent implements OnInit {
    public test: string[] = ['hello'];

    ngOnInit() {
        console.log('I am here');
    }

    public addItem() {
        this.test.push('sample');
    }
}

Parent Template

<child-component [data]="test"></child-component>
<button (click)="addItem()">Add</button>

I understand that ngOnChanges will only be called when the array reference changes and if I push an element to the array it will not be called. The following catches my attention. When I press the add button, a new element is added to the array and the view is updated correctly. I use the json pipe and an ngFor to prove that it updates correctly. If I not use json pipe, behaviour is the same. So, if the view is updated correctly, why isn't ngOnChanges called? Who is responsible for detecting that there is a change to show it in the view?

In summary, the doubt is as follows. Why when an element is added to the array the view is updated correctly but why is ngOnChanges not called?

Change detection is done differently?

Mr. Mars
  • 762
  • 1
  • 9
  • 39

1 Answers1

2

As You might be well aware ngOnChanges is designed for handling any changes in @Input() properties (in case that the simple feed of the new data to the component might have some additional effects and cannot be done by combining set and @Input()). So it is focused only on @Input() changes. Also as You well aware this will update only if the reference is about to change.

And Your component gets updated anyway because:

  • You are not telling the component to use only OnPush change detection strategy (so it will change not only on those reference changes of any @Input() or on any observable/event)
  • and in addition (which is a key here) You have a inpure pipe inside Your child view. Here I found some question about dirty pipes and I think it should answer You question: link (as json pipe is inpure - docs). Try to delete it and see what will happen.

Please kindly accept the fact this is just a theory. But I am quite sure that it is a right one.

Edit: oh, and it seems like there is a perfect example of that in official docs here:

...the flying heroes display updates as you add heroes, even when you mutate the heroes array.

Eatos
  • 444
  • 4
  • 12
  • Thanks for your answer. One thing, I think the last link is not correct, as it leads to the Json Pipe documentation. Could you share the correct link? :) With respect to this, even if you delete the json pipe, the ngFor works correctly, that is, it updates the information in the view. The behavior is the same with json pipe or without it. So, are there more detections of changes in the view than in the @Input (ngOnChanges)? Thanks in advance. – Mr. Mars Mar 04 '20 at 18:55
  • Yea, sorry, link updated. As for the problem, I've created an [example](https://stackblitz.com/edit/angular-symwdx?file=src%2Fapp%2Fchild%2Fchild.component.html) but to be honest I do not knowthe answer. I thought it was due to pipe triggering view change. Maybe I'll find something but this is the limit of my knowledge. – Eatos Mar 04 '20 at 21:12
  • Angular also looks for changes to data-bound values. That's what we know. I guess that Angular updates view because some of its binding's value changed so maybe it is cleaver enough to interprate `*ngFor` as set of individual bindings `{{ data[0] }}`, `[{{ data[1] }}`,... (as I tried in the example). I have found only [this](https://angular.io/guide/pipes#pipes-and-change-detection) and [this](https://stackoverflow.com/a/34799257/12739261). – Eatos Mar 04 '20 at 21:37