12

I am having an issue with ngOnChange. I have following component:

@Component({
    selector:'user-table',
    template: `...`,

})

export class UserTable implements OnChanges{
    @Input() users: User[];
    years:string[];
    constructor(private _usersCollection:UsersCollection){
    }

    ngOnChanges(){
     if (this.users.length)
       {this.years =this._usersCollection.createYearsArray(this.users)}
    }
}

However, if condition only gets checked once - when this.users is not yet fetched from the server, and hence its length is 0. How can I find solution to deal with this kind of async inputs?

The array is updated, as when I set the following logs:

    console.log('ON FIRST INIT' , this.programs);
    this.years = this._usersCollection.createYearsArray();
    console.log(this.years);
    setInterval(()=>{
        console.log('IN INTERVVAL' , this.programs);
    },1000);

The console output is:

ON FIRST INIT []
UsersTable.component.ts:21 []
UsersTable.component.ts:23 IN INTERVVAL [Object, Object, Object, Object]
uksz
  • 18,239
  • 30
  • 94
  • 161

2 Answers2

13

If you don't need to execute any logic when your input property changes (e.g., you only use the property in template bindings), you don't need to do anything. Angular will automatically propagate new values down from the parent to the input property.

If you want to execute some component logic when the input property changes, use ngOnChanges(), which is called whenever any component input property changes.

Since Angular uses === to detect changes (well, there's some special handling for NaN too), this means that

  • for reference types (Array, Object, Date, etc.), the reference (i.e., the array, object, etc. reference) must change. E.g., myArray = someNewArray;
    If only an item in the array changes, ngOnChanges() is not called. E.g., for a change such as myArray[0].name = newName;, ngOnChanges() is not called.
  • for primitive types (number, boolean, string), this simply means that the value must change. E.g, myNumber = 5; or myNumber = newNumber;

Another option is to implement your own change detection logic using ngDoCheck(). See this answer for an example. That lifecycle hook is called "every time that the input properties of a component or a directive are checked. Use it to extend change detection by performing a custom check" -- from lifecyle hooks.md

Community
  • 1
  • 1
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • Should this be detected by Object.assign(this.users,success.json()) ? – uksz Mar 14 '16 at 15:35
  • @uksz, `Object.assign()` does not create a new array, it modifies the existing `this.users` array. So, `ngOnChanges()` will not be called. – Mark Rajcok Mar 14 '16 at 15:46
  • Great, thanks! so this was actually the issue. Btw, how did you learned about the change detection in angular2? I've read ngbook2 and there was very little mentioned about it. – uksz Mar 14 '16 at 15:48
1

ngOnChanges() gets called when users are updated. You just need to make sure a new array is assigned users in the parent component instead of filling an existing array. Otherwise Angulars change detection won't recognize the change.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Actually, array is changed. Edited my question with some more console logs. – uksz Mar 14 '16 at 15:06
  • Is it wise to use a setter on @Input when I have multiple async inputs and different logic to execute? ngOnChanges() would work fine but I need to check which property has changed. – Gambo Jul 06 '17 at 15:31
  • Not sure without full details about what you try to accomplish. If your logic depends on multiple inputs being changed, `ngOnChanges` might be more convenient in some cases, but otherwise I think a setter is usually more convenient. – Günter Zöchbauer Jul 06 '17 at 15:36