3

Regarding ChangeDetectorRef, I already know that -

detectChanges actually triggers change detection while with - markForCheck - The actual change detection for the component is not scheduled but when it will happen in the future (either as part of the current or next CD cycle)

Taken from here

Looking at markForCheck - if it's not scheduled, so when will it run? Obviously after Observables callback, async callbacks and setTimout and events.

The docs has a component with OnPush strategy

@Component({
  selector: 'cmp',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `Number of ticks: {{numberOfTicks}}`
})
class Cmp {
  numberOfTicks = 0;

  constructor(private ref: ChangeDetectorRef) {
    setInterval(() => {
      this.numberOfTicks++;
      // the following is required, otherwise the view will not be updated
      this.ref.markForCheck();
    }, 1000);
  }
}

Question:

If it marks for checking its ancestors, for the next cycle, so who ran the current cycle? (Is it the prev setTimeout invocation?)

Because this code is showing each second being replaced by another - in other words each second there is a change detection (?!).

What is actually going on here via a POV of steps?

Royi Namir
  • 144,742
  • 138
  • 468
  • 792

1 Answers1

5

markForCheck, as you guessed, and as the name suggests, will tell the Angular to mark the component to be change-detectable for the next cycle.

When you're writing setInterval, this is what you're actually writing:

constructor(private ref: ChangeDetectorRef) {
    setInterval(() => {
      this.numberOfTicks++;
      // the following is required, otherwise the view will not be updated
      this.ref.markForCheck();

      detectChangesFromZoneJS(); // this is a psudo code, but you can imagine something like this will happen, which is patched by NGZone
     // Note that this is the last function that will always be called by Zone at the end of all the async events

    }, 1000);
}

This is handled by ZoneJS.

Zone monkey patches all of the async events, and when they finish, Zone will notify Angular and then Angular. knows it's time to detect the changes ( update the view based on the latest model changes), but when you're component is OnPush is not going to detect the changes, unless there has special things happened inside the component, ( like a click event, or any of the @input's is changed).

So when you're deliberately saying markForCheck, you're basically saying: "I know you shouldn't detect the changes because it's OnPush only, but I'm telling you to detect it anyway"

So here is step by step:

  1. Component is initialized, Angular will detect all the changes, your view is updated

  2. You run a setInterval, inside it, you're mutating your model, Angular knows it shouldn't update the view because it's OnPush

  3. the first interval's callback gets called, we're inside the first function, you're marking the component to be deliberately checked, we're at the end of the interval function ( still the first one interval).

  4. Zone notify's Anguar that an Async event is just finished, time to detect the changes

  5. Angular looks at the OnPush and wants to ignore it, but remembers that you've marked the component to be checked by force

  6. the view get's updated

  7. we go to the second interval and so on.

Milad
  • 27,506
  • 11
  • 76
  • 85
  • So it's like I thought : the current cycle was ran by the prev setTimeout ? – Royi Namir Dec 21 '17 at 11:32
  • No, the current cycle is run by the end of current timeout – Milad Dec 21 '17 at 11:32
  • 1
    https://stackoverflow.com/questions/41364386/whats-the-difference-between-markforcheck-and-detectchanges/41364469#41364469 also could help understand better – Milad Dec 21 '17 at 11:47
  • if you want more clarification on NGZone https://stackoverflow.com/questions/45903493/zones-in-angular/45903716#45903716 – Milad Dec 21 '17 at 11:48