Skip to content

Commit fa451bc

Browse files
committed
feat(ivy): support markForCheck (#22690)
PR Close #22690
1 parent 0d8deb0 commit fa451bc

4 files changed

Lines changed: 327 additions & 18 deletions

File tree

‎packages/core/src/change_detection/change_detector_ref.ts‎

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
*/
1212
export abstract class ChangeDetectorRef {
1313
/**
14-
* Marks all {@link ChangeDetectionStrategy#OnPush OnPush} ancestors as to be checked.
14+
* Marks a view and all of its ancestors dirty.
15+
*
16+
* This can be used to ensure an {@link ChangeDetectionStrategy#OnPush OnPush} component is
17+
* checked when it needs to be re-rendered but the two normal triggers haven't marked it
18+
* dirty (i.e. inputs haven't changed and events haven't fired in the view).
1519
*
1620
* <!-- TODO: Add a link to a chapter on OnPush components -->
1721
*
@@ -39,12 +43,12 @@ export abstract class ChangeDetectorRef {
3943
abstract markForCheck(): void;
4044

4145
/**
42-
* Detaches the change detector from the change detector tree.
43-
*
44-
* The detached change detector will not be checked until it is reattached.
46+
* Detaches the view from the change detection tree.
4547
*
46-
* This can also be used in combination with {@link ChangeDetectorRef#detectChanges detectChanges}
47-
* to implement local change detection checks.
48+
* Detached views will not be checked during change detection runs until they are
49+
* re-attached, even if they are dirty. `detach` can be used in combination with
50+
* {@link ChangeDetectorRef#detectChanges detectChanges} to implement local change
51+
* detection checks.
4852
*
4953
* <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
5054
* <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
@@ -93,7 +97,7 @@ export abstract class ChangeDetectorRef {
9397
abstract detach(): void;
9498

9599
/**
96-
* Checks the change detector and its children.
100+
* Checks the view and its children.
97101
*
98102
* This can also be used in combination with {@link ChangeDetectorRef#detach detach} to implement
99103
* local change detection checks.
@@ -108,8 +112,7 @@ export abstract class ChangeDetectorRef {
108112
* we want to check and update the list every five seconds.
109113
*
110114
* We can do that by detaching the component's change detector and doing a local change detection
111-
* check
112-
* every five seconds.
115+
* check every five seconds.
113116
*
114117
* See {@link ChangeDetectorRef#detach detach} for more information.
115118
*/
@@ -124,7 +127,10 @@ export abstract class ChangeDetectorRef {
124127
abstract checkNoChanges(): void;
125128

126129
/**
127-
* Reattach the change detector to the change detector tree.
130+
* Re-attaches the view to the change detection tree.
131+
*
132+
* This can be used to re-attach views that were previously detached from the tree
133+
* using {@link ChangeDetectorRef#detach detach}. Views are attached to the tree by default.
128134
*
129135
* <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
130136
*

‎packages/core/src/render3/instructions.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1499,7 +1499,7 @@ export function wrapListenerWithDirtyAndDefault(
14991499
}
15001500

15011501
/** Marks current view and all ancestors dirty */
1502-
function markViewDirty(view: LView): void {
1502+
export function markViewDirty(view: LView): void {
15031503
let currentView: LView|null = view;
15041504

15051505
while (currentView.parent != null) {

‎packages/core/src/render3/view_ref.ts‎

Lines changed: 155 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef} from '../linker/view_ref';
1010

11-
import {detectChanges} from './instructions';
11+
import {detectChanges, markViewDirty} from './instructions';
1212
import {ComponentTemplate} from './interfaces/definition';
1313
import {LViewNode} from './interfaces/node';
1414
import {LView, LViewFlags} from './interfaces/view';
@@ -26,25 +26,173 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T> {
2626
destroy(): void { notImplemented(); }
2727
destroyed: boolean;
2828
onDestroy(callback: Function) { notImplemented(); }
29-
markForCheck(): void { notImplemented(); }
3029

3130
/**
32-
* Detaches a view from the change detection tree.
31+
* Marks a view and all of its ancestors dirty.
3332
*
34-
* Detached views will not be checked during change detection runs, even if the view
35-
* is dirty. This can be used in combination with detectChanges to implement local
36-
* change detection checks.
33+
* It also triggers change detection by calling `scheduleTick` internally, which coalesces
34+
* multiple `markForCheck` calls to into one change detection run.
35+
*
36+
* This can be used to ensure an {@link ChangeDetectionStrategy#OnPush OnPush} component is
37+
* checked when it needs to be re-rendered but the two normal triggers haven't marked it
38+
* dirty (i.e. inputs haven't changed and events haven't fired in the view).
39+
*
40+
* <!-- TODO: Add a link to a chapter on OnPush components -->
41+
*
42+
* ### Example ([live demo](https://stackblitz.com/edit/angular-kx7rrw))
43+
*
44+
* ```typescript
45+
* @Component({
46+
* selector: 'my-app',
47+
* template: `Number of ticks: {{numberOfTicks}}`
48+
* changeDetection: ChangeDetectionStrategy.OnPush,
49+
* })
50+
* class AppComponent {
51+
* numberOfTicks = 0;
52+
*
53+
* constructor(private ref: ChangeDetectorRef) {
54+
* setInterval(() => {
55+
* this.numberOfTicks++;
56+
* // the following is required, otherwise the view will not be updated
57+
* this.ref.markForCheck();
58+
* }, 1000);
59+
* }
60+
* }
61+
* ```
62+
*/
63+
markForCheck(): void { markViewDirty(this._view); }
64+
65+
/**
66+
* Detaches the view from the change detection tree.
67+
*
68+
* Detached views will not be checked during change detection runs until they are
69+
* re-attached, even if they are dirty. `detach` can be used in combination with
70+
* {@link ChangeDetectorRef#detectChanges detectChanges} to implement local change
71+
* detection checks.
72+
*
73+
* <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
74+
* <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
75+
*
76+
* ### Example
77+
*
78+
* The following example defines a component with a large list of readonly data.
79+
* Imagine the data changes constantly, many times per second. For performance reasons,
80+
* we want to check and update the list every five seconds. We can do that by detaching
81+
* the component's change detector and doing a local check every five seconds.
82+
*
83+
* ```typescript
84+
* class DataProvider {
85+
* // in a real application the returned data will be different every time
86+
* get data() {
87+
* return [1,2,3,4,5];
88+
* }
89+
* }
90+
*
91+
* @Component({
92+
* selector: 'giant-list',
93+
* template: `
94+
* <li *ngFor="let d of dataProvider.data">Data {{d}}</li>
95+
* `,
96+
* })
97+
* class GiantList {
98+
* constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {
99+
* ref.detach();
100+
* setInterval(() => {
101+
* this.ref.detectChanges();
102+
* }, 5000);
103+
* }
104+
* }
105+
*
106+
* @Component({
107+
* selector: 'app',
108+
* providers: [DataProvider],
109+
* template: `
110+
* <giant-list><giant-list>
111+
* `,
112+
* })
113+
* class App {
114+
* }
115+
* ```
37116
*/
38117
detach(): void { this._view.flags &= ~LViewFlags.Attached; }
39118

40119
/**
41120
* Re-attaches a view to the change detection tree.
42121
*
43122
* This can be used to re-attach views that were previously detached from the tree
44-
* using detach(). Views are attached to the tree by default.
123+
* using {@link ChangeDetectorRef#detach detach}. Views are attached to the tree by default.
124+
*
125+
* <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
126+
*
127+
* ### Example ([live demo](https://stackblitz.com/edit/angular-ymgsxw))
128+
*
129+
* The following example creates a component displaying `live` data. The component will detach
130+
* its change detector from the main change detector tree when the component's live property
131+
* is set to false.
132+
*
133+
* ```typescript
134+
* class DataProvider {
135+
* data = 1;
136+
*
137+
* constructor() {
138+
* setInterval(() => {
139+
* this.data = this.data * 2;
140+
* }, 500);
141+
* }
142+
* }
143+
*
144+
* @Component({
145+
* selector: 'live-data',
146+
* inputs: ['live'],
147+
* template: 'Data: {{dataProvider.data}}'
148+
* })
149+
* class LiveData {
150+
* constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {}
151+
*
152+
* set live(value) {
153+
* if (value) {
154+
* this.ref.reattach();
155+
* } else {
156+
* this.ref.detach();
157+
* }
158+
* }
159+
* }
160+
*
161+
* @Component({
162+
* selector: 'my-app',
163+
* providers: [DataProvider],
164+
* template: `
165+
* Live Update: <input type="checkbox" [(ngModel)]="live">
166+
* <live-data [live]="live"><live-data>
167+
* `,
168+
* })
169+
* class AppComponent {
170+
* live = true;
171+
* }
172+
* ```
45173
*/
46174
reattach(): void { this._view.flags |= LViewFlags.Attached; }
47175

176+
/**
177+
* Checks the view and its children.
178+
*
179+
* This can also be used in combination with {@link ChangeDetectorRef#detach detach} to implement
180+
* local change detection checks.
181+
*
182+
* <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
183+
* <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
184+
*
185+
* ### Example
186+
*
187+
* The following example defines a component with a large list of readonly data.
188+
* Imagine, the data changes constantly, many times per second. For performance reasons,
189+
* we want to check and update the list every five seconds.
190+
*
191+
* We can do that by detaching the component's change detector and doing a local change detection
192+
* check every five seconds.
193+
*
194+
* See {@link ChangeDetectorRef#detach detach} for more information.
195+
*/
48196
detectChanges(): void { detectChanges(this.context); }
49197

50198
checkNoChanges(): void { notImplemented(); }

0 commit comments

Comments
 (0)