Reputation: 4595
I still do not understand what is the difference between cdr.detectChanges()
and cdr.markForCheck()
for OnPush
change detection strategy from the usage view.
Eventhough I have read this SO question and InDepth explanation.
Why can't I just call cdr.detectChanges()
?
Why do I need to mark the tree from current component to the root (Or is it due to postpone to the next detection cycle)?
Or is it somehow required to update parent components too?
In the following example, both ways works:
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
@Component({
selector: 'app-test',
template: `{{ i }}`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TestComponent implements OnInit {
private i = 0;
constructor(private cdr: ChangeDetectorRef) { }
ngOnInit() {
setInterval(() => {
this.i++;
// this.cdr.detectChanges(); // this works too and updates view
this.cdr.markForCheck(); // but this is for some reason recommended
}, 1000);
}
}
Upvotes: 1
Views: 3928
Reputation: 845
Not 100% sure about this, but from what I understand using detectChanges()
everywhere would likely be less performant than using markForCheck()
.
detectChanges()
Imagine TestComponent
has several fields foo
, bar
, baz
, and the setter for each field looks like:
set foo(value) {
...
this.cdr.detectChanges();
};
Some asynchronous event could do:
testComponent.foo = ...; // triggers 1st change detection cycle on TestComponent and its children
testComponent.bar = ...; // triggers 2nd change detection cycle on TestComponent and its children
testComponent.baz = ...; // triggers 3rd change detection cycle on TestComponent and its children
// after the async event, Angular runs regular change detection
// starting from the root component,
// TestComponent was not marked for check so it is skipped.
In total, we've run change detection on TestComponent
3 times, but it's unnecessary work and we could have just done a single change detection cycle after all three changes.
Note: you could rewrite your code and restrict TestComponent
's public API to optimize detectChanges()
use, but that's all on you to manually figure out.
markForCheck()
Now let's say the setters use markForCheck()
:
set foo(value) {
...
this.cdr.markForCheck();
};
Some asynchronous event could do:
testComponent.foo = ...; // no change detection triggered
testComponent.bar = ...; // no change detection triggered
testComponent.baz = ...; // no change detection triggered
// after the async event, Angular runs regular change detection
// starting from the root component,
// TestComponent was marked for check so change detection runs on it.
In total, change detection only runs once for TestComponent
. The more expensive it is to run change detection on TestComponent
(e.g. what if TestComponent
has a huge subtree of child components), the more this matters.
Upvotes: 1
Reputation: 982
detectChanges
runs a change detection cycle directly for the component
markForCheck
does what it's called like, it just mark the component for check
In your example you run setInterval
, that means that the changeDetection gets triggered by setInterval, but runs in your component in the next cycle.
Thats because NgZone
does something like monkey patch all default api's it can reaches to run changeDetection
. With on Push you hook out there and tell your component that you tell angular when it have to run additional checks.
If you want to get some deeper knowledge about NgZone and ChangeDetection take a look at this video Angular Performance: Your App at the Speed of Light. This contains very good understandable knowledge round about ChangeDetection and how NgZone works which every Angular Developer should know about.
Upvotes: 3