Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 4 detach change detection between children

I don't understand why even if I use ChangeDetectionStrategy.OnPush and changeDetectorRef.detach() the function ngDoCheck keep been called. I have thousands of components in my app, and I'd like to block the changeDetection of child1 if an event (mouse click, etc) has been raised from child2.

Here is a plunker

As you can see I have a father component

@Component({
  selector: 'my-app',
  template: `     
    <app-child1 test="test"></app-child1>
    <app-child2 test="test"></app-child2> `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent {
  test = 'test';

constructor() { }

  ngDoCheck() {
    console.log("### ngDoCheck app component");
  }
}

and 2 identical children:

@Component({
  selector: 'app-child1',
  template: `<input type="text" [(ngModel)]="test" /><br/> `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class Child1Component implements OnInit {
  @Input()
  test: string;

  constructor(public cd: ChangeDetectorRef) { }

  ngAfterViewInit() {
      console.log("###### DETACH child 1");
      this.cd.detach();

  }

  ngDoCheck() {
    console.log("### ngDoCheck child 1");
  }
}

If I start typing in the input of child1, the ngDoCheck function of child2 is called.

Think about having thousands of children, it get really slow...

Thank you!

like image 544
user2010955 Avatar asked Aug 10 '17 13:08

user2010955


1 Answers

That is the intended behavior, read Everything you need to know about change detection in Angular. Here is the quote:

  1. calls OnInit and ngDoCheck on a child component (OnInit is called only during first check)
  2. runs change detection for a child view (repeats the steps in this list)

So as you can see the ngDoCheck is always triggered on the child component. The OnPush check is performed after when trying to run change detection for the child component. Here is the quote:

Finally, change detection for the current view is responsible for starting change detection for child views (operation 8). This is the place where state of the child component view is checked and if it’s ChecksEnabled, then for this view the change detection is performed. Here is the relevant code:

viewState = view.state;
...
case ViewAction.CheckAndUpdate:
  if ((viewState & ViewState.ChecksEnabled) &&
    (viewState & (ViewState.Errored | ViewState.Destroyed)) === 0) {
    checkAndUpdateView(view);
  }
}

However, it will be called only for the top component on which the OnPush strategy is used. It will not be called on child components:

     normal component
           |
  child OnPush component  <---- ngDoCheck called only for this component
           | 
    child component 1     <---- ngDoCheck is not called for this component
           |
    child component 2     <---- ngDoCheck is not called for this component

Why is it triggered?

It's triggered to give you opportunity to perform your own custom logic inside this hook and choose to run change detection cycle once even if @Inputs haven't changed:

class OnPushComponent {
   constructor(private cd: ChangeDetectorRef) {}

   ngDoCheck() {
      if (some check) {
          this.cd.markForCheck();
      }
   }
}

Also see Angular ngDoCheck() gets called even with ChangeDetectionStrategy.OnPush this answer for real use case example.

like image 79
Max Koretskyi Avatar answered Sep 26 '22 13:09

Max Koretskyi