6

Everywhere it is said that markForCheck just marks current component view and all parent components (up to the root one) as dirty. So next time DetectChanges executes it will update the view. From this point I have 2 questions. Both in a context that component has changeDetection: ChangeDetectionStrategy.OnPush

1) If 'async pipe' does nothing but call markForCheck (source code), Why the view is updated?

2) If I try calling markForCheck inside some async process, the view will be updated as well.

The demo: stackblitz

Can you help me to understand what happens during these processes and why the view actually updated? I am expecting that someone call DetectChanges method after 1) and 2), but who...

simply good
  • 991
  • 1
  • 12
  • 26
  • Does this answer your question? [What's the difference between markForCheck() and detectChanges()](https://stackoverflow.com/questions/41364386/whats-the-difference-between-markforcheck-and-detectchanges) – ruth May 25 '20 at 15:02
  • 1
    Angular uses `zone.js` to patch browser APIs and events (`setTimeout`, `click`, `xhr` requests), so that it knows when to trigger change detection. `zone.js`also provides patches for `rxjs`, so it's probably because of that that it behaves like this – David May 25 '20 at 15:23
  • @MichaelD no, both answers in this question just about how these 2 methods work inside and the reasons why and when to use them. My question is more about what runs detect changes when my async operation calls markForCheck – simply good May 25 '20 at 22:43
  • @David is it really true, that RXJs is patched with zone? So this should mean that any .subscribe() has after-effect made by zone which call changeDetector. By the way, in this case, does zone calls DetectChanges for the root component? (because detectChanges checks for changes in child components) – simply good May 25 '20 at 22:47
  • 1
    I'm not sure exactly how it works, but set a breakpoint in your subscribe, then check the callstack. You'll see along the chain a `patchTimer` function from `zone.js`. Not I'm not sure if they mean rxjs timer, or the timer function that is used by rxjs. – David May 26 '20 at 09:08
  • @David, yes, you are right. It is seems so :). Rx internally uses native timers and such of thing, and zone patched all of them. So I guess this is the reason – simply good May 26 '20 at 12:28

2 Answers2

3

With help of @David I found the answer.

First things first, RxJs is not wrapped with zone anyhow, but native async functions are (as setTimer/Iterval, fetch api, XHR, DOM events). Because of RxJs timer/delay(...) operators use native async functions this causes them to be handled in a context of zone as well.

This is how the Callstack looks like when we are in the Rx tap operator

enter image description here You can see that Rx part comes after Angular/Zone one.

Angular in his turn uses zone to call tick() function whenever async callback is executed. Tick method goes down from root component and looks for marked for checking views.

I put breakpoint in tick function, it is called after our callback is executed

enter image description here

So what happens in the first case when we use async pipe on the template.

  1. Async callback is handled by async pipe operator and markForCheck() function is called
  2. As we know markForCheck marks current and parent views up to the RootComponent for checking
  3. After callback from step 1 is done, the tick() is called. Which finally will check the view and update it

And almost the same happens in the second case, when we call markForCheck inside RxJs operator. We do the same thing as async pipe does, and because our callback is also wrapped with zone we have the the same process be executed after callback is done.

If you will not call markForCheck in your async callback, but call tick function, no update will be done. Worth mentioning that if you call detectChanges() the update will be done despite not calling markForCheck function. Here is the example on stackblitz This is due to detechChanges from ChangeDetectorRef ignores markForCheck flag and does change detection for Component and all its children. A lot more details in this article

This method runs change detection for the current component view regardless of its state

simply good
  • 991
  • 1
  • 12
  • 26
  • In the screenshot attached, under call stack section, I see groupings of function calls under "Angular" and "RxJs" groups. I do not see such a grouping in my chrome debugger. How did you get it? – Phalgun Nov 11 '21 at 17:53
0

See at the time of onpush, any changes in inputs make the component dirty and then only re-rendering is done. But if you want to make it dirty even without these events you can call markDirty method for changeDetection to happen. Please find the link to official statement below :-

official statement

Aakash Garg
  • 10,649
  • 2
  • 7
  • 25
  • I know the purpose of these methods. My question is about different thing. Who does call change detection when value is pushed to async pipe or markForCheck was called from the code. It appears that rxJs is wrapped with zone – simply good May 25 '20 at 22:52
  • Like in case of input change angular automatically calls it because template is marked as dirty, so whenever template is dirty detect changes will be called by Angular. And markforcheck set template dirty. Its only zone js not rxjs wrapped. As with async pipe, the pipe only return the data as soon as observable emits new data. As soon as pipe does that. Template becomes dirty. Here zonejs comes into play and calls detectchanges. – Aakash Garg May 26 '20 at 03:15
  • Are you sure that making component dirty makes triggering change detection process? I hear this for the first time. – simply good May 26 '20 at 19:20
  • yes, as far as i understand change detection for angular to decide when to run it it marks template as dirty. – Aakash Garg May 26 '20 at 19:24
  • Regarding 'dirtiness' - the implementation of `markForCheck` is literally `markForCheck() { markViewDirty(this._cdRefInjectingView || this._lView); }` which in turn marks all ancestors as dirty. – Simon_Weaver Jul 13 '21 at 07:32