Search code examples
angulardata-bindingangular2-changedetection

Why do i need to add markForCheck() to trigger change detection in Angular?


I don't quite understand why i need to add markForCheck() to the code below to make the changes visible. Why is my @Input() not triggering change detection?

I am refactoring my project to OnPush. These 2 components both have OnPush enabled. As i understand it, when this is enabled, and an Input() is changed (like messages) the change detection gets triggered.

In the code below i make a call via the graphqlService. When i receive the call i do some parsing with the incoming data and then set it to informationMessages property, which is binded to the child component cv-messages through its messages property.

The result is that the ngOnChanges function only get called once, when the informationMessages property gets initialized. But not when the final parsed data is set to it.

If I add markForCheck() it works fine.

Consider this parent component, with a template like this:

<cv-messages [messages]="informationMessages"></cv-messages>

And a typescript file with this piece of code:

informationMessages: InformationMessageType[] = [];

ngOnInit() {
    this.graphqlService.loadInformationMessages().subscribe(data => {
        const informationMessages: InformationMessageType[] = data;

        .... // parsing stuff

        this.informationMessages = informationMessages;
        // add markForCheck() here
    });
}

The messages component has an ngOnChanges function like this:

ngOnChanges(changes) {
    console.log(this.constructor.name ' CHANGE:', changes);
}

Update:

You can find the solution in the comments of an answer below. Basically, change detection is NOT triggered when an @Input() changes asynchronously. So in that case we need to add a markForCheck() to force a change detection.


Solution

  • As Angular docs says:

    When a view uses the OnPush (checkOnce) change detection strategy, explicitly marks the view as changed so that it can be checked again.

    Components are normally marked as dirty (in need of rerendering) when inputs have changed or events have fired in the view. Call this method to ensure that a component is checked even if these triggers have not occured.

    So this method is needed to to mark a component as dirty to be rerendered.

    UPDATE:

    There are two types of ChangeDetectionStrategy:

    OnPush: 0 Use the CheckOnce strategy, meaning that automatic change detection is deactivated until reactivated by setting the strategy to Default (CheckAlways). Change detection can still be explicitly invoked. This strategy applies to all child directives and cannot be overridden.

    Default: 1 Use the default CheckAlways strategy, in which change detection is automatic until explicitly deactivated.

    So when you use OnPush then automatic change detection is deactivated and it is necessary to mark the view as changed so that it can be checked again.