Dirk
Dirk

Reputation: 165

Angular 2: ngOnChanges fires when template renders

When my template renders the function inside ngOnChanges fires one time, and then only when an @input changes. Is this the expected behaviour and how can I prevent it?

Child:

export class MobileMenuComponent implements OnInit, OnChanges {

@Input('test') dezeTest: string;

//declare menu
menu;

constructor() { 

    //define menu

    this.menu = {state: 'inactive'};

}

togglemenu() {

    var that = this;

    if (that.menu.state === 'inactive'){

        that.menu.state = 'active';

    }

    else {

        that.menu.state = 'inactive';

    }

}

ngOnChanges(changes: SimpleChanges) {


    this.togglemenu();


}
}

Upvotes: 2

Views: 12125

Answers (3)

ggranum
ggranum

Reputation: 1060

To expand on existing answers, and address a typing question raised in a comment at the same time:

The SimpleChange#firstChange field exists for this exact case.

Alternatively, because the value is set on your Component before ngOnChanges is called, you can also check if a field has changed, followed by if it's set:

ngOnChanges(changes: { myField: SimpleChange }) {
    if(changes.myField && this.myField){ 
        // do a thing only when 'myField' changes and is not nullish.
    }
    // Or, if you prefer: 
    if(changes.myField && changes.myField.firstChange){ 
        // Do a thing only on the first change of 'myField'.
        // WARNING! If you initialize the value within this class 
        // (e.g. in the constructor)  you can get null values for your first change
    }
}

Another little warning: If you were to use tools like WebStorm to rename 'myField' on your Component, the 'myField' of the ngOnChanges method parameters ({myField: SimpleChange }) will NOT be updated. Which can lead to some fun Component initialization errors.

Upvotes: 3

Oliver Renner
Oliver Renner

Reputation: 563

As Dylan Meeus suggested, its the normal behaviour. but i would suggest a different solution, which takes advantage of the passed SimpleChange object. it contains a previousValue and a currentValue.. initially, the previousValue is not set. https://angular.io/docs/ts/latest/api/core/index/SimpleChange-class.html

ngOnChanges(changes : any){
   if(changes.menu.previousValue){
      this.togglemenu();
   }
}

additionally, take care about OnChanges, since it fires on every input param... (you might add some more in the future)

Upvotes: 1

Dylan Meeus
Dylan Meeus

Reputation: 5802

This is the normal behaviour of ngOnChanges.

The ngOnChanges method will fire the first time because your properties have been checked, and subsequently fire when a property is updated. From the documentation, you can see

ngOnChanges is called right after the data-bound properties have been checked and before view and content children are checked if at least one of them has changed.

If you want to change it, you need to consider how you want to change it. That is not very clear from your question, but if you want to prevent the ngOnChanges from firing again, when a property is updated (I think that you want this because of your toggleMenu() you might consider using the ngOnInit() instead of ngOnChanges(). Alternatively, you can block the togglemenu(); after the first time.

 firstrun : boolean = true; // class variable
 ngOnChanges(changes : SimpleChanges){
    if(firstrun){
      this.togglemenu();
      firstrun = false;
    }
  }

Alternatively, as hinted at earlier, another lifecycle hook might suit your needs better.

Upvotes: 6

Related Questions