Skip to content

Commit fdd560e

Browse files
committed
feat(core): Add ability to configure zone change detection to use zoneless scheduler (#55252)
This commit adds a configuration option to zone-based change detection which allows applications to enable/disable the zoneless scheduler. When the zoneless scheduler is enabled in zone-based applications, updates that happen outside the Angular zone will still result in a change detection being scheduled. Previously, Angular change detection was solely based on the state of the Angular Zone. PR Close #55252
1 parent c9abe77 commit fdd560e

File tree

17 files changed

+42
-67
lines changed

17 files changed

+42
-67
lines changed

goldens/public-api/core/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ export function booleanAttribute(value: unknown): boolean;
156156

157157
// @public
158158
export interface BootstrapOptions {
159+
ignoreChangesOutsideZone?: boolean;
159160
ngZone?: NgZone | 'zone.js' | 'noop';
160161
ngZoneEventCoalescing?: boolean;
161162
ngZoneRunCoalescing?: boolean;
@@ -1218,6 +1219,7 @@ export class NgZone {
12181219
// @public
12191220
export interface NgZoneOptions {
12201221
eventCoalescing?: boolean;
1222+
ignoreChangesOutsideZone?: boolean;
12211223
runCoalescing?: boolean;
12221224
}
12231225

packages/core/src/application/application_ref.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,20 @@ export interface BootstrapOptions {
143143
*
144144
*/
145145
ngZoneRunCoalescing?: boolean;
146+
147+
/**
148+
* When false, change detection is scheduled when Angular receives
149+
* a clear indication that templates need to be refreshed. This includes:
150+
*
151+
* - calling `ChangeDetectorRef.markForCheck`
152+
* - calling `ComponentRef.setInput`
153+
* - updating a signal that is read in a template
154+
* - when bound host or template listeners are triggered
155+
* - attaching a view that is marked dirty
156+
* - removing a view
157+
* - registering a render hook (templates are only refreshed if render hooks do one of the above)
158+
*/
159+
ignoreChangesOutsideZone?: boolean;
146160
}
147161

148162
/** Maximum number of times ApplicationRef will refresh all attached views in a single tick. */

packages/core/src/change_detection/scheduling/ng_zone_scheduling.ts

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -59,26 +59,9 @@ export class NgZoneChangeDetectionScheduler {
5959
export const PROVIDED_NG_ZONE = new InjectionToken<boolean>(
6060
(typeof ngDevMode === 'undefined' || ngDevMode) ? 'provideZoneChangeDetection token' : '');
6161

62-
/**
63-
* Configures change detection scheduling when using ZoneJS.
64-
*/
65-
export enum SchedulingMode {
66-
/**
67-
* Change detection will run when the `NgZone.onMicrotaskEmpty` observable emits.
68-
* Change detection will also be scheduled to run whenever Angular is notified
69-
* of a change. This includes calling `ChangeDetectorRef.markForCheck`,
70-
* setting a `signal` value, and attaching a view.
71-
*/
72-
Hybrid,
73-
/**
74-
* Change detection will only run when the `NgZone.onMicrotaskEmpty` observable emits.
75-
*/
76-
NgZoneOnly,
77-
}
78-
7962
export function internalProvideZoneChangeDetection(
80-
{ngZoneFactory, schedulingMode}:
81-
{ngZoneFactory: () => NgZone, schedulingMode?: SchedulingMode}): StaticProvider[] {
63+
{ngZoneFactory, ignoreChangesOutsideZone}:
64+
{ngZoneFactory: () => NgZone, ignoreChangesOutsideZone?: boolean}): StaticProvider[] {
8265
return [
8366
{provide: NgZone, useFactory: ngZoneFactory},
8467
{
@@ -109,11 +92,9 @@ export function internalProvideZoneChangeDetection(
10992
},
11093
{provide: INTERNAL_APPLICATION_ERROR_HANDLER, useFactory: ngZoneApplicationErrorHandlerFactory},
11194
// Always disable scheduler whenever explicitly disabled, even if Hybrid was specified elsewhere
112-
schedulingMode === SchedulingMode.NgZoneOnly ?
113-
{provide: ZONELESS_SCHEDULER_DISABLED, useValue: true} :
114-
[],
115-
// Only provide scheduler when explicitly enabled
116-
schedulingMode === SchedulingMode.Hybrid ?
95+
ignoreChangesOutsideZone === true ? {provide: ZONELESS_SCHEDULER_DISABLED, useValue: true} : [],
96+
// Only provide scheduler when explicitly not disabled
97+
ignoreChangesOutsideZone === false ?
11798
{provide: ChangeDetectionScheduler, useExisting: ChangeDetectionSchedulerImpl} :
11899
[],
119100
];
@@ -146,7 +127,7 @@ export function ngZoneApplicationErrorHandlerFactory() {
146127
* @see {@link NgZoneOptions}
147128
*/
148129
export function provideZoneChangeDetection(options?: NgZoneOptions): EnvironmentProviders {
149-
const schedulingMode = (options as any)?.schedulingMode;
130+
const ignoreChangesOutsideZone = options?.ignoreChangesOutsideZone;
150131
const zoneProviders = internalProvideZoneChangeDetection({
151132
ngZoneFactory: () => {
152133
const ngZoneOptions = getNgZoneOptions(options);
@@ -155,7 +136,7 @@ export function provideZoneChangeDetection(options?: NgZoneOptions): Environment
155136
}
156137
return new NgZone(ngZoneOptions);
157138
},
158-
schedulingMode
139+
ignoreChangesOutsideZone
159140
});
160141
return makeEnvironmentProviders([
161142
(typeof ngDevMode === 'undefined' || ngDevMode) ? {provide: PROVIDED_NG_ZONE, useValue: true} :
@@ -214,6 +195,20 @@ export interface NgZoneOptions {
214195
*
215196
*/
216197
runCoalescing?: boolean;
198+
199+
/**
200+
* When false, change detection is scheduled when Angular receives
201+
* a clear indication that templates need to be refreshed. This includes:
202+
*
203+
* - calling `ChangeDetectorRef.markForCheck`
204+
* - calling `ComponentRef.setInput`
205+
* - updating a signal that is read in a template
206+
* - when bound host or template listeners are triggered
207+
* - attaching a view that is marked dirty
208+
* - removing a view
209+
* - registering a render hook (templates are only refreshed if render hooks do one of the above)
210+
*/
211+
ignoreChangesOutsideZone?: boolean;
217212
}
218213

219214
// Transforms a set of `BootstrapOptions` (supported by the NgModule-based bootstrap APIs) ->

packages/core/src/core_private_export.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export {detectChangesInViewIfRequired as ɵdetectChangesInViewIfRequired, whenSt
1111
export {IMAGE_CONFIG as ɵIMAGE_CONFIG, IMAGE_CONFIG_DEFAULTS as ɵIMAGE_CONFIG_DEFAULTS, ImageConfig as ɵImageConfig} from './application/application_tokens';
1212
export {internalCreateApplication as ɵinternalCreateApplication} from './application/create_application';
1313
export {defaultIterableDiffers as ɵdefaultIterableDiffers, defaultKeyValueDiffers as ɵdefaultKeyValueDiffers} from './change_detection/change_detection';
14-
export {SchedulingMode as ɵSchedulingMode} from './change_detection/scheduling/ng_zone_scheduling';
1514
export {ChangeDetectionScheduler as ɵChangeDetectionScheduler, ZONELESS_ENABLED as ɵZONELESS_ENABLED} from './change_detection/scheduling/zoneless_scheduling';
1615
export {provideZonelessChangeDetection as ɵprovideZonelessChangeDetection} from './change_detection/scheduling/zoneless_scheduling_impl';
1716
export {Console as ɵConsole} from './console';

packages/core/src/platform/platform_ref.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import {ApplicationInitStatus} from '../application/application_init';
1010
import {compileNgModuleFactory} from '../application/application_ngmodule_factory_compiler';
1111
import {_callAndReportToErrorHandler, ApplicationRef, BootstrapOptions, optionsReducer, remove} from '../application/application_ref';
12-
import {getNgZoneOptions, internalProvideZoneChangeDetection, PROVIDED_NG_ZONE, SchedulingMode} from '../change_detection/scheduling/ng_zone_scheduling';
12+
import {getNgZoneOptions, internalProvideZoneChangeDetection, PROVIDED_NG_ZONE} from '../change_detection/scheduling/ng_zone_scheduling';
1313
import {Injectable, InjectionToken, Injector} from '../di';
1414
import {ErrorHandler} from '../error_handler';
1515
import {RuntimeError, RuntimeErrorCode} from '../errors';
@@ -72,11 +72,12 @@ export class PlatformRef {
7272
// Do not try to replace ngZone.run with ApplicationRef#run because ApplicationRef would then be
7373
// created outside of the Angular zone.
7474
return ngZone.run(() => {
75-
const schedulingMode = (options as any)?.schedulingMode;
75+
const ignoreChangesOutsideZone = options?.ignoreChangesOutsideZone;
7676
const moduleRef = createNgModuleRefWithProviders(
7777
moduleFactory.moduleType,
7878
this.injector,
79-
internalProvideZoneChangeDetection({ngZoneFactory: () => ngZone, schedulingMode}),
79+
internalProvideZoneChangeDetection(
80+
{ngZoneFactory: () => ngZone, ignoreChangesOutsideZone}),
8081
);
8182

8283
if ((typeof ngDevMode === 'undefined' || ngDevMode) &&

packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -461,9 +461,6 @@
461461
{
462462
"name": "Sanitizer"
463463
},
464-
{
465-
"name": "SchedulingMode"
466-
},
467464
{
468465
"name": "ShadowDomRenderer"
469466
},

packages/core/test/bundling/animations/bundle.golden_symbols.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -506,9 +506,6 @@
506506
{
507507
"name": "Sanitizer"
508508
},
509-
{
510-
"name": "SchedulingMode"
511-
},
512509
{
513510
"name": "ShadowDomRenderer"
514511
},

packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -383,9 +383,6 @@
383383
{
384384
"name": "Sanitizer"
385385
},
386-
{
387-
"name": "SchedulingMode"
388-
},
389386
{
390387
"name": "ShadowDomRenderer"
391388
},

packages/core/test/bundling/defer/bundle.golden_symbols.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -440,9 +440,6 @@
440440
{
441441
"name": "Sanitizer"
442442
},
443-
{
444-
"name": "SchedulingMode"
445-
},
446443
{
447444
"name": "ShadowDomRenderer"
448445
},

packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -527,9 +527,6 @@
527527
{
528528
"name": "Sanitizer"
529529
},
530-
{
531-
"name": "SchedulingMode"
532-
},
533530
{
534531
"name": "ShadowDomRenderer"
535532
},

0 commit comments

Comments
 (0)