Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ngOnChanges not firing when input property changed

Tags:

angular

Can you programatically trigger angular's change detection when mutating a component property in angular2?

@Component({
   selector: 'my-component', 
})
class MyComponent implements OnChanges {
   @Input() message: string;

   ngOnChanges(changeRecord) {
      for (var change in changeRecord) {
         console.log('changed: ' + change);
      }
   }

   doSomething() {
     // I want ngOnChanges to be called some time after I set the 
     // message. Currently it is only called if the host element
     // changes the value of [message] on the element.
     this.message = 'some important stuff';
   }
}
like image 966
ovangle Avatar asked Jan 02 '16 02:01

ovangle


People also ask

Why ngOnChanges is not getting called?

That's because ngOnChanges is called by Angular as the component is instantiated. It is also invoked before ngOnInit in case you were unaware.

Does detect changes trigger ngOnChanges?

Use ngOnChanges whenever you want to detect changes from a variable decorated by @Input. Remember that only changes from the parent component will trigger this function. Also remember that changes from the parent still update the child value even without implementing ngOnChanges.

What specifically does Angular monitor when it determines whether or not to fire ngOnChanges?

During change detection, when Angular checks components' input properties for change, it uses (essentially) === for dirty checking. For arrays, this means the array references (only) are dirty checked. Since the rawLapsData array reference isn't changing, ngOnChanges() will not be called.

How do you call ngOnChanges?

ngOnChanges only runs when the Input change comes from a template binding like <component [someInput]="aValue"> . If you set it manually like this component. someInput = aValue , that happens outside the change detection cycle and you need to let Angular know somehow that you've changed something.


2 Answers

The reason it doesn’t work can be found in the source code.

https://github.com/angular/angular/blob/885f1af509eb7d9ee049349a2fe5565282fbfefb/packages/core/src/view/provider.ts

Where ngOnChanges is called from, and where the SimpleChanges structure is built are very much tied into the component / directive code.

It’s not just a ‘change tracker’ running that looks over every property however it was set, so ngOnChanges only works for bindings set by parent components.

This is where ngDoCheck comes in and possibly KeyValueDiffers.

See also:

https://netbasal.com/angular-the-ngstyle-directive-under-the-hood-2ed720fb9b61 https://juristr.com/blog/2016/04/angular2-change-detection/

like image 158
Simon_Weaver Avatar answered Oct 16 '22 17:10

Simon_Weaver


I was having the same issue, and this is a simple but not very elegant workaround I am using. Pass in another property to force trigger ngOnChanges method

<div poll-stat-chart [barData]="barData" [changeTrigger]="changeTrigger"></div>

In the parent component class, whenever you want to manually fire the ngOnChanges method on child component, just modify "changeTrigger" property

ParentComponent Class (poll-stat-chart is the child component)

     @Component({
        directives: [PollStatChartCmp],
        template: `
            <div poll-stat-chart [barData]="barData" [changeTrigger]="changeTrigger">
            </div>
            <button (click)="triggerChild()"></button>
        `
      }
    export class ParentComponent {
        changeTrigger = 1;
        barData = [{key:1, value:'1'}, {key:2, value'2'}];
        triggerChild() {
            this.barData[0].value = 'changedValue';

            //This will force fire ngOnChanges method of PollStatChartComponent
            this.changeTrigger ++ ;           
        }

    }

And then in child component class, add a property [changeTrigger]

    @Component({
        selector: '[poll-stat-chart]',
        inputs: ['barData', 'changeTrigger'],
        template: `
            <h4>This should be a BAR CHAR</h4>
        `
    })
    export class PollStatChartCmp {
        barData;
        changeTrigger;
        constructor(private elementRef: ElementRef) {
            this.render();

        }

        ngOnChanges(changes) {
            console.log('ngOnChanges fired');
            this.render();
        }

        render() { console.log('render fired');}

}
like image 32
Yong Wang Avatar answered Oct 16 '22 16:10

Yong Wang