3

I'm reading about Zone.JS and the Angular change detection process, in the code source, Zone.JS notifies Angular about changes and run the verification in the first component from top to bottom by propagating the check into all components, but in my tests, I had peculiar behavior.

Check my sample:

http://plnkr.co/edit/QPHBQ7cYg9JFZr2oVnGZ?p=preview

The sample has the following architecture:

<app-component>
    <child-component>
        <grand-child-component>

They are nested within each other, none of them have @Input properties, and all of them are OnPush, and they display a simple value in the view like:

view:

{{getComponentValue}}

typescript

value = "app component";

get getComponentValue() { // <-- ES6 get to log when Angular check
    console.log('change detection checking here in component app-component')
    return this.value;
}

you can check this code in the sample.

When the application is started we can see the log:

check component app
check component child
check component grand child

Cool, but, now let's create a scenario if grand-child triggers an DOM event?

Changing the view in the grand child

<button (click)="undefined"> {{getComponentValue}} </button>  
<!-- (click)="undefined" trigger an event -->

And then we have the log:

check component app
check component child
check component grand child

I was expecting when I clicked on the grand-child button, the event would start in the app-component, But as all of them are OnPush, the event was not going to catch the grand-child component because no @Input properties weren't changed, and I would have to manually call the markforcheck or detectchanges, but the code above looks like I invoked the markforcheck.

I called settimeout, interval and events with observables within in grand-child component and none of them invoked changes (check the grand-component ts), only view events (click) the check is called...

The verification is happening, you can test e.g: in the child or in the app component you can add setInterval that increments a value And you can check the view, realize that only when the grand-child triggers the (click) values of your ancestors are updated!

Therefore are they forced by simulating a markForCheck() when a DOM event is triggered, actually is it happening?

1 Answers1

5

Yes, that's by design: a bound event triggers markForCheck internally

export function dispatchEvent(
    view: ViewData, nodeIndex: number, eventName: string, event: any): boolean {
  const nodeDef = view.def.nodes[nodeIndex];
  const startView =
      nodeDef.flags & NodeFlags.ComponentView ? asElementData(view, nodeIndex).componentView : view;
  markParentViewsForCheck(startView); // <== this line
  return Services.handleEvent(view, nodeIndex, eventName, event);
}

See also

yurzui
  • 205,937
  • 32
  • 433
  • 399
  • Thank you for the reply! Cool, in your link you said 4 cases that change detection is called with OnPush, but you have not forgotten the detectChanges()? It will trigger changes for him and his children, correct? – Gustavo Costa Jul 24 '17 at 19:15
  • @GustavoCosta It won't trigger changes on children with onPush strategy. https://plnkr.co/edit/O65ZZ8147J6eDVjreXaO?p=preview – yurzui Jul 24 '17 at 19:19
  • With detectChanges the check occurs only in the component with your bindings and your children and @Input properties will not be checked, is that it? – Gustavo Costa Jul 24 '17 at 19:32
  • My children which have OnPush strategy won't be checked at all if i run detectChanges on their parent. – yurzui Jul 24 '17 at 19:34
  • Cool, but if any children have properties and they change (new ref), the check will be run, see: https://plnkr.co/edit/67RYAoj7HiXZM0g9ubwl?p=preview – Gustavo Costa Jul 24 '17 at 20:02
  • @GustavoCosta Yes, in this case child will be checked – yurzui Jul 24 '17 at 20:02