Search code examples
angularangular-changedetection

different ChangeDetectionStrategy.OnPush behaviour with embedded vs ng-content children


I am tweaking the performance of the application and came across following differences. Please see the stackblitz for the live example.

Components with the suffix -on-push have ChangeDetectionStrategy.OnPush, whereas components with the suffix -default have the default strategy.

I have two approaches which i named "embedded" for the first, and "ng-content" for the second. The clarification follows below.

root template:

<h1>embedded</h1>
<app-embedded-on-push></app-embedded-on-push>

<h1>ng-content</h1>
<app-ng-content-on-push>
  <app-ng-content-default></app-ng-content-default>
</app-ng-content-on-push>

Where app-embedded-on-pushs template just "embeds" the app-embedded-default component:

<app-embedded-default></app-embedded-default>

On the other hand, app-ng-content-on-pushs template projects the app-ng-content-default component with ng-content:

<ng-content></ng-content>

Please see the stackblitz in order to get a better idea.

expectation

Since components with ChangeDetectionStrategy.OnPush are only being checked (the component itself and its children) if the input references have changed, my expectation is that on the component itself and its children, the change detection won't be triggered with both approaches, named the embedded and the content-projection approach.

reality

With the embedded approach, the behaviour is as expected, change detection on app-embedded-default won't be triggered. On the other hand, with content projection (ng-content), the change detection of the child component of app-ng-content-on-push is being triggered, as you can see on the above stackblitz.

Can someone explain why this is the outcoming behaviour? The component tree is the same on both approaches, or am I wrong?


Solution

  • The reality is correct because if we take a look at the template:

    <app-ng-content-on-push>
      <app-ng-content-default></app-ng-content-default>
    </app-ng-content-on-push>
    

    we can't say that app-ng-content-default is the child component of app-ng-content-on-push but rather this component is projected into app-ng-content-on-push.

    This means that this component is a part of AppComponent template and will be checked when AppComponent template is checked. app-ng-content-on-push won't protect app-ng-content-default from checking.