Why angular 2 ngOnChanges not responding to input array push
Asked Answered
H

3

43

My angular application stuck with a issue, i used input as array and pushed a value to the array when the click event arise. But the ngOnChanges not firing when the array push is done. Is there a way to fire ngOnChange

My Code is ts file is

@Component({
  selector: 'adv-search',
  templateUrl: './app/modules/result.html'
})

export class InputComponent {
  @Input() metas:any=[];

  ngOnChanges() {
    console.log(this.metas);
  }
}

My Selector Tag

<adv-search [metas] = "metaIds"></adv-search>

Click Event Code

insertIds(id:any) {
   metaIds.push(id);
}
Hoyos answered 5/4, 2017 at 6:37 Comment(2)
where you have used insterIds event ?Hinayana
You change array in main component Or in your child component?Schell
B
86

Angular change detection only checks object identity, not object content. Inserts or removals are therefore not detected.

What you can do is to copy the array after each update

insertIds(id:any) {
  this.metaIds.push(id);
  this.metaIds = this.metaIds.slice();
}

or use IterableDiffer to check for changes inside InputComponent in ngDoCheck like NgFor does.

Bott answered 5/4, 2017 at 6:43 Comment(14)
whats the meaning of copy array here ?Hinayana
The array will be another one (another object instance) but with the original content. Angular will recognize this change.Buss
are you sure that by doing so angular will recognize the changes ?Hinayana
@PardeepJain thanks for the hint, didn't pay attention. Updated my answer.Buss
is this the same reason that angular won't rerender for a pure pipe?Iodate
@Iodate yes, the same reason. Angular calls a pipe when the value or parameters passed to the pipe change. If it doesn't recognize a change, it won't call the pipe. An impure pipe is called every time change detection runs, no matter if there was a change.Buss
now it will work @GünterZöchbauer , well explained answer :)Hinayana
@GünterZöchbauer : I tried this, sure now it started reflecting the change everywhere, but it doesn't trigger the ngOnChanges function. This got triggered only when the value was first initialized. Why is this so?Hyatt
@TempO'rary ngOnChanges is not supposed to be called when you modify an @Input(), ngOnChanges() only is called when Angular detects for a binding like [metas]="xxx" that xxx was modified and updates @Input() metas;. You can just make metas a getter/setter and put your code in the setter, then it is called every time the setter is called by your custom code or because Angular change detection noticed a change.Buss
@GünterZöchbauer I tried the getter/setter thing as per your suggestion. See it here (plnkr.co/edit/NSDC8Wi4sKcC5ub1PINB?p=preview). However, it still does not go to the ngOnChanges. I find that there is a difference how reference type and primitive types work. In my case its an object like in the example. Can you please guide how to get this example working?Hyatt
What is the expected behavior? What doesn't work as you expect? Angular doesn't care about changes of properties of objects if the properties are not bound directly.Buss
Expected behaviour: I want to catch whenever value of the object changes somehow. I'm expecting it in the ngOnChange function. Similar to $watchCollection in AngularJS. However, it doesn't seem to happen that way. So, in the code that I've shared what needs to be changed?Hyatt
There is nothing like that in Angular, mostly for performance reasons. The best way is usually to have an Observable in the object and subscribe to it where you want to get notified. For arrays, there is angular.io/api/core/IterableDiffers. You can check the source of *ngFor about how to use it.Buss
Out of context... Wasted hours on this issue... Thanks!Palfrey
B
14

If you reassign your metaIds array, the ngOnChanges lifecycle event will be fired. You can deconstruct your array into a new array.

insertIds(id:any){
    this.metaIds = [...this.metaIds, id];
}
Bolshevist answered 12/7, 2017 at 19:59 Comment(0)
B
12

@GünterZöchbauer 's answer is completely correct. However, I would like to add a alternative/supplement to copying the whole array. Using the same example from the question.

<adv-search [metas] = "metaIds"></adv-search>

altered to

<adv-search [metas] = "{trackBy: metaIds.length, metaIds: metaIds}"></adv-search>

Where length here is the equivalent of the *ngFor trackBy function for the component. With this the ngOnChanges occurs with content length changes.

If used in combination with the *ngFor trackBy method there should be some performance benefits as the whole array's bound templates are not recalculated (no screen/content flickering).

Bikol answered 31/5, 2017 at 20:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.