|
9 | 9 | import {ApplicationRef} from '../../application/application_ref';
|
10 | 10 | import {ChangeDetectionSchedulerImpl} from './zoneless_scheduling_impl';
|
11 | 11 | import {inject} from '../../di/injector_compatibility';
|
12 |
| -import {makeEnvironmentProviders} from '../../di/provider_collection'; |
| 12 | +import {provideEnvironmentInitializer} from '../../di/provider_collection'; |
13 | 13 | import {NgZone} from '../../zone/ng_zone';
|
14 | 14 |
|
15 |
| -import {EnvironmentInjector} from '../../di/r3_injector'; |
16 |
| -import {ENVIRONMENT_INITIALIZER} from '../../di/initializer_token'; |
17 |
| -import {CheckNoChangesMode} from '../../render3/state'; |
18 | 15 | import {ErrorHandler} from '../../error_handler';
|
19 | 16 | import {checkNoChangesInternal} from '../../render3/instructions/change_detection';
|
20 |
| -import {ZONELESS_ENABLED} from './zoneless_scheduling'; |
21 | 17 |
|
22 |
| -/** |
23 |
| - * Used to periodically verify no expressions have changed after they were checked. |
24 |
| - * |
25 |
| - * @param options Used to configure when the check will execute. |
26 |
| - * - `interval` will periodically run exhaustive `checkNoChanges` on application views |
27 |
| - * - `useNgZoneOnStable` will use ZoneJS to determine when change detection might have run |
28 |
| - * in an application using ZoneJS to drive change detection. When the `NgZone.onStable` would |
29 |
| - * have emitted, all views attached to the `ApplicationRef` are checked for changes. |
30 |
| - * - 'exhaustive' means that all views attached to `ApplicationRef` and all the descendants of those views will be |
31 |
| - * checked for changes (excluding those subtrees which are detached via `ChangeDetectorRef.detach()`). |
32 |
| - * This is useful because the check that runs after regular change detection does not work for components using `ChangeDetectionStrategy.OnPush`. |
33 |
| - * This check is will surface any existing errors hidden by `OnPush` components. By default, this check is exhaustive |
34 |
| - * and will always check all views, regardless of their "dirty" state and `ChangeDetectionStrategy`. |
35 |
| - * |
36 |
| - * When the `useNgZoneOnStable` option is `true`, this function will provide its own `NgZone` implementation and needs |
37 |
| - * to come after any other `NgZone` provider, including `provideZoneChangeDetection()` and `provideZonelessChangeDetection()`. |
38 |
| - * |
39 |
| - * @experimental |
40 |
| - * @publicApi |
41 |
| - */ |
42 |
| -export function provideExperimentalCheckNoChangesForDebug(options: { |
43 |
| - interval?: number; |
44 |
| - useNgZoneOnStable?: boolean; |
45 |
| - exhaustive?: boolean; |
46 |
| -}) { |
47 |
| - if (typeof ngDevMode === 'undefined' || ngDevMode) { |
48 |
| - if (options.interval === undefined && !options.useNgZoneOnStable) { |
49 |
| - throw new Error('Must provide one of `useNgZoneOnStable` or `interval`'); |
50 |
| - } |
51 |
| - const checkNoChangesMode = |
52 |
| - options?.exhaustive === false |
53 |
| - ? CheckNoChangesMode.OnlyDirtyViews |
54 |
| - : CheckNoChangesMode.Exhaustive; |
55 |
| - return makeEnvironmentProviders([ |
56 |
| - options?.useNgZoneOnStable |
57 |
| - ? {provide: NgZone, useFactory: () => new DebugNgZoneForCheckNoChanges(checkNoChangesMode)} |
58 |
| - : [], |
59 |
| - options?.interval !== undefined |
60 |
| - ? exhaustiveCheckNoChangesInterval(options.interval, checkNoChangesMode) |
61 |
| - : [], |
62 |
| - { |
63 |
| - provide: ENVIRONMENT_INITIALIZER, |
64 |
| - multi: true, |
65 |
| - useValue: () => { |
66 |
| - if ( |
67 |
| - options?.useNgZoneOnStable && |
68 |
| - !(inject(NgZone) instanceof DebugNgZoneForCheckNoChanges) |
69 |
| - ) { |
70 |
| - throw new Error( |
71 |
| - '`provideExperimentalCheckNoChangesForDebug` with `useNgZoneOnStable` must be after any other provider for `NgZone`.', |
72 |
| - ); |
| 18 | +export function exhaustiveCheckNoChangesInterval(interval: number) { |
| 19 | + return provideEnvironmentInitializer(() => { |
| 20 | + const applicationRef = inject(ApplicationRef); |
| 21 | + const errorHandler = inject(ErrorHandler); |
| 22 | + const scheduler = inject(ChangeDetectionSchedulerImpl); |
| 23 | + const ngZone = inject(NgZone); |
| 24 | + |
| 25 | + function scheduleCheckNoChanges() { |
| 26 | + ngZone.runOutsideAngular(() => { |
| 27 | + setTimeout(() => { |
| 28 | + if (applicationRef.destroyed) { |
| 29 | + return; |
| 30 | + } |
| 31 | + if (scheduler.pendingRenderTaskId || scheduler.runningTick) { |
| 32 | + scheduleCheckNoChanges(); |
| 33 | + return; |
73 | 34 | }
|
74 |
| - }, |
75 |
| - }, |
76 |
| - ]); |
77 |
| - } else { |
78 |
| - return makeEnvironmentProviders([]); |
79 |
| - } |
80 |
| -} |
81 |
| - |
82 |
| -export class DebugNgZoneForCheckNoChanges extends NgZone { |
83 |
| - private applicationRef?: ApplicationRef; |
84 |
| - private scheduler?: ChangeDetectionSchedulerImpl; |
85 |
| - private errorHandler?: ErrorHandler; |
86 |
| - private readonly injector = inject(EnvironmentInjector); |
87 | 35 |
|
88 |
| - constructor(private readonly checkNoChangesMode: CheckNoChangesMode) { |
89 |
| - const zonelessEnabled = inject(ZONELESS_ENABLED); |
90 |
| - // Use coalescing to ensure we aren't ever running this check synchronously |
91 |
| - super({ |
92 |
| - shouldCoalesceEventChangeDetection: true, |
93 |
| - shouldCoalesceRunChangeDetection: zonelessEnabled, |
94 |
| - }); |
| 36 | + for (const view of applicationRef.allViews) { |
| 37 | + try { |
| 38 | + checkNoChangesInternal(view._lView, true /** exhaustive */); |
| 39 | + } catch (e) { |
| 40 | + errorHandler.handleError(e); |
| 41 | + } |
| 42 | + } |
95 | 43 |
|
96 |
| - if (zonelessEnabled) { |
97 |
| - // prevent emits to ensure code doesn't rely on these |
98 |
| - this.onMicrotaskEmpty.emit = () => {}; |
99 |
| - this.onStable.emit = () => { |
100 |
| - this.scheduler ||= this.injector.get(ChangeDetectionSchedulerImpl); |
101 |
| - if (this.scheduler.pendingRenderTaskId || this.scheduler.runningTick) { |
102 |
| - return; |
103 |
| - } |
104 |
| - this.checkApplicationViews(); |
105 |
| - }; |
106 |
| - this.onUnstable.emit = () => {}; |
107 |
| - } else { |
108 |
| - this.runOutsideAngular(() => { |
109 |
| - this.onStable.subscribe(() => { |
110 |
| - this.checkApplicationViews(); |
111 |
| - }); |
| 44 | + scheduleCheckNoChanges(); |
| 45 | + }, interval); |
112 | 46 | });
|
113 | 47 | }
|
114 |
| - } |
115 |
| - |
116 |
| - private checkApplicationViews() { |
117 |
| - this.applicationRef ||= this.injector.get(ApplicationRef); |
118 |
| - for (const view of this.applicationRef.allViews) { |
119 |
| - try { |
120 |
| - checkNoChangesInternal(view._lView, this.checkNoChangesMode); |
121 |
| - } catch (e) { |
122 |
| - this.errorHandler ||= this.injector.get(ErrorHandler); |
123 |
| - this.errorHandler.handleError(e); |
124 |
| - } |
125 |
| - } |
126 |
| - } |
127 |
| -} |
128 |
| - |
129 |
| -function exhaustiveCheckNoChangesInterval( |
130 |
| - interval: number, |
131 |
| - checkNoChangesMode: CheckNoChangesMode, |
132 |
| -) { |
133 |
| - return { |
134 |
| - provide: ENVIRONMENT_INITIALIZER, |
135 |
| - multi: true, |
136 |
| - useFactory: () => { |
137 |
| - const applicationRef = inject(ApplicationRef); |
138 |
| - const errorHandler = inject(ErrorHandler); |
139 |
| - const scheduler = inject(ChangeDetectionSchedulerImpl); |
140 |
| - const ngZone = inject(NgZone); |
141 |
| - |
142 |
| - return () => { |
143 |
| - function scheduleCheckNoChanges() { |
144 |
| - ngZone.runOutsideAngular(() => { |
145 |
| - setTimeout(() => { |
146 |
| - if (applicationRef.destroyed) { |
147 |
| - return; |
148 |
| - } |
149 |
| - if (scheduler.pendingRenderTaskId || scheduler.runningTick) { |
150 |
| - scheduleCheckNoChanges(); |
151 |
| - return; |
152 |
| - } |
153 |
| - |
154 |
| - for (const view of applicationRef.allViews) { |
155 |
| - try { |
156 |
| - checkNoChangesInternal(view._lView, checkNoChangesMode); |
157 |
| - } catch (e) { |
158 |
| - errorHandler.handleError(e); |
159 |
| - } |
160 |
| - } |
161 |
| - |
162 |
| - scheduleCheckNoChanges(); |
163 |
| - }, interval); |
164 |
| - }); |
165 |
| - } |
166 |
| - scheduleCheckNoChanges(); |
167 |
| - }; |
168 |
| - }, |
169 |
| - }; |
| 48 | + scheduleCheckNoChanges(); |
| 49 | + }); |
170 | 50 | }
|
0 commit comments