@angular/core 16.2.2 → 16.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/src/di/injection_token.mjs +12 -7
- package/esm2022/src/hydration/annotate.mjs +53 -25
- package/esm2022/src/linker/view_container_ref.mjs +35 -1
- package/esm2022/src/render3/after_render_hooks.mjs +6 -3
- package/esm2022/src/render3/collect_native_nodes.mjs +30 -23
- package/esm2022/src/render3/component.mjs +4 -3
- package/esm2022/src/render3/di.mjs +1 -1
- package/esm2022/src/render3/instructions/change_detection.mjs +4 -4
- package/esm2022/src/render3/instructions/shared.mjs +20 -14
- package/esm2022/src/render3/interfaces/injector.mjs +1 -1
- package/esm2022/src/render3/interfaces/styling.mjs +4 -7
- package/esm2022/src/render3/node_manipulation.mjs +4 -3
- package/esm2022/src/render3/reactive_lview_consumer.mjs +25 -45
- package/esm2022/src/render3/reactivity/effect.mjs +8 -8
- package/esm2022/src/render3/util/injector_utils.mjs +1 -1
- package/esm2022/src/signals/index.mjs +4 -4
- package/esm2022/src/signals/src/api.mjs +2 -11
- package/esm2022/src/signals/src/computed.mjs +43 -93
- package/esm2022/src/signals/src/graph.mjs +238 -162
- package/esm2022/src/signals/src/signal.mjs +59 -79
- package/esm2022/src/signals/src/watch.mjs +38 -52
- package/esm2022/src/signals/src/weak_ref.mjs +2 -29
- package/esm2022/src/util/dom.mjs +2 -2
- package/esm2022/src/util/security/trusted_type_defs.mjs +1 -1
- package/esm2022/src/util/security/trusted_types.mjs +1 -1
- package/esm2022/src/version.mjs +1 -1
- package/esm2022/src/zone/ng_zone.mjs +16 -1
- package/esm2022/testing/src/logger.mjs +3 -3
- package/fesm2022/core.mjs +14963 -14928
- package/fesm2022/core.mjs.map +1 -1
- package/fesm2022/rxjs-interop.mjs +373 -413
- package/fesm2022/rxjs-interop.mjs.map +1 -1
- package/fesm2022/testing.mjs +1162 -644
- package/fesm2022/testing.mjs.map +1 -1
- package/index.d.ts +123 -108
- package/package.json +1 -1
- package/rxjs-interop/index.d.ts +1 -1
- package/schematics/ng-generate/standalone-migration/bundle.js +9 -9
- package/schematics/ng-generate/standalone-migration/bundle.js.map +1 -1
- package/testing/index.d.ts +1 -1
package/fesm2022/testing.mjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v16.2.
|
|
2
|
+
* @license Angular v16.2.4
|
|
3
3
|
* (c) 2010-2022 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { getDebugNode, RendererFactory2 as RendererFactory2$1, InjectionToken as InjectionToken$1, ɵstringify, ɵReflectionCapabilities, Directive, Component, Pipe, NgModule, ɵgetInjectableDef, resolveForwardRef as resolveForwardRef$1, ɵNG_COMP_DEF, ɵRender3NgModuleRef, ApplicationInitStatus, LOCALE_ID as LOCALE_ID$1, ɵDEFAULT_LOCALE_ID, ɵsetLocaleId, ɵRender3ComponentFactory, ɵcompileComponent, ɵNG_DIR_DEF, ɵcompileDirective, ɵNG_PIPE_DEF, ɵcompilePipe, ɵNG_MOD_DEF, ɵtransitiveScopesFor, ɵpatchComponentDefWithScope, ɵNG_INJ_DEF, ɵcompileNgModuleDefs, provideZoneChangeDetection, Compiler, COMPILER_OPTIONS, Injector as Injector$1, ɵisEnvironmentProviders, ɵNgModuleFactory, ModuleWithComponentFactories, ɵconvertToBitFlags, InjectFlags as InjectFlags$1, ɵsetAllowDuplicateNgModuleIdsForTest, ɵresetCompiledComponents, ɵsetUnknownElementStrictMode as ɵsetUnknownElementStrictMode$1, ɵsetUnknownPropertyStrictMode as ɵsetUnknownPropertyStrictMode$1, ɵgetUnknownElementStrictMode as ɵgetUnknownElementStrictMode$1, ɵgetUnknownPropertyStrictMode as ɵgetUnknownPropertyStrictMode$1, EnvironmentInjector as EnvironmentInjector$1, NgZone, ɵflushModuleScopingQueueAsMuchAsPossible } from '@angular/core';
|
|
7
|
+
import { getDebugNode, RendererFactory2 as RendererFactory2$1, InjectionToken as InjectionToken$1, ɵstringify, ɵReflectionCapabilities, Directive, Component, Pipe, NgModule, ɵgetInjectableDef, resolveForwardRef as resolveForwardRef$1, ɵNG_COMP_DEF, ɵRender3NgModuleRef, ApplicationInitStatus, LOCALE_ID as LOCALE_ID$1, ɵDEFAULT_LOCALE_ID, ɵsetLocaleId, ɵRender3ComponentFactory, ɵcompileComponent, ɵNG_DIR_DEF, ɵcompileDirective, ɵNG_PIPE_DEF, ɵcompilePipe, ɵNG_MOD_DEF, ɵtransitiveScopesFor, ɵpatchComponentDefWithScope, ɵNG_INJ_DEF, ɵcompileNgModuleDefs, provideZoneChangeDetection, Compiler, COMPILER_OPTIONS, Injector as Injector$1, ɵisEnvironmentProviders, ɵNgModuleFactory, ModuleWithComponentFactories, ɵconvertToBitFlags, InjectFlags as InjectFlags$1, ɵsetAllowDuplicateNgModuleIdsForTest, ɵresetCompiledComponents, ɵsetUnknownElementStrictMode as ɵsetUnknownElementStrictMode$1, ɵsetUnknownPropertyStrictMode as ɵsetUnknownPropertyStrictMode$1, ɵgetUnknownElementStrictMode as ɵgetUnknownElementStrictMode$1, ɵgetUnknownPropertyStrictMode as ɵgetUnknownPropertyStrictMode$1, EnvironmentInjector as EnvironmentInjector$1, NgZone as NgZone$1, ɵflushModuleScopingQueueAsMuchAsPossible } from '@angular/core';
|
|
8
8
|
import { ResourceLoader } from '@angular/compiler';
|
|
9
|
-
import { Subject, Subscription } from 'rxjs';
|
|
9
|
+
import { Subject, Subscription, Observable, merge as merge$1 } from 'rxjs';
|
|
10
|
+
import { share } from 'rxjs/operators';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Wraps a test function in an asynchronous test zone. The test will automatically
|
|
@@ -3564,15 +3565,6 @@ const SIGNAL = Symbol('SIGNAL');
|
|
|
3564
3565
|
function isSignal(value) {
|
|
3565
3566
|
return typeof value === 'function' && value[SIGNAL] !== undefined;
|
|
3566
3567
|
}
|
|
3567
|
-
/**
|
|
3568
|
-
* Converts `fn` into a marked signal function (where `isSignal(fn)` will be `true`), and
|
|
3569
|
-
* potentially add some set of extra properties (passed as an object record `extraApi`).
|
|
3570
|
-
*/
|
|
3571
|
-
function createSignalFromFunction(node, fn, extraApi = {}) {
|
|
3572
|
-
fn[SIGNAL] = node;
|
|
3573
|
-
// Copy properties from `extraApi` to `fn` to complete the desired API of the `Signal`.
|
|
3574
|
-
return Object.assign(fn, extraApi);
|
|
3575
|
-
}
|
|
3576
3568
|
/**
|
|
3577
3569
|
* The default equality function used for `signal` and `computed`, which treats objects and arrays
|
|
3578
3570
|
* as never equal, and all other primitive values using identity semantics.
|
|
@@ -3593,216 +3585,265 @@ function defaultEquals(a, b) {
|
|
|
3593
3585
|
|
|
3594
3586
|
// Required as the signals library is in a separate package, so we need to explicitly ensure the
|
|
3595
3587
|
/**
|
|
3596
|
-
*
|
|
3597
|
-
*
|
|
3588
|
+
* The currently active consumer `ReactiveNode`, if running code in a reactive context.
|
|
3589
|
+
*
|
|
3590
|
+
* Change this via `setActiveConsumer`.
|
|
3598
3591
|
*/
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3592
|
+
let activeConsumer = null;
|
|
3593
|
+
let inNotificationPhase = false;
|
|
3594
|
+
function setActiveConsumer(consumer) {
|
|
3595
|
+
const prev = activeConsumer;
|
|
3596
|
+
activeConsumer = consumer;
|
|
3597
|
+
return prev;
|
|
3598
|
+
}
|
|
3599
|
+
const REACTIVE_NODE = {
|
|
3600
|
+
version: 0,
|
|
3601
|
+
dirty: false,
|
|
3602
|
+
producerNode: undefined,
|
|
3603
|
+
producerLastReadVersion: undefined,
|
|
3604
|
+
producerIndexOfThis: undefined,
|
|
3605
|
+
nextProducerIndex: 0,
|
|
3606
|
+
liveConsumerNode: undefined,
|
|
3607
|
+
liveConsumerIndexOfThis: undefined,
|
|
3608
|
+
consumerAllowSignalWrites: false,
|
|
3609
|
+
consumerIsAlwaysLive: false,
|
|
3610
|
+
producerMustRecompute: () => false,
|
|
3611
|
+
producerRecomputeValue: () => { },
|
|
3612
|
+
consumerMarkedDirty: () => { },
|
|
3613
|
+
};
|
|
3614
|
+
/**
|
|
3615
|
+
* Called by implementations when a producer's signal is read.
|
|
3616
|
+
*/
|
|
3617
|
+
function producerAccessed(node) {
|
|
3618
|
+
if (inNotificationPhase) {
|
|
3619
|
+
throw new Error(typeof ngDevMode !== 'undefined' && ngDevMode ?
|
|
3620
|
+
`Assertion error: signal read during notification phase` :
|
|
3621
|
+
'');
|
|
3602
3622
|
}
|
|
3603
|
-
|
|
3604
|
-
|
|
3623
|
+
if (activeConsumer === null) {
|
|
3624
|
+
// Accessed outside of a reactive context, so nothing to record.
|
|
3625
|
+
return;
|
|
3605
3626
|
}
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3627
|
+
// This producer is the `idx`th dependency of `activeConsumer`.
|
|
3628
|
+
const idx = activeConsumer.nextProducerIndex++;
|
|
3629
|
+
assertConsumerNode(activeConsumer);
|
|
3630
|
+
if (idx < activeConsumer.producerNode.length && activeConsumer.producerNode[idx] !== node) {
|
|
3631
|
+
// There's been a change in producers since the last execution of `activeConsumer`.
|
|
3632
|
+
// `activeConsumer.producerNode[idx]` holds a stale dependency which will be be removed and
|
|
3633
|
+
// replaced with `this`.
|
|
3634
|
+
//
|
|
3635
|
+
// If `activeConsumer` isn't live, then this is a no-op, since we can replace the producer in
|
|
3636
|
+
// `activeConsumer.producerNode` directly. However, if `activeConsumer` is live, then we need
|
|
3637
|
+
// to remove it from the stale producer's `liveConsumer`s.
|
|
3638
|
+
if (consumerIsLive(activeConsumer)) {
|
|
3639
|
+
const staleProducer = activeConsumer.producerNode[idx];
|
|
3640
|
+
producerRemoveLiveConsumerAtIndex(staleProducer, activeConsumer.producerIndexOfThis[idx]);
|
|
3641
|
+
// At this point, the only record of `staleProducer` is the reference at
|
|
3642
|
+
// `activeConsumer.producerNode[idx]` which will be overwritten below.
|
|
3643
|
+
}
|
|
3614
3644
|
}
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3645
|
+
if (activeConsumer.producerNode[idx] !== node) {
|
|
3646
|
+
// We're a new dependency of the consumer (at `idx`).
|
|
3647
|
+
activeConsumer.producerNode[idx] = node;
|
|
3648
|
+
// If the active consumer is live, then add it as a live consumer. If not, then use 0 as a
|
|
3649
|
+
// placeholder value.
|
|
3650
|
+
activeConsumer.producerIndexOfThis[idx] =
|
|
3651
|
+
consumerIsLive(activeConsumer) ? producerAddLiveConsumer(node, activeConsumer, idx) : 0;
|
|
3652
|
+
}
|
|
3653
|
+
activeConsumer.producerLastReadVersion[idx] = node.version;
|
|
3620
3654
|
}
|
|
3621
|
-
|
|
3622
|
-
// Required as the signals library is in a separate package, so we need to explicitly ensure the
|
|
3623
3655
|
/**
|
|
3624
|
-
*
|
|
3656
|
+
* Ensure this producer's `version` is up-to-date.
|
|
3625
3657
|
*/
|
|
3626
|
-
|
|
3658
|
+
function producerUpdateValueVersion(node) {
|
|
3659
|
+
if (consumerIsLive(node) && !node.dirty) {
|
|
3660
|
+
// A live consumer will be marked dirty by producers, so a clean state means that its version
|
|
3661
|
+
// is guaranteed to be up-to-date.
|
|
3662
|
+
return;
|
|
3663
|
+
}
|
|
3664
|
+
if (!node.producerMustRecompute(node) && !consumerPollProducersForChange(node)) {
|
|
3665
|
+
// None of our producers report a change since the last time they were read, so no
|
|
3666
|
+
// recomputation of our value is necessary, and we can consider ourselves clean.
|
|
3667
|
+
node.dirty = false;
|
|
3668
|
+
return;
|
|
3669
|
+
}
|
|
3670
|
+
node.producerRecomputeValue(node);
|
|
3671
|
+
// After recomputing the value, we're no longer dirty.
|
|
3672
|
+
node.dirty = false;
|
|
3673
|
+
}
|
|
3627
3674
|
/**
|
|
3628
|
-
*
|
|
3629
|
-
* consumer).
|
|
3675
|
+
* Propagate a dirty notification to live consumers of this producer.
|
|
3630
3676
|
*/
|
|
3631
|
-
|
|
3677
|
+
function producerNotifyConsumers(node) {
|
|
3678
|
+
if (node.liveConsumerNode === undefined) {
|
|
3679
|
+
return;
|
|
3680
|
+
}
|
|
3681
|
+
// Prevent signal reads when we're updating the graph
|
|
3682
|
+
const prev = inNotificationPhase;
|
|
3683
|
+
inNotificationPhase = true;
|
|
3684
|
+
try {
|
|
3685
|
+
for (const consumer of node.liveConsumerNode) {
|
|
3686
|
+
if (!consumer.dirty) {
|
|
3687
|
+
consumerMarkDirty(consumer);
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
finally {
|
|
3692
|
+
inNotificationPhase = prev;
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3632
3695
|
/**
|
|
3633
|
-
* Whether
|
|
3696
|
+
* Whether this `ReactiveNode` in its producer capacity is currently allowed to initiate updates,
|
|
3697
|
+
* based on the current consumer context.
|
|
3634
3698
|
*/
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3699
|
+
function producerUpdatesAllowed() {
|
|
3700
|
+
return activeConsumer?.consumerAllowSignalWrites !== false;
|
|
3701
|
+
}
|
|
3702
|
+
function consumerMarkDirty(node) {
|
|
3703
|
+
node.dirty = true;
|
|
3704
|
+
producerNotifyConsumers(node);
|
|
3705
|
+
node.consumerMarkedDirty?.(node);
|
|
3640
3706
|
}
|
|
3641
3707
|
/**
|
|
3642
|
-
*
|
|
3643
|
-
*
|
|
3644
|
-
* Nodes can be producers of reactive values, consumers of other reactive values, or both.
|
|
3645
|
-
*
|
|
3646
|
-
* Producers are nodes that produce values, and can be depended upon by consumer nodes.
|
|
3647
|
-
*
|
|
3648
|
-
* Producers expose a monotonic `valueVersion` counter, and are responsible for incrementing this
|
|
3649
|
-
* version when their value semantically changes. Some producers may produce their values lazily and
|
|
3650
|
-
* thus at times need to be polled for potential updates to their value (and by extension their
|
|
3651
|
-
* `valueVersion`). This is accomplished via the `onProducerUpdateValueVersion` method for
|
|
3652
|
-
* implemented by producers, which should perform whatever calculations are necessary to ensure
|
|
3653
|
-
* `valueVersion` is up to date.
|
|
3708
|
+
* Prepare this consumer to run a computation in its reactive context.
|
|
3654
3709
|
*
|
|
3655
|
-
*
|
|
3656
|
-
*
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
*
|
|
3664
|
-
* reads it needs and establish a new set of dependencies as a result.
|
|
3710
|
+
* Must be called by subclasses which represent reactive computations, before those computations
|
|
3711
|
+
* begin.
|
|
3712
|
+
*/
|
|
3713
|
+
function consumerBeforeComputation(node) {
|
|
3714
|
+
node && (node.nextProducerIndex = 0);
|
|
3715
|
+
return setActiveConsumer(node);
|
|
3716
|
+
}
|
|
3717
|
+
/**
|
|
3718
|
+
* Finalize this consumer's state after a reactive computation has run.
|
|
3665
3719
|
*
|
|
3666
|
-
*
|
|
3667
|
-
*
|
|
3668
|
-
* comparing the consumer's `trackingVersion` to the version at which the dependency was
|
|
3669
|
-
* last observed.
|
|
3720
|
+
* Must be called by subclasses which represent reactive computations, after those computations
|
|
3721
|
+
* have finished.
|
|
3670
3722
|
*/
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
*/
|
|
3677
|
-
this.ref = newWeakRef(this);
|
|
3678
|
-
/**
|
|
3679
|
-
* Edges to producers on which this node depends (in its consumer capacity).
|
|
3680
|
-
*/
|
|
3681
|
-
this.producers = new Map();
|
|
3682
|
-
/**
|
|
3683
|
-
* Edges to consumers on which this node depends (in its producer capacity).
|
|
3684
|
-
*/
|
|
3685
|
-
this.consumers = new Map();
|
|
3686
|
-
/**
|
|
3687
|
-
* Monotonically increasing counter representing a version of this `Consumer`'s
|
|
3688
|
-
* dependencies.
|
|
3689
|
-
*/
|
|
3690
|
-
this.trackingVersion = 0;
|
|
3691
|
-
/**
|
|
3692
|
-
* Monotonically increasing counter which increases when the value of this `Producer`
|
|
3693
|
-
* semantically changes.
|
|
3694
|
-
*/
|
|
3695
|
-
this.valueVersion = 0;
|
|
3723
|
+
function consumerAfterComputation(node, prevConsumer) {
|
|
3724
|
+
setActiveConsumer(prevConsumer);
|
|
3725
|
+
if (!node || node.producerNode === undefined || node.producerIndexOfThis === undefined ||
|
|
3726
|
+
node.producerLastReadVersion === undefined) {
|
|
3727
|
+
return;
|
|
3696
3728
|
}
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
* rerun any reactions.
|
|
3703
|
-
*/
|
|
3704
|
-
consumerPollProducersForChange() {
|
|
3705
|
-
for (const [producerId, edge] of this.producers) {
|
|
3706
|
-
const producer = edge.producerNode.deref();
|
|
3707
|
-
// On Safari < 16.1 deref can return null, we need to check for null also.
|
|
3708
|
-
// See https://github.com/WebKit/WebKit/commit/44c15ba58912faab38b534fef909dd9e13e095e0
|
|
3709
|
-
if (producer == null || edge.atTrackingVersion !== this.trackingVersion) {
|
|
3710
|
-
// This dependency edge is stale, so remove it.
|
|
3711
|
-
this.producers.delete(producerId);
|
|
3712
|
-
producer?.consumers.delete(this.id);
|
|
3713
|
-
continue;
|
|
3714
|
-
}
|
|
3715
|
-
if (producer.producerPollStatus(edge.seenValueVersion)) {
|
|
3716
|
-
// One of the dependencies reports a real value change.
|
|
3717
|
-
return true;
|
|
3718
|
-
}
|
|
3729
|
+
if (consumerIsLive(node)) {
|
|
3730
|
+
// For live consumers, we need to remove the producer -> consumer edge for any stale producers
|
|
3731
|
+
// which weren't dependencies after the recomputation.
|
|
3732
|
+
for (let i = node.nextProducerIndex; i < node.producerNode.length; i++) {
|
|
3733
|
+
producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
|
|
3719
3734
|
}
|
|
3720
|
-
// No dependency reported a real value change, so the `Consumer` has also not been
|
|
3721
|
-
// impacted.
|
|
3722
|
-
return false;
|
|
3723
3735
|
}
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
const prev = inNotificationPhase;
|
|
3730
|
-
inNotificationPhase = true;
|
|
3731
|
-
try {
|
|
3732
|
-
for (const [consumerId, edge] of this.consumers) {
|
|
3733
|
-
const consumer = edge.consumerNode.deref();
|
|
3734
|
-
// On Safari < 16.1 deref can return null, we need to check for null also.
|
|
3735
|
-
// See https://github.com/WebKit/WebKit/commit/44c15ba58912faab38b534fef909dd9e13e095e0
|
|
3736
|
-
if (consumer == null || consumer.trackingVersion !== edge.atTrackingVersion) {
|
|
3737
|
-
this.consumers.delete(consumerId);
|
|
3738
|
-
consumer?.producers.delete(this.id);
|
|
3739
|
-
continue;
|
|
3740
|
-
}
|
|
3741
|
-
consumer.onConsumerDependencyMayHaveChanged();
|
|
3742
|
-
}
|
|
3743
|
-
}
|
|
3744
|
-
finally {
|
|
3745
|
-
inNotificationPhase = prev;
|
|
3746
|
-
}
|
|
3736
|
+
// Truncate the producer tracking arrays.
|
|
3737
|
+
for (let i = node.nextProducerIndex; i < node.producerNode.length; i++) {
|
|
3738
|
+
node.producerNode.pop();
|
|
3739
|
+
node.producerLastReadVersion.pop();
|
|
3740
|
+
node.producerIndexOfThis.pop();
|
|
3747
3741
|
}
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3742
|
+
}
|
|
3743
|
+
/**
|
|
3744
|
+
* Determine whether this consumer has any dependencies which have changed since the last time
|
|
3745
|
+
* they were read.
|
|
3746
|
+
*/
|
|
3747
|
+
function consumerPollProducersForChange(node) {
|
|
3748
|
+
assertConsumerNode(node);
|
|
3749
|
+
// Poll producers for change.
|
|
3750
|
+
for (let i = 0; i < node.producerNode.length; i++) {
|
|
3751
|
+
const producer = node.producerNode[i];
|
|
3752
|
+
const seenVersion = node.producerLastReadVersion[i];
|
|
3753
|
+
// First check the versions. A mismatch means that the producer's value is known to have
|
|
3754
|
+
// changed since the last time we read it.
|
|
3755
|
+
if (seenVersion !== producer.version) {
|
|
3756
|
+
return true;
|
|
3759
3757
|
}
|
|
3760
|
-
//
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
atTrackingVersion: activeConsumer.trackingVersion,
|
|
3768
|
-
};
|
|
3769
|
-
activeConsumer.producers.set(this.id, edge);
|
|
3770
|
-
this.consumers.set(activeConsumer.id, edge);
|
|
3758
|
+
// The producer's version is the same as the last time we read it, but it might itself be
|
|
3759
|
+
// stale. Force the producer to recompute its version (calculating a new value if necessary).
|
|
3760
|
+
producerUpdateValueVersion(producer);
|
|
3761
|
+
// Now when we do this check, `producer.version` is guaranteed to be up to date, so if the
|
|
3762
|
+
// versions still match then it has not changed since the last time we read it.
|
|
3763
|
+
if (seenVersion !== producer.version) {
|
|
3764
|
+
return true;
|
|
3771
3765
|
}
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3766
|
+
}
|
|
3767
|
+
return false;
|
|
3768
|
+
}
|
|
3769
|
+
/**
|
|
3770
|
+
* Disconnect this consumer from the graph.
|
|
3771
|
+
*/
|
|
3772
|
+
function consumerDestroy(node) {
|
|
3773
|
+
assertConsumerNode(node);
|
|
3774
|
+
if (consumerIsLive(node)) {
|
|
3775
|
+
// Drop all connections from the graph to this node.
|
|
3776
|
+
for (let i = 0; i < node.producerNode.length; i++) {
|
|
3777
|
+
producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
|
|
3775
3778
|
}
|
|
3776
3779
|
}
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3780
|
+
// Truncate all the arrays to drop all connection from this node to the graph.
|
|
3781
|
+
node.producerNode.length = node.producerLastReadVersion.length = node.producerIndexOfThis.length =
|
|
3782
|
+
0;
|
|
3783
|
+
if (node.liveConsumerNode) {
|
|
3784
|
+
node.liveConsumerNode.length = node.liveConsumerIndexOfThis.length = 0;
|
|
3782
3785
|
}
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3786
|
+
}
|
|
3787
|
+
/**
|
|
3788
|
+
* Add `consumer` as a live consumer of this node.
|
|
3789
|
+
*
|
|
3790
|
+
* Note that this operation is potentially transitive. If this node becomes live, then it becomes
|
|
3791
|
+
* a live consumer of all of its current producers.
|
|
3792
|
+
*/
|
|
3793
|
+
function producerAddLiveConsumer(node, consumer, indexOfThis) {
|
|
3794
|
+
assertProducerNode(node);
|
|
3795
|
+
assertConsumerNode(node);
|
|
3796
|
+
if (node.liveConsumerNode.length === 0) {
|
|
3797
|
+
// When going from 0 to 1 live consumers, we become a live consumer to our producers.
|
|
3798
|
+
for (let i = 0; i < node.producerNode.length; i++) {
|
|
3799
|
+
node.producerIndexOfThis[i] = producerAddLiveConsumer(node.producerNode[i], node, i);
|
|
3800
|
+
}
|
|
3789
3801
|
}
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3802
|
+
node.liveConsumerIndexOfThis.push(indexOfThis);
|
|
3803
|
+
return node.liveConsumerNode.push(consumer) - 1;
|
|
3804
|
+
}
|
|
3805
|
+
/**
|
|
3806
|
+
* Remove the live consumer at `idx`.
|
|
3807
|
+
*/
|
|
3808
|
+
function producerRemoveLiveConsumerAtIndex(node, idx) {
|
|
3809
|
+
assertProducerNode(node);
|
|
3810
|
+
assertConsumerNode(node);
|
|
3811
|
+
if (node.liveConsumerNode.length === 1) {
|
|
3812
|
+
// When removing the last live consumer, we will no longer be live. We need to remove
|
|
3813
|
+
// ourselves from our producers' tracking (which may cause consumer-producers to lose
|
|
3814
|
+
// liveness as well).
|
|
3815
|
+
for (let i = 0; i < node.producerNode.length; i++) {
|
|
3816
|
+
producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
|
|
3800
3817
|
}
|
|
3801
|
-
// Trigger the `Producer` to update its `valueVersion` if necessary.
|
|
3802
|
-
this.onProducerUpdateValueVersion();
|
|
3803
|
-
// At this point, we can trust `producer.valueVersion`.
|
|
3804
|
-
return this.valueVersion !== lastSeenValueVersion;
|
|
3805
3818
|
}
|
|
3819
|
+
// Move the last value of `liveConsumers` into `idx`. Note that if there's only a single
|
|
3820
|
+
// live consumer, this is a no-op.
|
|
3821
|
+
const lastIdx = node.liveConsumerNode.length - 1;
|
|
3822
|
+
node.liveConsumerNode[idx] = node.liveConsumerNode[lastIdx];
|
|
3823
|
+
node.liveConsumerIndexOfThis[idx] = node.liveConsumerIndexOfThis[lastIdx];
|
|
3824
|
+
// Truncate the array.
|
|
3825
|
+
node.liveConsumerNode.length--;
|
|
3826
|
+
node.liveConsumerIndexOfThis.length--;
|
|
3827
|
+
// If the index is still valid, then we need to fix the index pointer from the producer to this
|
|
3828
|
+
// consumer, and update it from `lastIdx` to `idx` (accounting for the move above).
|
|
3829
|
+
if (idx < node.liveConsumerNode.length) {
|
|
3830
|
+
const idxProducer = node.liveConsumerIndexOfThis[idx];
|
|
3831
|
+
const consumer = node.liveConsumerNode[idx];
|
|
3832
|
+
assertConsumerNode(consumer);
|
|
3833
|
+
consumer.producerIndexOfThis[idxProducer] = idx;
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
function consumerIsLive(node) {
|
|
3837
|
+
return node.consumerIsAlwaysLive || (node?.liveConsumerNode?.length ?? 0) > 0;
|
|
3838
|
+
}
|
|
3839
|
+
function assertConsumerNode(node) {
|
|
3840
|
+
node.producerNode ??= [];
|
|
3841
|
+
node.producerIndexOfThis ??= [];
|
|
3842
|
+
node.producerLastReadVersion ??= [];
|
|
3843
|
+
}
|
|
3844
|
+
function assertProducerNode(node) {
|
|
3845
|
+
node.liveConsumerNode ??= [];
|
|
3846
|
+
node.liveConsumerIndexOfThis ??= [];
|
|
3806
3847
|
}
|
|
3807
3848
|
|
|
3808
3849
|
/**
|
|
@@ -3811,10 +3852,21 @@ class ReactiveNode {
|
|
|
3811
3852
|
* @developerPreview
|
|
3812
3853
|
*/
|
|
3813
3854
|
function computed(computation, options) {
|
|
3814
|
-
const node =
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3855
|
+
const node = Object.create(COMPUTED_NODE);
|
|
3856
|
+
node.computation = computation;
|
|
3857
|
+
options?.equal && (node.equal = options.equal);
|
|
3858
|
+
const computed = () => {
|
|
3859
|
+
// Check if the value needs updating before returning it.
|
|
3860
|
+
producerUpdateValueVersion(node);
|
|
3861
|
+
// Record that someone looked at this signal.
|
|
3862
|
+
producerAccessed(node);
|
|
3863
|
+
if (node.value === ERRORED) {
|
|
3864
|
+
throw node.error;
|
|
3865
|
+
}
|
|
3866
|
+
return node.value;
|
|
3867
|
+
};
|
|
3868
|
+
computed[SIGNAL] = node;
|
|
3869
|
+
return computed;
|
|
3818
3870
|
}
|
|
3819
3871
|
/**
|
|
3820
3872
|
* A dedicated symbol used before a computed value has been calculated for the first time.
|
|
@@ -3833,108 +3885,47 @@ const COMPUTING = Symbol('COMPUTING');
|
|
|
3833
3885
|
* Explicitly typed as `any` so we can use it as signal's value.
|
|
3834
3886
|
*/
|
|
3835
3887
|
const ERRORED = Symbol('ERRORED');
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
* This can also be one of the special values `UNSET`, `COMPUTING`, or `ERRORED`.
|
|
3850
|
-
*/
|
|
3851
|
-
this.value = UNSET;
|
|
3852
|
-
/**
|
|
3853
|
-
* If `value` is `ERRORED`, the error caught from the last computation attempt which will
|
|
3854
|
-
* be re-thrown.
|
|
3855
|
-
*/
|
|
3856
|
-
this.error = null;
|
|
3857
|
-
/**
|
|
3858
|
-
* Flag indicating that the computation is currently stale, meaning that one of the
|
|
3859
|
-
* dependencies has notified of a potential change.
|
|
3860
|
-
*
|
|
3861
|
-
* It's possible that no dependency has _actually_ changed, in which case the `stale`
|
|
3862
|
-
* state can be resolved without recomputing the value.
|
|
3863
|
-
*/
|
|
3864
|
-
this.stale = true;
|
|
3865
|
-
this.consumerAllowSignalWrites = false;
|
|
3866
|
-
}
|
|
3867
|
-
onConsumerDependencyMayHaveChanged() {
|
|
3868
|
-
if (this.stale) {
|
|
3869
|
-
// We've already notified consumers that this value has potentially changed.
|
|
3870
|
-
return;
|
|
3871
|
-
}
|
|
3872
|
-
// Record that the currently cached value may be stale.
|
|
3873
|
-
this.stale = true;
|
|
3874
|
-
// Notify any consumers about the potential change.
|
|
3875
|
-
this.producerMayHaveChanged();
|
|
3876
|
-
}
|
|
3877
|
-
onProducerUpdateValueVersion() {
|
|
3878
|
-
if (!this.stale) {
|
|
3879
|
-
// The current value and its version are already up to date.
|
|
3880
|
-
return;
|
|
3881
|
-
}
|
|
3882
|
-
// The current value is stale. Check whether we need to produce a new one.
|
|
3883
|
-
if (this.value !== UNSET && this.value !== COMPUTING &&
|
|
3884
|
-
!this.consumerPollProducersForChange()) {
|
|
3885
|
-
// Even though we were previously notified of a potential dependency update, all of
|
|
3886
|
-
// our dependencies report that they have not actually changed in value, so we can
|
|
3887
|
-
// resolve the stale state without needing to recompute the current value.
|
|
3888
|
-
this.stale = false;
|
|
3889
|
-
return;
|
|
3890
|
-
}
|
|
3891
|
-
// The current value is stale, and needs to be recomputed. It still may not change -
|
|
3892
|
-
// that depends on whether the newly computed value is equal to the old.
|
|
3893
|
-
this.recomputeValue();
|
|
3894
|
-
}
|
|
3895
|
-
recomputeValue() {
|
|
3896
|
-
if (this.value === COMPUTING) {
|
|
3888
|
+
const COMPUTED_NODE = {
|
|
3889
|
+
...REACTIVE_NODE,
|
|
3890
|
+
value: UNSET,
|
|
3891
|
+
dirty: true,
|
|
3892
|
+
error: null,
|
|
3893
|
+
equal: defaultEquals,
|
|
3894
|
+
producerMustRecompute(node) {
|
|
3895
|
+
// Force a recomputation if there's no current value, or if the current value is in the process
|
|
3896
|
+
// of being calculated (which should throw an error).
|
|
3897
|
+
return node.value === UNSET || node.value === COMPUTING;
|
|
3898
|
+
},
|
|
3899
|
+
producerRecomputeValue(node) {
|
|
3900
|
+
if (node.value === COMPUTING) {
|
|
3897
3901
|
// Our computation somehow led to a cyclic read of itself.
|
|
3898
3902
|
throw new Error('Detected cycle in computations.');
|
|
3899
3903
|
}
|
|
3900
|
-
const oldValue =
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
this.trackingVersion++;
|
|
3904
|
-
const prevConsumer = setActiveConsumer(this);
|
|
3904
|
+
const oldValue = node.value;
|
|
3905
|
+
node.value = COMPUTING;
|
|
3906
|
+
const prevConsumer = consumerBeforeComputation(node);
|
|
3905
3907
|
let newValue;
|
|
3906
3908
|
try {
|
|
3907
|
-
newValue =
|
|
3909
|
+
newValue = node.computation();
|
|
3908
3910
|
}
|
|
3909
3911
|
catch (err) {
|
|
3910
3912
|
newValue = ERRORED;
|
|
3911
|
-
|
|
3913
|
+
node.error = err;
|
|
3912
3914
|
}
|
|
3913
3915
|
finally {
|
|
3914
|
-
|
|
3916
|
+
consumerAfterComputation(node, prevConsumer);
|
|
3915
3917
|
}
|
|
3916
|
-
this.stale = false;
|
|
3917
3918
|
if (oldValue !== UNSET && oldValue !== ERRORED && newValue !== ERRORED &&
|
|
3918
|
-
|
|
3919
|
+
node.equal(oldValue, newValue)) {
|
|
3919
3920
|
// No change to `valueVersion` - old and new values are
|
|
3920
3921
|
// semantically equivalent.
|
|
3921
|
-
|
|
3922
|
+
node.value = oldValue;
|
|
3922
3923
|
return;
|
|
3923
3924
|
}
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
}
|
|
3927
|
-
|
|
3928
|
-
// Check if the value needs updating before returning it.
|
|
3929
|
-
this.onProducerUpdateValueVersion();
|
|
3930
|
-
// Record that someone looked at this signal.
|
|
3931
|
-
this.producerAccessed();
|
|
3932
|
-
if (this.value === ERRORED) {
|
|
3933
|
-
throw this.error;
|
|
3934
|
-
}
|
|
3935
|
-
return this.value;
|
|
3936
|
-
}
|
|
3937
|
-
}
|
|
3925
|
+
node.value = newValue;
|
|
3926
|
+
node.version++;
|
|
3927
|
+
},
|
|
3928
|
+
};
|
|
3938
3929
|
|
|
3939
3930
|
function defaultThrowError() {
|
|
3940
3931
|
throw new Error();
|
|
@@ -3954,88 +3945,24 @@ function setThrowInvalidWriteToSignalError(fn) {
|
|
|
3954
3945
|
* of setting a signal.
|
|
3955
3946
|
*/
|
|
3956
3947
|
let postSignalSetFn = null;
|
|
3957
|
-
class WritableSignalImpl extends ReactiveNode {
|
|
3958
|
-
constructor(value, equal) {
|
|
3959
|
-
super();
|
|
3960
|
-
this.value = value;
|
|
3961
|
-
this.equal = equal;
|
|
3962
|
-
this.consumerAllowSignalWrites = false;
|
|
3963
|
-
}
|
|
3964
|
-
onConsumerDependencyMayHaveChanged() {
|
|
3965
|
-
// This never happens for writable signals as they're not consumers.
|
|
3966
|
-
}
|
|
3967
|
-
onProducerUpdateValueVersion() {
|
|
3968
|
-
// Writable signal value versions are always up to date.
|
|
3969
|
-
}
|
|
3970
|
-
/**
|
|
3971
|
-
* Directly update the value of the signal to a new value, which may or may not be
|
|
3972
|
-
* equal to the previous.
|
|
3973
|
-
*
|
|
3974
|
-
* In the event that `newValue` is semantically equal to the current value, `set` is
|
|
3975
|
-
* a no-op.
|
|
3976
|
-
*/
|
|
3977
|
-
set(newValue) {
|
|
3978
|
-
if (!this.producerUpdatesAllowed) {
|
|
3979
|
-
throwInvalidWriteToSignalError();
|
|
3980
|
-
}
|
|
3981
|
-
if (!this.equal(this.value, newValue)) {
|
|
3982
|
-
this.value = newValue;
|
|
3983
|
-
this.valueVersion++;
|
|
3984
|
-
this.producerMayHaveChanged();
|
|
3985
|
-
postSignalSetFn?.();
|
|
3986
|
-
}
|
|
3987
|
-
}
|
|
3988
|
-
/**
|
|
3989
|
-
* Derive a new value for the signal from its current value using the `updater` function.
|
|
3990
|
-
*
|
|
3991
|
-
* This is equivalent to calling `set` on the result of running `updater` on the current
|
|
3992
|
-
* value.
|
|
3993
|
-
*/
|
|
3994
|
-
update(updater) {
|
|
3995
|
-
if (!this.producerUpdatesAllowed) {
|
|
3996
|
-
throwInvalidWriteToSignalError();
|
|
3997
|
-
}
|
|
3998
|
-
this.set(updater(this.value));
|
|
3999
|
-
}
|
|
4000
|
-
/**
|
|
4001
|
-
* Calls `mutator` on the current value and assumes that it has been mutated.
|
|
4002
|
-
*/
|
|
4003
|
-
mutate(mutator) {
|
|
4004
|
-
if (!this.producerUpdatesAllowed) {
|
|
4005
|
-
throwInvalidWriteToSignalError();
|
|
4006
|
-
}
|
|
4007
|
-
// Mutate bypasses equality checks as it's by definition changing the value.
|
|
4008
|
-
mutator(this.value);
|
|
4009
|
-
this.valueVersion++;
|
|
4010
|
-
this.producerMayHaveChanged();
|
|
4011
|
-
postSignalSetFn?.();
|
|
4012
|
-
}
|
|
4013
|
-
asReadonly() {
|
|
4014
|
-
if (this.readonlySignal === undefined) {
|
|
4015
|
-
this.readonlySignal = createSignalFromFunction(this, () => this.signal());
|
|
4016
|
-
}
|
|
4017
|
-
return this.readonlySignal;
|
|
4018
|
-
}
|
|
4019
|
-
signal() {
|
|
4020
|
-
this.producerAccessed();
|
|
4021
|
-
return this.value;
|
|
4022
|
-
}
|
|
4023
|
-
}
|
|
4024
3948
|
/**
|
|
4025
3949
|
* Create a `Signal` that can be set or updated directly.
|
|
4026
3950
|
*
|
|
4027
3951
|
* @developerPreview
|
|
4028
3952
|
*/
|
|
4029
3953
|
function signal(initialValue, options) {
|
|
4030
|
-
const
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
3954
|
+
const node = Object.create(SIGNAL_NODE);
|
|
3955
|
+
node.value = initialValue;
|
|
3956
|
+
options?.equal && (node.equal = options.equal);
|
|
3957
|
+
function signalFn() {
|
|
3958
|
+
producerAccessed(node);
|
|
3959
|
+
return node.value;
|
|
3960
|
+
}
|
|
3961
|
+
signalFn.set = signalSetFn;
|
|
3962
|
+
signalFn.update = signalUpdateFn;
|
|
3963
|
+
signalFn.mutate = signalMutateFn;
|
|
3964
|
+
signalFn.asReadonly = signalAsReadonlyFn;
|
|
3965
|
+
signalFn[SIGNAL] = node;
|
|
4039
3966
|
return signalFn;
|
|
4040
3967
|
}
|
|
4041
3968
|
function setPostSignalSetFn(fn) {
|
|
@@ -4043,6 +3970,50 @@ function setPostSignalSetFn(fn) {
|
|
|
4043
3970
|
postSignalSetFn = fn;
|
|
4044
3971
|
return prev;
|
|
4045
3972
|
}
|
|
3973
|
+
const SIGNAL_NODE = {
|
|
3974
|
+
...REACTIVE_NODE,
|
|
3975
|
+
equal: defaultEquals,
|
|
3976
|
+
readonlyFn: undefined,
|
|
3977
|
+
};
|
|
3978
|
+
function signalValueChanged(node) {
|
|
3979
|
+
node.version++;
|
|
3980
|
+
producerNotifyConsumers(node);
|
|
3981
|
+
postSignalSetFn?.();
|
|
3982
|
+
}
|
|
3983
|
+
function signalSetFn(newValue) {
|
|
3984
|
+
const node = this[SIGNAL];
|
|
3985
|
+
if (!producerUpdatesAllowed()) {
|
|
3986
|
+
throwInvalidWriteToSignalError();
|
|
3987
|
+
}
|
|
3988
|
+
if (!node.equal(node.value, newValue)) {
|
|
3989
|
+
node.value = newValue;
|
|
3990
|
+
signalValueChanged(node);
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3993
|
+
function signalUpdateFn(updater) {
|
|
3994
|
+
if (!producerUpdatesAllowed()) {
|
|
3995
|
+
throwInvalidWriteToSignalError();
|
|
3996
|
+
}
|
|
3997
|
+
signalSetFn.call(this, updater(this[SIGNAL].value));
|
|
3998
|
+
}
|
|
3999
|
+
function signalMutateFn(mutator) {
|
|
4000
|
+
const node = this[SIGNAL];
|
|
4001
|
+
if (!producerUpdatesAllowed()) {
|
|
4002
|
+
throwInvalidWriteToSignalError();
|
|
4003
|
+
}
|
|
4004
|
+
// Mutate bypasses equality checks as it's by definition changing the value.
|
|
4005
|
+
mutator(node.value);
|
|
4006
|
+
signalValueChanged(node);
|
|
4007
|
+
}
|
|
4008
|
+
function signalAsReadonlyFn() {
|
|
4009
|
+
const node = this[SIGNAL];
|
|
4010
|
+
if (node.readonlyFn === undefined) {
|
|
4011
|
+
const readonlyFn = () => this();
|
|
4012
|
+
readonlyFn[SIGNAL] = node;
|
|
4013
|
+
node.readonlyFn = readonlyFn;
|
|
4014
|
+
}
|
|
4015
|
+
return node.readonlyFn;
|
|
4016
|
+
}
|
|
4046
4017
|
|
|
4047
4018
|
/**
|
|
4048
4019
|
* Execute an arbitrary function in a non-reactive (non-tracking) context. The executed function
|
|
@@ -4062,63 +4033,53 @@ function untracked(nonReactiveReadsFn) {
|
|
|
4062
4033
|
}
|
|
4063
4034
|
}
|
|
4064
4035
|
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
this.dirty = false;
|
|
4079
|
-
this.cleanupFn = NOOP_CLEANUP_FN;
|
|
4080
|
-
this.registerOnCleanup = (cleanupFn) => {
|
|
4081
|
-
this.cleanupFn = cleanupFn;
|
|
4082
|
-
};
|
|
4083
|
-
this.consumerAllowSignalWrites = allowSignalWrites;
|
|
4084
|
-
}
|
|
4085
|
-
notify() {
|
|
4086
|
-
if (!this.dirty) {
|
|
4087
|
-
this.schedule(this);
|
|
4088
|
-
}
|
|
4089
|
-
this.dirty = true;
|
|
4090
|
-
}
|
|
4091
|
-
onConsumerDependencyMayHaveChanged() {
|
|
4092
|
-
this.notify();
|
|
4093
|
-
}
|
|
4094
|
-
onProducerUpdateValueVersion() {
|
|
4095
|
-
// Watches are not producers.
|
|
4096
|
-
}
|
|
4097
|
-
/**
|
|
4098
|
-
* Execute the reactive expression in the context of this `Watch` consumer.
|
|
4099
|
-
*
|
|
4100
|
-
* Should be called by the user scheduling algorithm when the provided
|
|
4101
|
-
* `schedule` hook is called by `Watch`.
|
|
4102
|
-
*/
|
|
4103
|
-
run() {
|
|
4104
|
-
this.dirty = false;
|
|
4105
|
-
if (this.trackingVersion !== 0 && !this.consumerPollProducersForChange()) {
|
|
4036
|
+
function watch(fn, schedule, allowSignalWrites) {
|
|
4037
|
+
const node = Object.create(WATCH_NODE);
|
|
4038
|
+
if (allowSignalWrites) {
|
|
4039
|
+
node.consumerAllowSignalWrites = true;
|
|
4040
|
+
}
|
|
4041
|
+
node.fn = fn;
|
|
4042
|
+
node.schedule = schedule;
|
|
4043
|
+
const registerOnCleanup = (cleanupFn) => {
|
|
4044
|
+
node.cleanupFn = cleanupFn;
|
|
4045
|
+
};
|
|
4046
|
+
const run = () => {
|
|
4047
|
+
node.dirty = false;
|
|
4048
|
+
if (node.hasRun && !consumerPollProducersForChange(node)) {
|
|
4106
4049
|
return;
|
|
4107
4050
|
}
|
|
4108
|
-
|
|
4109
|
-
|
|
4051
|
+
node.hasRun = true;
|
|
4052
|
+
const prevConsumer = consumerBeforeComputation(node);
|
|
4110
4053
|
try {
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4054
|
+
node.cleanupFn();
|
|
4055
|
+
node.cleanupFn = NOOP_CLEANUP_FN;
|
|
4056
|
+
node.fn(registerOnCleanup);
|
|
4114
4057
|
}
|
|
4115
4058
|
finally {
|
|
4116
|
-
|
|
4059
|
+
consumerAfterComputation(node, prevConsumer);
|
|
4117
4060
|
}
|
|
4118
|
-
}
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4061
|
+
};
|
|
4062
|
+
node.ref = {
|
|
4063
|
+
notify: () => consumerMarkDirty(node),
|
|
4064
|
+
run,
|
|
4065
|
+
cleanup: () => node.cleanupFn(),
|
|
4066
|
+
};
|
|
4067
|
+
return node.ref;
|
|
4068
|
+
}
|
|
4069
|
+
const NOOP_CLEANUP_FN = () => { };
|
|
4070
|
+
const WATCH_NODE = {
|
|
4071
|
+
...REACTIVE_NODE,
|
|
4072
|
+
consumerIsAlwaysLive: true,
|
|
4073
|
+
consumerAllowSignalWrites: false,
|
|
4074
|
+
consumerMarkedDirty: (node) => {
|
|
4075
|
+
node.schedule(node.ref);
|
|
4076
|
+
},
|
|
4077
|
+
hasRun: false,
|
|
4078
|
+
cleanupFn: NOOP_CLEANUP_FN,
|
|
4079
|
+
};
|
|
4080
|
+
|
|
4081
|
+
function setAlternateWeakRefImpl(impl) {
|
|
4082
|
+
// TODO: remove this function
|
|
4122
4083
|
}
|
|
4123
4084
|
|
|
4124
4085
|
/**
|
|
@@ -6373,11 +6334,17 @@ function setAllowDuplicateNgModuleIdsForTest(allowDuplicates) {
|
|
|
6373
6334
|
* `InjectionToken` is parameterized on `T` which is the type of object which will be returned by
|
|
6374
6335
|
* the `Injector`. This provides an additional level of type safety.
|
|
6375
6336
|
*
|
|
6376
|
-
*
|
|
6377
|
-
*
|
|
6378
|
-
*
|
|
6379
|
-
*
|
|
6380
|
-
*
|
|
6337
|
+
* <div class="alert is-helpful">
|
|
6338
|
+
*
|
|
6339
|
+
* **Important Note**: Ensure that you use the same instance of the `InjectionToken` in both the
|
|
6340
|
+
* provider and the injection call. Creating a new instance of `InjectionToken` in different places,
|
|
6341
|
+
* even with the same description, will be treated as different tokens by Angular's DI system,
|
|
6342
|
+
* leading to a `NullInjectorError`.
|
|
6343
|
+
*
|
|
6344
|
+
* </div>
|
|
6345
|
+
*
|
|
6346
|
+
* <code-example format="typescript" language="typescript" path="injection-token/src/main.ts"
|
|
6347
|
+
* region="InjectionToken"></code-example>
|
|
6381
6348
|
*
|
|
6382
6349
|
* When creating an `InjectionToken`, you can optionally specify a factory function which returns
|
|
6383
6350
|
* (possibly by creating) a default value of the parameterized type `T`. This sets up the
|
|
@@ -6404,7 +6371,6 @@ function setAllowDuplicateNgModuleIdsForTest(allowDuplicates) {
|
|
|
6404
6371
|
*
|
|
6405
6372
|
* {@example core/di/ts/injector_spec.ts region='ShakableInjectionToken'}
|
|
6406
6373
|
*
|
|
6407
|
-
*
|
|
6408
6374
|
* @publicApi
|
|
6409
6375
|
*/
|
|
6410
6376
|
class InjectionToken {
|
|
@@ -8009,7 +7975,7 @@ const COMMENT_DISALLOWED = /^>|^->|<!--|-->|--!>|<!-$/g;
|
|
|
8009
7975
|
/**
|
|
8010
7976
|
* Delimiter in the disallowed strings which needs to be wrapped with zero with character.
|
|
8011
7977
|
*/
|
|
8012
|
-
const COMMENT_DELIMITER = /(<|>)
|
|
7978
|
+
const COMMENT_DELIMITER = /(<|>)/g;
|
|
8013
7979
|
const COMMENT_DELIMITER_ESCAPED = '\u200B$1\u200B';
|
|
8014
7980
|
/**
|
|
8015
7981
|
* Escape the content of comment strings so that it can be safely inserted into a comment node.
|
|
@@ -8776,8 +8742,8 @@ function detachView(lContainer, removeIndex) {
|
|
|
8776
8742
|
function destroyLView(tView, lView) {
|
|
8777
8743
|
if (!(lView[FLAGS] & 256 /* LViewFlags.Destroyed */)) {
|
|
8778
8744
|
const renderer = lView[RENDERER];
|
|
8779
|
-
lView[REACTIVE_TEMPLATE_CONSUMER]
|
|
8780
|
-
lView[REACTIVE_HOST_BINDING_CONSUMER]
|
|
8745
|
+
lView[REACTIVE_TEMPLATE_CONSUMER] && consumerDestroy(lView[REACTIVE_TEMPLATE_CONSUMER]);
|
|
8746
|
+
lView[REACTIVE_HOST_BINDING_CONSUMER] && consumerDestroy(lView[REACTIVE_HOST_BINDING_CONSUMER]);
|
|
8781
8747
|
if (renderer.destroyNode) {
|
|
8782
8748
|
applyView(tView, lView, renderer, 3 /* WalkTNodeTreeAction.Destroy */, null, null);
|
|
8783
8749
|
}
|
|
@@ -10894,105 +10860,676 @@ class Renderer2 {
|
|
|
10894
10860
|
this.destroyNode = null;
|
|
10895
10861
|
}
|
|
10896
10862
|
/**
|
|
10897
|
-
* @internal
|
|
10898
|
-
* @nocollapse
|
|
10863
|
+
* @internal
|
|
10864
|
+
* @nocollapse
|
|
10865
|
+
*/
|
|
10866
|
+
static { this.__NG_ELEMENT_ID__ = () => injectRenderer2(); }
|
|
10867
|
+
}
|
|
10868
|
+
/** Injects a Renderer2 for the current component. */
|
|
10869
|
+
function injectRenderer2() {
|
|
10870
|
+
// We need the Renderer to be based on the component that it's being injected into, however since
|
|
10871
|
+
// DI happens before we've entered its view, `getLView` will return the parent view instead.
|
|
10872
|
+
const lView = getLView();
|
|
10873
|
+
const tNode = getCurrentTNode();
|
|
10874
|
+
const nodeAtIndex = getComponentLViewByIndex(tNode.index, lView);
|
|
10875
|
+
return (isLView(nodeAtIndex) ? nodeAtIndex : lView)[RENDERER];
|
|
10876
|
+
}
|
|
10877
|
+
|
|
10878
|
+
/**
|
|
10879
|
+
* Sanitizer is used by the views to sanitize potentially dangerous values.
|
|
10880
|
+
*
|
|
10881
|
+
* @publicApi
|
|
10882
|
+
*/
|
|
10883
|
+
class Sanitizer {
|
|
10884
|
+
/** @nocollapse */
|
|
10885
|
+
static { this.ɵprov = ɵɵdefineInjectable({
|
|
10886
|
+
token: Sanitizer,
|
|
10887
|
+
providedIn: 'root',
|
|
10888
|
+
factory: () => null,
|
|
10889
|
+
}); }
|
|
10890
|
+
}
|
|
10891
|
+
|
|
10892
|
+
/**
|
|
10893
|
+
* @description Represents the version of Angular
|
|
10894
|
+
*
|
|
10895
|
+
* @publicApi
|
|
10896
|
+
*/
|
|
10897
|
+
class Version {
|
|
10898
|
+
constructor(full) {
|
|
10899
|
+
this.full = full;
|
|
10900
|
+
this.major = full.split('.')[0];
|
|
10901
|
+
this.minor = full.split('.')[1];
|
|
10902
|
+
this.patch = full.split('.').slice(2).join('.');
|
|
10903
|
+
}
|
|
10904
|
+
}
|
|
10905
|
+
/**
|
|
10906
|
+
* @publicApi
|
|
10907
|
+
*/
|
|
10908
|
+
const VERSION = new Version('16.2.4');
|
|
10909
|
+
|
|
10910
|
+
// This default value is when checking the hierarchy for a token.
|
|
10911
|
+
//
|
|
10912
|
+
// It means both:
|
|
10913
|
+
// - the token is not provided by the current injector,
|
|
10914
|
+
// - only the element injectors should be checked (ie do not check module injectors
|
|
10915
|
+
//
|
|
10916
|
+
// mod1
|
|
10917
|
+
// /
|
|
10918
|
+
// el1 mod2
|
|
10919
|
+
// \ /
|
|
10920
|
+
// el2
|
|
10921
|
+
//
|
|
10922
|
+
// When requesting el2.injector.get(token), we should check in the following order and return the
|
|
10923
|
+
// first found value:
|
|
10924
|
+
// - el2.injector.get(token, default)
|
|
10925
|
+
// - el1.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) -> do not check the module
|
|
10926
|
+
// - mod2.injector.get(token, default)
|
|
10927
|
+
const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
|
|
10928
|
+
|
|
10929
|
+
/**
|
|
10930
|
+
* `DestroyRef` lets you set callbacks to run for any cleanup or destruction behavior.
|
|
10931
|
+
* The scope of this destruction depends on where `DestroyRef` is injected. If `DestroyRef`
|
|
10932
|
+
* is injected in a component or directive, the callbacks run when that component or
|
|
10933
|
+
* directive is destroyed. Otherwise the callbacks run when a corresponding injector is destroyed.
|
|
10934
|
+
*
|
|
10935
|
+
* @publicApi
|
|
10936
|
+
*/
|
|
10937
|
+
class DestroyRef {
|
|
10938
|
+
/**
|
|
10939
|
+
* @internal
|
|
10940
|
+
* @nocollapse
|
|
10941
|
+
*/
|
|
10942
|
+
static { this.__NG_ELEMENT_ID__ = injectDestroyRef; }
|
|
10943
|
+
/**
|
|
10944
|
+
* @internal
|
|
10945
|
+
* @nocollapse
|
|
10946
|
+
*/
|
|
10947
|
+
static { this.__NG_ENV_ID__ = (injector) => injector; }
|
|
10948
|
+
}
|
|
10949
|
+
class NodeInjectorDestroyRef extends DestroyRef {
|
|
10950
|
+
constructor(_lView) {
|
|
10951
|
+
super();
|
|
10952
|
+
this._lView = _lView;
|
|
10953
|
+
}
|
|
10954
|
+
onDestroy(callback) {
|
|
10955
|
+
storeLViewOnDestroy(this._lView, callback);
|
|
10956
|
+
return () => removeLViewOnDestroy(this._lView, callback);
|
|
10957
|
+
}
|
|
10958
|
+
}
|
|
10959
|
+
function injectDestroyRef() {
|
|
10960
|
+
return new NodeInjectorDestroyRef(getLView());
|
|
10961
|
+
}
|
|
10962
|
+
|
|
10963
|
+
/// <reference types="rxjs" />
|
|
10964
|
+
class EventEmitter_ extends Subject {
|
|
10965
|
+
constructor(isAsync = false) {
|
|
10966
|
+
super();
|
|
10967
|
+
this.__isAsync = isAsync;
|
|
10968
|
+
}
|
|
10969
|
+
emit(value) {
|
|
10970
|
+
super.next(value);
|
|
10971
|
+
}
|
|
10972
|
+
subscribe(observerOrNext, error, complete) {
|
|
10973
|
+
let nextFn = observerOrNext;
|
|
10974
|
+
let errorFn = error || (() => null);
|
|
10975
|
+
let completeFn = complete;
|
|
10976
|
+
if (observerOrNext && typeof observerOrNext === 'object') {
|
|
10977
|
+
const observer = observerOrNext;
|
|
10978
|
+
nextFn = observer.next?.bind(observer);
|
|
10979
|
+
errorFn = observer.error?.bind(observer);
|
|
10980
|
+
completeFn = observer.complete?.bind(observer);
|
|
10981
|
+
}
|
|
10982
|
+
if (this.__isAsync) {
|
|
10983
|
+
errorFn = _wrapInTimeout(errorFn);
|
|
10984
|
+
if (nextFn) {
|
|
10985
|
+
nextFn = _wrapInTimeout(nextFn);
|
|
10986
|
+
}
|
|
10987
|
+
if (completeFn) {
|
|
10988
|
+
completeFn = _wrapInTimeout(completeFn);
|
|
10989
|
+
}
|
|
10990
|
+
}
|
|
10991
|
+
const sink = super.subscribe({ next: nextFn, error: errorFn, complete: completeFn });
|
|
10992
|
+
if (observerOrNext instanceof Subscription) {
|
|
10993
|
+
observerOrNext.add(sink);
|
|
10994
|
+
}
|
|
10995
|
+
return sink;
|
|
10996
|
+
}
|
|
10997
|
+
}
|
|
10998
|
+
function _wrapInTimeout(fn) {
|
|
10999
|
+
return (value) => {
|
|
11000
|
+
setTimeout(fn, undefined, value);
|
|
11001
|
+
};
|
|
11002
|
+
}
|
|
11003
|
+
/**
|
|
11004
|
+
* @publicApi
|
|
11005
|
+
*/
|
|
11006
|
+
const EventEmitter = EventEmitter_;
|
|
11007
|
+
|
|
11008
|
+
function noop(...args) {
|
|
11009
|
+
// Do nothing.
|
|
11010
|
+
}
|
|
11011
|
+
|
|
11012
|
+
function getNativeRequestAnimationFrame() {
|
|
11013
|
+
// Note: the `getNativeRequestAnimationFrame` is used in the `NgZone` class, but we cannot use the
|
|
11014
|
+
// `inject` function. The `NgZone` instance may be created manually, and thus the injection
|
|
11015
|
+
// context will be unavailable. This might be enough to check whether `requestAnimationFrame` is
|
|
11016
|
+
// available because otherwise, we'll fall back to `setTimeout`.
|
|
11017
|
+
const isBrowser = typeof _global['requestAnimationFrame'] === 'function';
|
|
11018
|
+
// Note: `requestAnimationFrame` is unavailable when the code runs in the Node.js environment. We
|
|
11019
|
+
// use `setTimeout` because no changes are required other than checking if the current platform is
|
|
11020
|
+
// the browser. `setTimeout` is a well-established API that is available in both environments.
|
|
11021
|
+
// `requestAnimationFrame` is used in the browser to coalesce event tasks since event tasks are
|
|
11022
|
+
// usually executed within the same rendering frame (but this is more implementation details of
|
|
11023
|
+
// browsers).
|
|
11024
|
+
let nativeRequestAnimationFrame = _global[isBrowser ? 'requestAnimationFrame' : 'setTimeout'];
|
|
11025
|
+
let nativeCancelAnimationFrame = _global[isBrowser ? 'cancelAnimationFrame' : 'clearTimeout'];
|
|
11026
|
+
if (typeof Zone !== 'undefined' && nativeRequestAnimationFrame && nativeCancelAnimationFrame) {
|
|
11027
|
+
// Note: zone.js sets original implementations on patched APIs behind the
|
|
11028
|
+
// `__zone_symbol__OriginalDelegate` key (see `attachOriginToPatched`). Given the following
|
|
11029
|
+
// example: `window.requestAnimationFrame.__zone_symbol__OriginalDelegate`; this would return an
|
|
11030
|
+
// unpatched implementation of the `requestAnimationFrame`, which isn't intercepted by the
|
|
11031
|
+
// Angular zone. We use the unpatched implementation to avoid another change detection when
|
|
11032
|
+
// coalescing tasks.
|
|
11033
|
+
const unpatchedRequestAnimationFrame = nativeRequestAnimationFrame[Zone.__symbol__('OriginalDelegate')];
|
|
11034
|
+
if (unpatchedRequestAnimationFrame) {
|
|
11035
|
+
nativeRequestAnimationFrame = unpatchedRequestAnimationFrame;
|
|
11036
|
+
}
|
|
11037
|
+
const unpatchedCancelAnimationFrame = nativeCancelAnimationFrame[Zone.__symbol__('OriginalDelegate')];
|
|
11038
|
+
if (unpatchedCancelAnimationFrame) {
|
|
11039
|
+
nativeCancelAnimationFrame = unpatchedCancelAnimationFrame;
|
|
11040
|
+
}
|
|
11041
|
+
}
|
|
11042
|
+
return { nativeRequestAnimationFrame, nativeCancelAnimationFrame };
|
|
11043
|
+
}
|
|
11044
|
+
|
|
11045
|
+
class AsyncStackTaggingZoneSpec {
|
|
11046
|
+
constructor(namePrefix, consoleAsyncStackTaggingImpl = console) {
|
|
11047
|
+
this.name = 'asyncStackTagging for ' + namePrefix;
|
|
11048
|
+
this.createTask = consoleAsyncStackTaggingImpl?.createTask ?? (() => null);
|
|
11049
|
+
}
|
|
11050
|
+
onScheduleTask(delegate, _current, target, task) {
|
|
11051
|
+
task.consoleTask = this.createTask(`Zone - ${task.source || task.type}`);
|
|
11052
|
+
return delegate.scheduleTask(target, task);
|
|
11053
|
+
}
|
|
11054
|
+
onInvokeTask(delegate, _currentZone, targetZone, task, applyThis, applyArgs) {
|
|
11055
|
+
let ret;
|
|
11056
|
+
if (task.consoleTask) {
|
|
11057
|
+
ret = task.consoleTask.run(() => delegate.invokeTask(targetZone, task, applyThis, applyArgs));
|
|
11058
|
+
}
|
|
11059
|
+
else {
|
|
11060
|
+
ret = delegate.invokeTask(targetZone, task, applyThis, applyArgs);
|
|
11061
|
+
}
|
|
11062
|
+
return ret;
|
|
11063
|
+
}
|
|
11064
|
+
}
|
|
11065
|
+
|
|
11066
|
+
/**
|
|
11067
|
+
* An injectable service for executing work inside or outside of the Angular zone.
|
|
11068
|
+
*
|
|
11069
|
+
* The most common use of this service is to optimize performance when starting a work consisting of
|
|
11070
|
+
* one or more asynchronous tasks that don't require UI updates or error handling to be handled by
|
|
11071
|
+
* Angular. Such tasks can be kicked off via {@link #runOutsideAngular} and if needed, these tasks
|
|
11072
|
+
* can reenter the Angular zone via {@link #run}.
|
|
11073
|
+
*
|
|
11074
|
+
* <!-- TODO: add/fix links to:
|
|
11075
|
+
* - docs explaining zones and the use of zones in Angular and change-detection
|
|
11076
|
+
* - link to runOutsideAngular/run (throughout this file!)
|
|
11077
|
+
* -->
|
|
11078
|
+
*
|
|
11079
|
+
* @usageNotes
|
|
11080
|
+
* ### Example
|
|
11081
|
+
*
|
|
11082
|
+
* ```
|
|
11083
|
+
* import {Component, NgZone} from '@angular/core';
|
|
11084
|
+
* import {NgIf} from '@angular/common';
|
|
11085
|
+
*
|
|
11086
|
+
* @Component({
|
|
11087
|
+
* selector: 'ng-zone-demo',
|
|
11088
|
+
* template: `
|
|
11089
|
+
* <h2>Demo: NgZone</h2>
|
|
11090
|
+
*
|
|
11091
|
+
* <p>Progress: {{progress}}%</p>
|
|
11092
|
+
* <p *ngIf="progress >= 100">Done processing {{label}} of Angular zone!</p>
|
|
11093
|
+
*
|
|
11094
|
+
* <button (click)="processWithinAngularZone()">Process within Angular zone</button>
|
|
11095
|
+
* <button (click)="processOutsideOfAngularZone()">Process outside of Angular zone</button>
|
|
11096
|
+
* `,
|
|
11097
|
+
* })
|
|
11098
|
+
* export class NgZoneDemo {
|
|
11099
|
+
* progress: number = 0;
|
|
11100
|
+
* label: string;
|
|
11101
|
+
*
|
|
11102
|
+
* constructor(private _ngZone: NgZone) {}
|
|
11103
|
+
*
|
|
11104
|
+
* // Loop inside the Angular zone
|
|
11105
|
+
* // so the UI DOES refresh after each setTimeout cycle
|
|
11106
|
+
* processWithinAngularZone() {
|
|
11107
|
+
* this.label = 'inside';
|
|
11108
|
+
* this.progress = 0;
|
|
11109
|
+
* this._increaseProgress(() => console.log('Inside Done!'));
|
|
11110
|
+
* }
|
|
11111
|
+
*
|
|
11112
|
+
* // Loop outside of the Angular zone
|
|
11113
|
+
* // so the UI DOES NOT refresh after each setTimeout cycle
|
|
11114
|
+
* processOutsideOfAngularZone() {
|
|
11115
|
+
* this.label = 'outside';
|
|
11116
|
+
* this.progress = 0;
|
|
11117
|
+
* this._ngZone.runOutsideAngular(() => {
|
|
11118
|
+
* this._increaseProgress(() => {
|
|
11119
|
+
* // reenter the Angular zone and display done
|
|
11120
|
+
* this._ngZone.run(() => { console.log('Outside Done!'); });
|
|
11121
|
+
* });
|
|
11122
|
+
* });
|
|
11123
|
+
* }
|
|
11124
|
+
*
|
|
11125
|
+
* _increaseProgress(doneCallback: () => void) {
|
|
11126
|
+
* this.progress += 1;
|
|
11127
|
+
* console.log(`Current progress: ${this.progress}%`);
|
|
11128
|
+
*
|
|
11129
|
+
* if (this.progress < 100) {
|
|
11130
|
+
* window.setTimeout(() => this._increaseProgress(doneCallback), 10);
|
|
11131
|
+
* } else {
|
|
11132
|
+
* doneCallback();
|
|
11133
|
+
* }
|
|
11134
|
+
* }
|
|
11135
|
+
* }
|
|
11136
|
+
* ```
|
|
11137
|
+
*
|
|
11138
|
+
* @publicApi
|
|
11139
|
+
*/
|
|
11140
|
+
class NgZone {
|
|
11141
|
+
constructor({ enableLongStackTrace = false, shouldCoalesceEventChangeDetection = false, shouldCoalesceRunChangeDetection = false }) {
|
|
11142
|
+
this.hasPendingMacrotasks = false;
|
|
11143
|
+
this.hasPendingMicrotasks = false;
|
|
11144
|
+
/**
|
|
11145
|
+
* Whether there are no outstanding microtasks or macrotasks.
|
|
11146
|
+
*/
|
|
11147
|
+
this.isStable = true;
|
|
11148
|
+
/**
|
|
11149
|
+
* Notifies when code enters Angular Zone. This gets fired first on VM Turn.
|
|
11150
|
+
*/
|
|
11151
|
+
this.onUnstable = new EventEmitter(false);
|
|
11152
|
+
/**
|
|
11153
|
+
* Notifies when there is no more microtasks enqueued in the current VM Turn.
|
|
11154
|
+
* This is a hint for Angular to do change detection, which may enqueue more microtasks.
|
|
11155
|
+
* For this reason this event can fire multiple times per VM Turn.
|
|
11156
|
+
*/
|
|
11157
|
+
this.onMicrotaskEmpty = new EventEmitter(false);
|
|
11158
|
+
/**
|
|
11159
|
+
* Notifies when the last `onMicrotaskEmpty` has run and there are no more microtasks, which
|
|
11160
|
+
* implies we are about to relinquish VM turn.
|
|
11161
|
+
* This event gets called just once.
|
|
11162
|
+
*/
|
|
11163
|
+
this.onStable = new EventEmitter(false);
|
|
11164
|
+
/**
|
|
11165
|
+
* Notifies that an error has been delivered.
|
|
11166
|
+
*/
|
|
11167
|
+
this.onError = new EventEmitter(false);
|
|
11168
|
+
if (typeof Zone == 'undefined') {
|
|
11169
|
+
throw new RuntimeError(908 /* RuntimeErrorCode.MISSING_ZONEJS */, ngDevMode && `In this configuration Angular requires Zone.js`);
|
|
11170
|
+
}
|
|
11171
|
+
Zone.assertZonePatched();
|
|
11172
|
+
const self = this;
|
|
11173
|
+
self._nesting = 0;
|
|
11174
|
+
self._outer = self._inner = Zone.current;
|
|
11175
|
+
// AsyncStackTaggingZoneSpec provides `linked stack traces` to show
|
|
11176
|
+
// where the async operation is scheduled. For more details, refer
|
|
11177
|
+
// to this article, https://developer.chrome.com/blog/devtools-better-angular-debugging/
|
|
11178
|
+
// And we only import this AsyncStackTaggingZoneSpec in development mode,
|
|
11179
|
+
// in the production mode, the AsyncStackTaggingZoneSpec will be tree shaken away.
|
|
11180
|
+
if (ngDevMode) {
|
|
11181
|
+
self._inner = self._inner.fork(new AsyncStackTaggingZoneSpec('Angular'));
|
|
11182
|
+
}
|
|
11183
|
+
if (Zone['TaskTrackingZoneSpec']) {
|
|
11184
|
+
self._inner = self._inner.fork(new Zone['TaskTrackingZoneSpec']);
|
|
11185
|
+
}
|
|
11186
|
+
if (enableLongStackTrace && Zone['longStackTraceZoneSpec']) {
|
|
11187
|
+
self._inner = self._inner.fork(Zone['longStackTraceZoneSpec']);
|
|
11188
|
+
}
|
|
11189
|
+
// if shouldCoalesceRunChangeDetection is true, all tasks including event tasks will be
|
|
11190
|
+
// coalesced, so shouldCoalesceEventChangeDetection option is not necessary and can be skipped.
|
|
11191
|
+
self.shouldCoalesceEventChangeDetection =
|
|
11192
|
+
!shouldCoalesceRunChangeDetection && shouldCoalesceEventChangeDetection;
|
|
11193
|
+
self.shouldCoalesceRunChangeDetection = shouldCoalesceRunChangeDetection;
|
|
11194
|
+
self.lastRequestAnimationFrameId = -1;
|
|
11195
|
+
self.nativeRequestAnimationFrame = getNativeRequestAnimationFrame().nativeRequestAnimationFrame;
|
|
11196
|
+
forkInnerZoneWithAngularBehavior(self);
|
|
11197
|
+
}
|
|
11198
|
+
/**
|
|
11199
|
+
This method checks whether the method call happens within an Angular Zone instance.
|
|
11200
|
+
*/
|
|
11201
|
+
static isInAngularZone() {
|
|
11202
|
+
// Zone needs to be checked, because this method might be called even when NoopNgZone is used.
|
|
11203
|
+
return typeof Zone !== 'undefined' && Zone.current.get('isAngularZone') === true;
|
|
11204
|
+
}
|
|
11205
|
+
/**
|
|
11206
|
+
Assures that the method is called within the Angular Zone, otherwise throws an error.
|
|
11207
|
+
*/
|
|
11208
|
+
static assertInAngularZone() {
|
|
11209
|
+
if (!NgZone.isInAngularZone()) {
|
|
11210
|
+
throw new RuntimeError(909 /* RuntimeErrorCode.UNEXPECTED_ZONE_STATE */, ngDevMode && 'Expected to be in Angular Zone, but it is not!');
|
|
11211
|
+
}
|
|
11212
|
+
}
|
|
11213
|
+
/**
|
|
11214
|
+
Assures that the method is called outside of the Angular Zone, otherwise throws an error.
|
|
11215
|
+
*/
|
|
11216
|
+
static assertNotInAngularZone() {
|
|
11217
|
+
if (NgZone.isInAngularZone()) {
|
|
11218
|
+
throw new RuntimeError(909 /* RuntimeErrorCode.UNEXPECTED_ZONE_STATE */, ngDevMode && 'Expected to not be in Angular Zone, but it is!');
|
|
11219
|
+
}
|
|
11220
|
+
}
|
|
11221
|
+
/**
|
|
11222
|
+
* Executes the `fn` function synchronously within the Angular zone and returns value returned by
|
|
11223
|
+
* the function.
|
|
11224
|
+
*
|
|
11225
|
+
* Running functions via `run` allows you to reenter Angular zone from a task that was executed
|
|
11226
|
+
* outside of the Angular zone (typically started via {@link #runOutsideAngular}).
|
|
11227
|
+
*
|
|
11228
|
+
* Any future tasks or microtasks scheduled from within this function will continue executing from
|
|
11229
|
+
* within the Angular zone.
|
|
11230
|
+
*
|
|
11231
|
+
* If a synchronous error happens it will be rethrown and not reported via `onError`.
|
|
11232
|
+
*/
|
|
11233
|
+
run(fn, applyThis, applyArgs) {
|
|
11234
|
+
return this._inner.run(fn, applyThis, applyArgs);
|
|
11235
|
+
}
|
|
11236
|
+
/**
|
|
11237
|
+
* Executes the `fn` function synchronously within the Angular zone as a task and returns value
|
|
11238
|
+
* returned by the function.
|
|
11239
|
+
*
|
|
11240
|
+
* Running functions via `run` allows you to reenter Angular zone from a task that was executed
|
|
11241
|
+
* outside of the Angular zone (typically started via {@link #runOutsideAngular}).
|
|
11242
|
+
*
|
|
11243
|
+
* Any future tasks or microtasks scheduled from within this function will continue executing from
|
|
11244
|
+
* within the Angular zone.
|
|
11245
|
+
*
|
|
11246
|
+
* If a synchronous error happens it will be rethrown and not reported via `onError`.
|
|
11247
|
+
*/
|
|
11248
|
+
runTask(fn, applyThis, applyArgs, name) {
|
|
11249
|
+
const zone = this._inner;
|
|
11250
|
+
const task = zone.scheduleEventTask('NgZoneEvent: ' + name, fn, EMPTY_PAYLOAD, noop, noop);
|
|
11251
|
+
try {
|
|
11252
|
+
return zone.runTask(task, applyThis, applyArgs);
|
|
11253
|
+
}
|
|
11254
|
+
finally {
|
|
11255
|
+
zone.cancelTask(task);
|
|
11256
|
+
}
|
|
11257
|
+
}
|
|
11258
|
+
/**
|
|
11259
|
+
* Same as `run`, except that synchronous errors are caught and forwarded via `onError` and not
|
|
11260
|
+
* rethrown.
|
|
11261
|
+
*/
|
|
11262
|
+
runGuarded(fn, applyThis, applyArgs) {
|
|
11263
|
+
return this._inner.runGuarded(fn, applyThis, applyArgs);
|
|
11264
|
+
}
|
|
11265
|
+
/**
|
|
11266
|
+
* Executes the `fn` function synchronously in Angular's parent zone and returns value returned by
|
|
11267
|
+
* the function.
|
|
11268
|
+
*
|
|
11269
|
+
* Running functions via {@link #runOutsideAngular} allows you to escape Angular's zone and do
|
|
11270
|
+
* work that
|
|
11271
|
+
* doesn't trigger Angular change-detection or is subject to Angular's error handling.
|
|
11272
|
+
*
|
|
11273
|
+
* Any future tasks or microtasks scheduled from within this function will continue executing from
|
|
11274
|
+
* outside of the Angular zone.
|
|
11275
|
+
*
|
|
11276
|
+
* Use {@link #run} to reenter the Angular zone and do work that updates the application model.
|
|
11277
|
+
*/
|
|
11278
|
+
runOutsideAngular(fn) {
|
|
11279
|
+
return this._outer.run(fn);
|
|
11280
|
+
}
|
|
11281
|
+
}
|
|
11282
|
+
const EMPTY_PAYLOAD = {};
|
|
11283
|
+
function checkStable(zone) {
|
|
11284
|
+
// TODO: @JiaLiPassion, should check zone.isCheckStableRunning to prevent
|
|
11285
|
+
// re-entry. The case is:
|
|
11286
|
+
//
|
|
11287
|
+
// @Component({...})
|
|
11288
|
+
// export class AppComponent {
|
|
11289
|
+
// constructor(private ngZone: NgZone) {
|
|
11290
|
+
// this.ngZone.onStable.subscribe(() => {
|
|
11291
|
+
// this.ngZone.run(() => console.log('stable'););
|
|
11292
|
+
// });
|
|
11293
|
+
// }
|
|
11294
|
+
//
|
|
11295
|
+
// The onStable subscriber run another function inside ngZone
|
|
11296
|
+
// which causes `checkStable()` re-entry.
|
|
11297
|
+
// But this fix causes some issues in g3, so this fix will be
|
|
11298
|
+
// launched in another PR.
|
|
11299
|
+
if (zone._nesting == 0 && !zone.hasPendingMicrotasks && !zone.isStable) {
|
|
11300
|
+
try {
|
|
11301
|
+
zone._nesting++;
|
|
11302
|
+
zone.onMicrotaskEmpty.emit(null);
|
|
11303
|
+
}
|
|
11304
|
+
finally {
|
|
11305
|
+
zone._nesting--;
|
|
11306
|
+
if (!zone.hasPendingMicrotasks) {
|
|
11307
|
+
try {
|
|
11308
|
+
zone.runOutsideAngular(() => zone.onStable.emit(null));
|
|
11309
|
+
}
|
|
11310
|
+
finally {
|
|
11311
|
+
zone.isStable = true;
|
|
11312
|
+
}
|
|
11313
|
+
}
|
|
11314
|
+
}
|
|
11315
|
+
}
|
|
11316
|
+
}
|
|
11317
|
+
function delayChangeDetectionForEvents(zone) {
|
|
11318
|
+
/**
|
|
11319
|
+
* We also need to check _nesting here
|
|
11320
|
+
* Consider the following case with shouldCoalesceRunChangeDetection = true
|
|
11321
|
+
*
|
|
11322
|
+
* ngZone.run(() => {});
|
|
11323
|
+
* ngZone.run(() => {});
|
|
11324
|
+
*
|
|
11325
|
+
* We want the two `ngZone.run()` only trigger one change detection
|
|
11326
|
+
* when shouldCoalesceRunChangeDetection is true.
|
|
11327
|
+
* And because in this case, change detection run in async way(requestAnimationFrame),
|
|
11328
|
+
* so we also need to check the _nesting here to prevent multiple
|
|
11329
|
+
* change detections.
|
|
10899
11330
|
*/
|
|
10900
|
-
|
|
11331
|
+
if (zone.isCheckStableRunning || zone.lastRequestAnimationFrameId !== -1) {
|
|
11332
|
+
return;
|
|
11333
|
+
}
|
|
11334
|
+
zone.lastRequestAnimationFrameId = zone.nativeRequestAnimationFrame.call(_global, () => {
|
|
11335
|
+
// This is a work around for https://github.com/angular/angular/issues/36839.
|
|
11336
|
+
// The core issue is that when event coalescing is enabled it is possible for microtasks
|
|
11337
|
+
// to get flushed too early (As is the case with `Promise.then`) between the
|
|
11338
|
+
// coalescing eventTasks.
|
|
11339
|
+
//
|
|
11340
|
+
// To workaround this we schedule a "fake" eventTask before we process the
|
|
11341
|
+
// coalescing eventTasks. The benefit of this is that the "fake" container eventTask
|
|
11342
|
+
// will prevent the microtasks queue from getting drained in between the coalescing
|
|
11343
|
+
// eventTask execution.
|
|
11344
|
+
if (!zone.fakeTopEventTask) {
|
|
11345
|
+
zone.fakeTopEventTask = Zone.root.scheduleEventTask('fakeTopEventTask', () => {
|
|
11346
|
+
zone.lastRequestAnimationFrameId = -1;
|
|
11347
|
+
updateMicroTaskStatus(zone);
|
|
11348
|
+
zone.isCheckStableRunning = true;
|
|
11349
|
+
checkStable(zone);
|
|
11350
|
+
zone.isCheckStableRunning = false;
|
|
11351
|
+
}, undefined, () => { }, () => { });
|
|
11352
|
+
}
|
|
11353
|
+
zone.fakeTopEventTask.invoke();
|
|
11354
|
+
});
|
|
11355
|
+
updateMicroTaskStatus(zone);
|
|
10901
11356
|
}
|
|
10902
|
-
|
|
10903
|
-
|
|
10904
|
-
|
|
10905
|
-
|
|
10906
|
-
|
|
10907
|
-
|
|
10908
|
-
|
|
10909
|
-
|
|
11357
|
+
function forkInnerZoneWithAngularBehavior(zone) {
|
|
11358
|
+
const delayChangeDetectionForEventsDelegate = () => {
|
|
11359
|
+
delayChangeDetectionForEvents(zone);
|
|
11360
|
+
};
|
|
11361
|
+
zone._inner = zone._inner.fork({
|
|
11362
|
+
name: 'angular',
|
|
11363
|
+
properties: { 'isAngularZone': true },
|
|
11364
|
+
onInvokeTask: (delegate, current, target, task, applyThis, applyArgs) => {
|
|
11365
|
+
if (shouldBeIgnoredByZone(applyArgs)) {
|
|
11366
|
+
return delegate.invokeTask(target, task, applyThis, applyArgs);
|
|
11367
|
+
}
|
|
11368
|
+
try {
|
|
11369
|
+
onEnter(zone);
|
|
11370
|
+
return delegate.invokeTask(target, task, applyThis, applyArgs);
|
|
11371
|
+
}
|
|
11372
|
+
finally {
|
|
11373
|
+
if ((zone.shouldCoalesceEventChangeDetection && task.type === 'eventTask') ||
|
|
11374
|
+
zone.shouldCoalesceRunChangeDetection) {
|
|
11375
|
+
delayChangeDetectionForEventsDelegate();
|
|
11376
|
+
}
|
|
11377
|
+
onLeave(zone);
|
|
11378
|
+
}
|
|
11379
|
+
},
|
|
11380
|
+
onInvoke: (delegate, current, target, callback, applyThis, applyArgs, source) => {
|
|
11381
|
+
try {
|
|
11382
|
+
onEnter(zone);
|
|
11383
|
+
return delegate.invoke(target, callback, applyThis, applyArgs, source);
|
|
11384
|
+
}
|
|
11385
|
+
finally {
|
|
11386
|
+
if (zone.shouldCoalesceRunChangeDetection) {
|
|
11387
|
+
delayChangeDetectionForEventsDelegate();
|
|
11388
|
+
}
|
|
11389
|
+
onLeave(zone);
|
|
11390
|
+
}
|
|
11391
|
+
},
|
|
11392
|
+
onHasTask: (delegate, current, target, hasTaskState) => {
|
|
11393
|
+
delegate.hasTask(target, hasTaskState);
|
|
11394
|
+
if (current === target) {
|
|
11395
|
+
// We are only interested in hasTask events which originate from our zone
|
|
11396
|
+
// (A child hasTask event is not interesting to us)
|
|
11397
|
+
if (hasTaskState.change == 'microTask') {
|
|
11398
|
+
zone._hasPendingMicrotasks = hasTaskState.microTask;
|
|
11399
|
+
updateMicroTaskStatus(zone);
|
|
11400
|
+
checkStable(zone);
|
|
11401
|
+
}
|
|
11402
|
+
else if (hasTaskState.change == 'macroTask') {
|
|
11403
|
+
zone.hasPendingMacrotasks = hasTaskState.macroTask;
|
|
11404
|
+
}
|
|
11405
|
+
}
|
|
11406
|
+
},
|
|
11407
|
+
onHandleError: (delegate, current, target, error) => {
|
|
11408
|
+
delegate.handleError(target, error);
|
|
11409
|
+
zone.runOutsideAngular(() => zone.onError.emit(error));
|
|
11410
|
+
return false;
|
|
11411
|
+
}
|
|
11412
|
+
});
|
|
10910
11413
|
}
|
|
10911
|
-
|
|
10912
|
-
|
|
10913
|
-
|
|
10914
|
-
|
|
10915
|
-
|
|
10916
|
-
|
|
10917
|
-
|
|
10918
|
-
|
|
10919
|
-
|
|
10920
|
-
token: Sanitizer,
|
|
10921
|
-
providedIn: 'root',
|
|
10922
|
-
factory: () => null,
|
|
10923
|
-
}); }
|
|
11414
|
+
function updateMicroTaskStatus(zone) {
|
|
11415
|
+
if (zone._hasPendingMicrotasks ||
|
|
11416
|
+
((zone.shouldCoalesceEventChangeDetection || zone.shouldCoalesceRunChangeDetection) &&
|
|
11417
|
+
zone.lastRequestAnimationFrameId !== -1)) {
|
|
11418
|
+
zone.hasPendingMicrotasks = true;
|
|
11419
|
+
}
|
|
11420
|
+
else {
|
|
11421
|
+
zone.hasPendingMicrotasks = false;
|
|
11422
|
+
}
|
|
10924
11423
|
}
|
|
10925
|
-
|
|
10926
|
-
|
|
10927
|
-
|
|
10928
|
-
|
|
10929
|
-
|
|
10930
|
-
*/
|
|
10931
|
-
class Version {
|
|
10932
|
-
constructor(full) {
|
|
10933
|
-
this.full = full;
|
|
10934
|
-
this.major = full.split('.')[0];
|
|
10935
|
-
this.minor = full.split('.')[1];
|
|
10936
|
-
this.patch = full.split('.').slice(2).join('.');
|
|
11424
|
+
function onEnter(zone) {
|
|
11425
|
+
zone._nesting++;
|
|
11426
|
+
if (zone.isStable) {
|
|
11427
|
+
zone.isStable = false;
|
|
11428
|
+
zone.onUnstable.emit(null);
|
|
10937
11429
|
}
|
|
10938
11430
|
}
|
|
11431
|
+
function onLeave(zone) {
|
|
11432
|
+
zone._nesting--;
|
|
11433
|
+
checkStable(zone);
|
|
11434
|
+
}
|
|
10939
11435
|
/**
|
|
10940
|
-
*
|
|
11436
|
+
* Provides a noop implementation of `NgZone` which does nothing. This zone requires explicit calls
|
|
11437
|
+
* to framework to perform rendering.
|
|
10941
11438
|
*/
|
|
10942
|
-
|
|
10943
|
-
|
|
10944
|
-
|
|
10945
|
-
|
|
10946
|
-
|
|
10947
|
-
|
|
10948
|
-
|
|
10949
|
-
|
|
10950
|
-
|
|
10951
|
-
|
|
10952
|
-
|
|
10953
|
-
|
|
10954
|
-
|
|
10955
|
-
|
|
10956
|
-
|
|
10957
|
-
|
|
10958
|
-
|
|
10959
|
-
|
|
10960
|
-
|
|
10961
|
-
|
|
10962
|
-
|
|
11439
|
+
class NoopNgZone {
|
|
11440
|
+
constructor() {
|
|
11441
|
+
this.hasPendingMicrotasks = false;
|
|
11442
|
+
this.hasPendingMacrotasks = false;
|
|
11443
|
+
this.isStable = true;
|
|
11444
|
+
this.onUnstable = new EventEmitter();
|
|
11445
|
+
this.onMicrotaskEmpty = new EventEmitter();
|
|
11446
|
+
this.onStable = new EventEmitter();
|
|
11447
|
+
this.onError = new EventEmitter();
|
|
11448
|
+
}
|
|
11449
|
+
run(fn, applyThis, applyArgs) {
|
|
11450
|
+
return fn.apply(applyThis, applyArgs);
|
|
11451
|
+
}
|
|
11452
|
+
runGuarded(fn, applyThis, applyArgs) {
|
|
11453
|
+
return fn.apply(applyThis, applyArgs);
|
|
11454
|
+
}
|
|
11455
|
+
runOutsideAngular(fn) {
|
|
11456
|
+
return fn();
|
|
11457
|
+
}
|
|
11458
|
+
runTask(fn, applyThis, applyArgs, name) {
|
|
11459
|
+
return fn.apply(applyThis, applyArgs);
|
|
11460
|
+
}
|
|
11461
|
+
}
|
|
10963
11462
|
/**
|
|
10964
|
-
*
|
|
10965
|
-
* The scope of this destruction depends on where `DestroyRef` is injected. If `DestroyRef`
|
|
10966
|
-
* is injected in a component or directive, the callbacks run when that component or
|
|
10967
|
-
* directive is destroyed. Otherwise the callbacks run when a corresponding injector is destroyed.
|
|
11463
|
+
* Token used to drive ApplicationRef.isStable
|
|
10968
11464
|
*
|
|
10969
|
-
*
|
|
11465
|
+
* TODO: This should be moved entirely to NgZone (as a breaking change) so it can be tree-shakeable
|
|
11466
|
+
* for `NoopNgZone` which is always just an `Observable` of `true`. Additionally, we should consider
|
|
11467
|
+
* whether the property on `NgZone` should be `Observable` or `Signal`.
|
|
10970
11468
|
*/
|
|
10971
|
-
|
|
10972
|
-
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
|
|
10976
|
-
|
|
10977
|
-
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
10981
|
-
|
|
11469
|
+
const ZONE_IS_STABLE_OBSERVABLE = new InjectionToken(ngDevMode ? 'isStable Observable' : '', {
|
|
11470
|
+
providedIn: 'root',
|
|
11471
|
+
// TODO(atscott): Replace this with a suitable default like `new
|
|
11472
|
+
// BehaviorSubject(true).asObservable`. Again, long term this won't exist on ApplicationRef at
|
|
11473
|
+
// all but until we can remove it, we need a default value zoneless.
|
|
11474
|
+
factory: isStableFactory,
|
|
11475
|
+
});
|
|
11476
|
+
function isStableFactory() {
|
|
11477
|
+
const zone = inject$1(NgZone);
|
|
11478
|
+
let _stable = true;
|
|
11479
|
+
const isCurrentlyStable = new Observable((observer) => {
|
|
11480
|
+
_stable = zone.isStable && !zone.hasPendingMacrotasks && !zone.hasPendingMicrotasks;
|
|
11481
|
+
zone.runOutsideAngular(() => {
|
|
11482
|
+
observer.next(_stable);
|
|
11483
|
+
observer.complete();
|
|
11484
|
+
});
|
|
11485
|
+
});
|
|
11486
|
+
const isStable = new Observable((observer) => {
|
|
11487
|
+
// Create the subscription to onStable outside the Angular Zone so that
|
|
11488
|
+
// the callback is run outside the Angular Zone.
|
|
11489
|
+
let stableSub;
|
|
11490
|
+
zone.runOutsideAngular(() => {
|
|
11491
|
+
stableSub = zone.onStable.subscribe(() => {
|
|
11492
|
+
NgZone.assertNotInAngularZone();
|
|
11493
|
+
// Check whether there are no pending macro/micro tasks in the next tick
|
|
11494
|
+
// to allow for NgZone to update the state.
|
|
11495
|
+
queueMicrotask(() => {
|
|
11496
|
+
if (!_stable && !zone.hasPendingMacrotasks && !zone.hasPendingMicrotasks) {
|
|
11497
|
+
_stable = true;
|
|
11498
|
+
observer.next(true);
|
|
11499
|
+
}
|
|
11500
|
+
});
|
|
11501
|
+
});
|
|
11502
|
+
});
|
|
11503
|
+
const unstableSub = zone.onUnstable.subscribe(() => {
|
|
11504
|
+
NgZone.assertInAngularZone();
|
|
11505
|
+
if (_stable) {
|
|
11506
|
+
_stable = false;
|
|
11507
|
+
zone.runOutsideAngular(() => {
|
|
11508
|
+
observer.next(false);
|
|
11509
|
+
});
|
|
11510
|
+
}
|
|
11511
|
+
});
|
|
11512
|
+
return () => {
|
|
11513
|
+
stableSub.unsubscribe();
|
|
11514
|
+
unstableSub.unsubscribe();
|
|
11515
|
+
};
|
|
11516
|
+
});
|
|
11517
|
+
return merge$1(isCurrentlyStable, isStable.pipe(share()));
|
|
10982
11518
|
}
|
|
10983
|
-
|
|
10984
|
-
|
|
10985
|
-
|
|
10986
|
-
this._lView = _lView;
|
|
11519
|
+
function shouldBeIgnoredByZone(applyArgs) {
|
|
11520
|
+
if (!Array.isArray(applyArgs)) {
|
|
11521
|
+
return false;
|
|
10987
11522
|
}
|
|
10988
|
-
|
|
10989
|
-
|
|
10990
|
-
|
|
11523
|
+
// We should only ever get 1 arg passed through to invokeTask.
|
|
11524
|
+
// Short circuit here incase that behavior changes.
|
|
11525
|
+
if (applyArgs.length !== 1) {
|
|
11526
|
+
return false;
|
|
10991
11527
|
}
|
|
11528
|
+
// Prevent triggering change detection when the __ignore_ng_zone__ flag is detected.
|
|
11529
|
+
return applyArgs[0].data?.['__ignore_ng_zone__'] === true;
|
|
10992
11530
|
}
|
|
10993
|
-
|
|
10994
|
-
|
|
10995
|
-
}
|
|
11531
|
+
|
|
11532
|
+
// Public API for Zone
|
|
10996
11533
|
|
|
10997
11534
|
/**
|
|
10998
11535
|
* Register a callback to be invoked each time the application
|
|
@@ -11044,7 +11581,8 @@ function afterRender(callback, options) {
|
|
|
11044
11581
|
let destroy;
|
|
11045
11582
|
const unregisterFn = injector.get(DestroyRef).onDestroy(() => destroy?.());
|
|
11046
11583
|
const manager = injector.get(AfterRenderEventManager);
|
|
11047
|
-
const
|
|
11584
|
+
const ngZone = injector.get(NgZone);
|
|
11585
|
+
const instance = new AfterRenderCallback(() => ngZone.runOutsideAngular(callback));
|
|
11048
11586
|
destroy = () => {
|
|
11049
11587
|
manager.unregister(instance);
|
|
11050
11588
|
unregisterFn();
|
|
@@ -11103,9 +11641,10 @@ function afterNextRender(callback, options) {
|
|
|
11103
11641
|
let destroy;
|
|
11104
11642
|
const unregisterFn = injector.get(DestroyRef).onDestroy(() => destroy?.());
|
|
11105
11643
|
const manager = injector.get(AfterRenderEventManager);
|
|
11644
|
+
const ngZone = injector.get(NgZone);
|
|
11106
11645
|
const instance = new AfterRenderCallback(() => {
|
|
11107
11646
|
destroy?.();
|
|
11108
|
-
callback
|
|
11647
|
+
ngZone.runOutsideAngular(callback);
|
|
11109
11648
|
});
|
|
11110
11649
|
destroy = () => {
|
|
11111
11650
|
manager.unregister(instance);
|
|
@@ -11419,48 +11958,11 @@ function getExpressionChangedErrorDetails(lView, bindingIndex, oldValue, newValu
|
|
|
11419
11958
|
return { propName: undefined, oldValue, newValue };
|
|
11420
11959
|
}
|
|
11421
11960
|
|
|
11422
|
-
class ReactiveLViewConsumer extends ReactiveNode {
|
|
11423
|
-
constructor() {
|
|
11424
|
-
super(...arguments);
|
|
11425
|
-
this.consumerAllowSignalWrites = false;
|
|
11426
|
-
this._lView = null;
|
|
11427
|
-
}
|
|
11428
|
-
set lView(lView) {
|
|
11429
|
-
(typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
11430
|
-
assertEqual(this._lView, null, 'Consumer already associated with a view.');
|
|
11431
|
-
this._lView = lView;
|
|
11432
|
-
}
|
|
11433
|
-
onConsumerDependencyMayHaveChanged() {
|
|
11434
|
-
(typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
11435
|
-
assertDefined(this._lView, 'Updating a signal during template or host binding execution is not allowed.');
|
|
11436
|
-
markViewDirty(this._lView);
|
|
11437
|
-
}
|
|
11438
|
-
onProducerUpdateValueVersion() {
|
|
11439
|
-
// This type doesn't implement the producer side of a `ReactiveNode`.
|
|
11440
|
-
}
|
|
11441
|
-
get hasReadASignal() {
|
|
11442
|
-
return this.hasProducers;
|
|
11443
|
-
}
|
|
11444
|
-
runInContext(fn, rf, ctx) {
|
|
11445
|
-
const prevConsumer = setActiveConsumer(this);
|
|
11446
|
-
this.trackingVersion++;
|
|
11447
|
-
try {
|
|
11448
|
-
fn(rf, ctx);
|
|
11449
|
-
}
|
|
11450
|
-
finally {
|
|
11451
|
-
setActiveConsumer(prevConsumer);
|
|
11452
|
-
}
|
|
11453
|
-
}
|
|
11454
|
-
destroy() {
|
|
11455
|
-
// Incrementing the version means that every producer which tries to update this consumer will
|
|
11456
|
-
// consider its record stale, and not notify.
|
|
11457
|
-
this.trackingVersion++;
|
|
11458
|
-
}
|
|
11459
|
-
}
|
|
11460
11961
|
let currentConsumer = null;
|
|
11461
|
-
function
|
|
11462
|
-
|
|
11463
|
-
|
|
11962
|
+
function setLViewForConsumer(node, lView) {
|
|
11963
|
+
(typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
11964
|
+
assertEqual(node.lView, null, 'Consumer already associated with a view.');
|
|
11965
|
+
node.lView = lView;
|
|
11464
11966
|
}
|
|
11465
11967
|
/**
|
|
11466
11968
|
* Create a new template consumer pointing at the specified LView.
|
|
@@ -11482,12 +11984,29 @@ function getReactiveLViewConsumer(lView, slot) {
|
|
|
11482
11984
|
*/
|
|
11483
11985
|
function commitLViewConsumerIfHasProducers(lView, slot) {
|
|
11484
11986
|
const consumer = getOrCreateCurrentLViewConsumer();
|
|
11485
|
-
if (!consumer.
|
|
11987
|
+
if (!consumer.producerNode?.length) {
|
|
11486
11988
|
return;
|
|
11487
11989
|
}
|
|
11488
11990
|
lView[slot] = currentConsumer;
|
|
11489
11991
|
consumer.lView = lView;
|
|
11490
|
-
currentConsumer =
|
|
11992
|
+
currentConsumer = createLViewConsumer();
|
|
11993
|
+
}
|
|
11994
|
+
const REACTIVE_LVIEW_CONSUMER_NODE = {
|
|
11995
|
+
...REACTIVE_NODE,
|
|
11996
|
+
consumerIsAlwaysLive: true,
|
|
11997
|
+
consumerMarkedDirty: (node) => {
|
|
11998
|
+
(typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
11999
|
+
assertDefined(node.lView, 'Updating a signal during template or host binding execution is not allowed.');
|
|
12000
|
+
markViewDirty(node.lView);
|
|
12001
|
+
},
|
|
12002
|
+
lView: null,
|
|
12003
|
+
};
|
|
12004
|
+
function createLViewConsumer() {
|
|
12005
|
+
return Object.create(REACTIVE_LVIEW_CONSUMER_NODE);
|
|
12006
|
+
}
|
|
12007
|
+
function getOrCreateCurrentLViewConsumer() {
|
|
12008
|
+
currentConsumer ??= createLViewConsumer();
|
|
12009
|
+
return currentConsumer;
|
|
11491
12010
|
}
|
|
11492
12011
|
|
|
11493
12012
|
/** A special value which designates that a value has not changed. */
|
|
@@ -11604,8 +12123,15 @@ function processHostBindingOpCodes(tView, lView) {
|
|
|
11604
12123
|
const bindingRootIndx = hostBindingOpCodes[++i];
|
|
11605
12124
|
const hostBindingFn = hostBindingOpCodes[++i];
|
|
11606
12125
|
setBindingRootForHostBindings(bindingRootIndx, directiveIdx);
|
|
11607
|
-
|
|
11608
|
-
|
|
12126
|
+
consumer.dirty = false;
|
|
12127
|
+
const prevConsumer = consumerBeforeComputation(consumer);
|
|
12128
|
+
try {
|
|
12129
|
+
const context = lView[directiveIdx];
|
|
12130
|
+
hostBindingFn(2 /* RenderFlags.Update */, context);
|
|
12131
|
+
}
|
|
12132
|
+
finally {
|
|
12133
|
+
consumerAfterComputation(consumer, prevConsumer);
|
|
12134
|
+
}
|
|
11609
12135
|
}
|
|
11610
12136
|
}
|
|
11611
12137
|
}
|
|
@@ -11745,17 +12271,16 @@ function executeTemplate(tView, lView, templateFn, rf, context) {
|
|
|
11745
12271
|
}
|
|
11746
12272
|
const preHookType = isUpdatePhase ? 2 /* ProfilerEvent.TemplateUpdateStart */ : 0 /* ProfilerEvent.TemplateCreateStart */;
|
|
11747
12273
|
profiler(preHookType, context);
|
|
11748
|
-
|
|
11749
|
-
|
|
11750
|
-
|
|
11751
|
-
|
|
11752
|
-
|
|
11753
|
-
try {
|
|
11754
|
-
templateFn(rf, context);
|
|
11755
|
-
}
|
|
11756
|
-
finally {
|
|
11757
|
-
setActiveConsumer(prevConsumer);
|
|
12274
|
+
const effectiveConsumer = isUpdatePhase ? consumer : null;
|
|
12275
|
+
const prevConsumer = consumerBeforeComputation(effectiveConsumer);
|
|
12276
|
+
try {
|
|
12277
|
+
if (effectiveConsumer !== null) {
|
|
12278
|
+
effectiveConsumer.dirty = false;
|
|
11758
12279
|
}
|
|
12280
|
+
templateFn(rf, context);
|
|
12281
|
+
}
|
|
12282
|
+
finally {
|
|
12283
|
+
consumerAfterComputation(effectiveConsumer, prevConsumer);
|
|
11759
12284
|
}
|
|
11760
12285
|
}
|
|
11761
12286
|
finally {
|
|
@@ -13009,21 +13534,21 @@ class EffectManager {
|
|
|
13009
13534
|
}
|
|
13010
13535
|
create(effectFn, destroyRef, allowSignalWrites) {
|
|
13011
13536
|
const zone = (typeof Zone === 'undefined') ? null : Zone.current;
|
|
13012
|
-
const
|
|
13537
|
+
const w = watch(effectFn, (watch) => {
|
|
13013
13538
|
if (!this.all.has(watch)) {
|
|
13014
13539
|
return;
|
|
13015
13540
|
}
|
|
13016
13541
|
this.queue.set(watch, zone);
|
|
13017
13542
|
}, allowSignalWrites);
|
|
13018
|
-
this.all.add(
|
|
13543
|
+
this.all.add(w);
|
|
13019
13544
|
// Effects start dirty.
|
|
13020
|
-
|
|
13545
|
+
w.notify();
|
|
13021
13546
|
let unregisterOnDestroy;
|
|
13022
13547
|
const destroy = () => {
|
|
13023
|
-
|
|
13548
|
+
w.cleanup();
|
|
13024
13549
|
unregisterOnDestroy?.();
|
|
13025
|
-
this.all.delete(
|
|
13026
|
-
this.queue.delete(
|
|
13550
|
+
this.all.delete(w);
|
|
13551
|
+
this.queue.delete(w);
|
|
13027
13552
|
};
|
|
13028
13553
|
unregisterOnDestroy = destroyRef?.onDestroy(destroy);
|
|
13029
13554
|
return {
|
|
@@ -13116,28 +13641,7 @@ function collectNativeNodes(tView, lView, tNode, result, isProjection = false) {
|
|
|
13116
13641
|
// ViewContainerRef). When we find a LContainer we need to descend into it to collect root nodes
|
|
13117
13642
|
// from the views in this container.
|
|
13118
13643
|
if (isLContainer(lNode)) {
|
|
13119
|
-
|
|
13120
|
-
const lViewInAContainer = lNode[i];
|
|
13121
|
-
const lViewFirstChildTNode = lViewInAContainer[TVIEW].firstChild;
|
|
13122
|
-
if (lViewFirstChildTNode !== null) {
|
|
13123
|
-
collectNativeNodes(lViewInAContainer[TVIEW], lViewInAContainer, lViewFirstChildTNode, result);
|
|
13124
|
-
}
|
|
13125
|
-
}
|
|
13126
|
-
// When an LContainer is created, the anchor (comment) node is:
|
|
13127
|
-
// - (1) either reused in case of an ElementContainer (<ng-container>)
|
|
13128
|
-
// - (2) or a new comment node is created
|
|
13129
|
-
// In the first case, the anchor comment node would be added to the final
|
|
13130
|
-
// list by the code above (`result.push(unwrapRNode(lNode))`), but the second
|
|
13131
|
-
// case requires extra handling: the anchor node needs to be added to the
|
|
13132
|
-
// final list manually. See additional information in the `createAnchorNode`
|
|
13133
|
-
// function in the `view_container_ref.ts`.
|
|
13134
|
-
//
|
|
13135
|
-
// In the first case, the same reference would be stored in the `NATIVE`
|
|
13136
|
-
// and `HOST` slots in an LContainer. Otherwise, this is the second case and
|
|
13137
|
-
// we should add an element to the final list.
|
|
13138
|
-
if (lNode[NATIVE] !== lNode[HOST]) {
|
|
13139
|
-
result.push(lNode[NATIVE]);
|
|
13140
|
-
}
|
|
13644
|
+
collectNativeNodesInLContainer(lNode, result);
|
|
13141
13645
|
}
|
|
13142
13646
|
const tNodeType = tNode.type;
|
|
13143
13647
|
if (tNodeType & 8 /* TNodeType.ElementContainer */) {
|
|
@@ -13165,6 +13669,34 @@ function collectNativeNodes(tView, lView, tNode, result, isProjection = false) {
|
|
|
13165
13669
|
}
|
|
13166
13670
|
return result;
|
|
13167
13671
|
}
|
|
13672
|
+
/**
|
|
13673
|
+
* Collects all root nodes in all views in a given LContainer.
|
|
13674
|
+
*/
|
|
13675
|
+
function collectNativeNodesInLContainer(lContainer, result) {
|
|
13676
|
+
for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
|
|
13677
|
+
const lViewInAContainer = lContainer[i];
|
|
13678
|
+
const lViewFirstChildTNode = lViewInAContainer[TVIEW].firstChild;
|
|
13679
|
+
if (lViewFirstChildTNode !== null) {
|
|
13680
|
+
collectNativeNodes(lViewInAContainer[TVIEW], lViewInAContainer, lViewFirstChildTNode, result);
|
|
13681
|
+
}
|
|
13682
|
+
}
|
|
13683
|
+
// When an LContainer is created, the anchor (comment) node is:
|
|
13684
|
+
// - (1) either reused in case of an ElementContainer (<ng-container>)
|
|
13685
|
+
// - (2) or a new comment node is created
|
|
13686
|
+
// In the first case, the anchor comment node would be added to the final
|
|
13687
|
+
// list by the code in the `collectNativeNodes` function
|
|
13688
|
+
// (see the `result.push(unwrapRNode(lNode))` line), but the second
|
|
13689
|
+
// case requires extra handling: the anchor node needs to be added to the
|
|
13690
|
+
// final list manually. See additional information in the `createAnchorNode`
|
|
13691
|
+
// function in the `view_container_ref.ts`.
|
|
13692
|
+
//
|
|
13693
|
+
// In the first case, the same reference would be stored in the `NATIVE`
|
|
13694
|
+
// and `HOST` slots in an LContainer. Otherwise, this is the second case and
|
|
13695
|
+
// we should add an element to the final list.
|
|
13696
|
+
if (lContainer[NATIVE] !== lContainer[HOST]) {
|
|
13697
|
+
result.push(lContainer[NATIVE]);
|
|
13698
|
+
}
|
|
13699
|
+
}
|
|
13168
13700
|
|
|
13169
13701
|
function detectChangesInternal(tView, lView, context, notifyErrorHandler = true) {
|
|
13170
13702
|
const environment = lView[ENVIRONMENT];
|
|
@@ -13400,15 +13932,15 @@ function detectChangesInView(lView, mode) {
|
|
|
13400
13932
|
return;
|
|
13401
13933
|
}
|
|
13402
13934
|
const tView = lView[TVIEW];
|
|
13403
|
-
|
|
13935
|
+
const flags = lView[FLAGS];
|
|
13936
|
+
if ((flags & (16 /* LViewFlags.CheckAlways */ | 64 /* LViewFlags.Dirty */) &&
|
|
13404
13937
|
mode === 0 /* ChangeDetectionMode.Global */) ||
|
|
13405
|
-
|
|
13938
|
+
flags & 1024 /* LViewFlags.RefreshView */ ||
|
|
13406
13939
|
mode === 2 /* ChangeDetectionMode.BugToForceRefreshAndIgnoreViewFlags */) {
|
|
13407
13940
|
refreshView(tView, lView, tView.template, lView[CONTEXT]);
|
|
13408
13941
|
}
|
|
13409
13942
|
else if (lView[DESCENDANT_VIEWS_TO_REFRESH] > 0) {
|
|
13410
13943
|
detectChangesInEmbeddedViews(lView, 1 /* ChangeDetectionMode.Targeted */);
|
|
13411
|
-
const tView = lView[TVIEW];
|
|
13412
13944
|
const components = tView.components;
|
|
13413
13945
|
if (components !== null) {
|
|
13414
13946
|
detectChangesInChildComponents(lView, components, 1 /* ChangeDetectionMode.Targeted */);
|
|
@@ -17184,14 +17716,12 @@ function getTStylingRangePrev(tStylingRange) {
|
|
|
17184
17716
|
}
|
|
17185
17717
|
function getTStylingRangePrevDuplicate(tStylingRange) {
|
|
17186
17718
|
ngDevMode && assertNumber(tStylingRange, 'expected number');
|
|
17187
|
-
return (tStylingRange & 2 /* StylingRange.PREV_DUPLICATE */) ==
|
|
17188
|
-
2 /* StylingRange.PREV_DUPLICATE */;
|
|
17719
|
+
return (tStylingRange & 2 /* StylingRange.PREV_DUPLICATE */) == 2 /* StylingRange.PREV_DUPLICATE */;
|
|
17189
17720
|
}
|
|
17190
17721
|
function setTStylingRangePrev(tStylingRange, previous) {
|
|
17191
17722
|
ngDevMode && assertNumber(tStylingRange, 'expected number');
|
|
17192
17723
|
ngDevMode && assertNumberInRange(previous, 0, 32767 /* StylingRange.UNSIGNED_MASK */);
|
|
17193
|
-
return ((tStylingRange & ~4294836224 /* StylingRange.PREV_MASK */) |
|
|
17194
|
-
(previous << 17 /* StylingRange.PREV_SHIFT */));
|
|
17724
|
+
return ((tStylingRange & ~4294836224 /* StylingRange.PREV_MASK */) | (previous << 17 /* StylingRange.PREV_SHIFT */));
|
|
17195
17725
|
}
|
|
17196
17726
|
function setTStylingRangePrevDuplicate(tStylingRange) {
|
|
17197
17727
|
ngDevMode && assertNumber(tStylingRange, 'expected number');
|
|
@@ -17209,8 +17739,7 @@ function setTStylingRangeNext(tStylingRange, next) {
|
|
|
17209
17739
|
}
|
|
17210
17740
|
function getTStylingRangeNextDuplicate(tStylingRange) {
|
|
17211
17741
|
ngDevMode && assertNumber(tStylingRange, 'expected number');
|
|
17212
|
-
return (tStylingRange & 1 /* StylingRange.NEXT_DUPLICATE */) ===
|
|
17213
|
-
1 /* StylingRange.NEXT_DUPLICATE */;
|
|
17742
|
+
return ((tStylingRange) & 1 /* StylingRange.NEXT_DUPLICATE */) === 1 /* StylingRange.NEXT_DUPLICATE */;
|
|
17214
17743
|
}
|
|
17215
17744
|
function setTStylingRangeNextDuplicate(tStylingRange) {
|
|
17216
17745
|
ngDevMode && assertNumber(tStylingRange, 'expected number');
|
|
@@ -23551,51 +24080,6 @@ function isPure(lView, index) {
|
|
|
23551
24080
|
return lView[TVIEW].data[index].pure;
|
|
23552
24081
|
}
|
|
23553
24082
|
|
|
23554
|
-
/// <reference types="rxjs" />
|
|
23555
|
-
class EventEmitter_ extends Subject {
|
|
23556
|
-
constructor(isAsync = false) {
|
|
23557
|
-
super();
|
|
23558
|
-
this.__isAsync = isAsync;
|
|
23559
|
-
}
|
|
23560
|
-
emit(value) {
|
|
23561
|
-
super.next(value);
|
|
23562
|
-
}
|
|
23563
|
-
subscribe(observerOrNext, error, complete) {
|
|
23564
|
-
let nextFn = observerOrNext;
|
|
23565
|
-
let errorFn = error || (() => null);
|
|
23566
|
-
let completeFn = complete;
|
|
23567
|
-
if (observerOrNext && typeof observerOrNext === 'object') {
|
|
23568
|
-
const observer = observerOrNext;
|
|
23569
|
-
nextFn = observer.next?.bind(observer);
|
|
23570
|
-
errorFn = observer.error?.bind(observer);
|
|
23571
|
-
completeFn = observer.complete?.bind(observer);
|
|
23572
|
-
}
|
|
23573
|
-
if (this.__isAsync) {
|
|
23574
|
-
errorFn = _wrapInTimeout(errorFn);
|
|
23575
|
-
if (nextFn) {
|
|
23576
|
-
nextFn = _wrapInTimeout(nextFn);
|
|
23577
|
-
}
|
|
23578
|
-
if (completeFn) {
|
|
23579
|
-
completeFn = _wrapInTimeout(completeFn);
|
|
23580
|
-
}
|
|
23581
|
-
}
|
|
23582
|
-
const sink = super.subscribe({ next: nextFn, error: errorFn, complete: completeFn });
|
|
23583
|
-
if (observerOrNext instanceof Subscription) {
|
|
23584
|
-
observerOrNext.add(sink);
|
|
23585
|
-
}
|
|
23586
|
-
return sink;
|
|
23587
|
-
}
|
|
23588
|
-
}
|
|
23589
|
-
function _wrapInTimeout(fn) {
|
|
23590
|
-
return (value) => {
|
|
23591
|
-
setTimeout(fn, undefined, value);
|
|
23592
|
-
};
|
|
23593
|
-
}
|
|
23594
|
-
/**
|
|
23595
|
-
* @publicApi
|
|
23596
|
-
*/
|
|
23597
|
-
const EventEmitter = EventEmitter_;
|
|
23598
|
-
|
|
23599
24083
|
function symbolIterator() {
|
|
23600
24084
|
// @ts-expect-error accessing a private member
|
|
23601
24085
|
return this._results[Symbol.iterator]();
|
|
@@ -24053,6 +24537,40 @@ function findMatchingDehydratedView(lContainer, template) {
|
|
|
24053
24537
|
* A view container instance can contain other view containers,
|
|
24054
24538
|
* creating a [view hierarchy](guide/glossary#view-hierarchy).
|
|
24055
24539
|
*
|
|
24540
|
+
* @usageNotes
|
|
24541
|
+
*
|
|
24542
|
+
* The example below demonstrates how the `createComponent` function can be used
|
|
24543
|
+
* to create an instance of a ComponentRef dynamically and attach it to an ApplicationRef,
|
|
24544
|
+
* so that it gets included into change detection cycles.
|
|
24545
|
+
*
|
|
24546
|
+
* Note: the example uses standalone components, but the function can also be used for
|
|
24547
|
+
* non-standalone components (declared in an NgModule) as well.
|
|
24548
|
+
*
|
|
24549
|
+
* ```typescript
|
|
24550
|
+
* @Component({
|
|
24551
|
+
* standalone: true,
|
|
24552
|
+
* selector: 'dynamic',
|
|
24553
|
+
* template: `<span>This is a content of a dynamic component.</span>`,
|
|
24554
|
+
* })
|
|
24555
|
+
* class DynamicComponent {
|
|
24556
|
+
* vcr = inject(ViewContainerRef);
|
|
24557
|
+
* }
|
|
24558
|
+
*
|
|
24559
|
+
* @Component({
|
|
24560
|
+
* standalone: true,
|
|
24561
|
+
* selector: 'app',
|
|
24562
|
+
* template: `<main>Hi! This is the main content.</main>`,
|
|
24563
|
+
* })
|
|
24564
|
+
* class AppComponent {
|
|
24565
|
+
* vcr = inject(ViewContainerRef);
|
|
24566
|
+
*
|
|
24567
|
+
* ngAfterViewInit() {
|
|
24568
|
+
* const compRef = this.vcr.createComponent(DynamicComponent);
|
|
24569
|
+
* compRef.changeDetectorRef.detectChanges();
|
|
24570
|
+
* }
|
|
24571
|
+
* }
|
|
24572
|
+
* ```
|
|
24573
|
+
*
|
|
24056
24574
|
* @see {@link ComponentRef}
|
|
24057
24575
|
* @see {@link EmbeddedViewRef}
|
|
24058
24576
|
*
|
|
@@ -26899,7 +27417,7 @@ class TestBedImpl {
|
|
|
26899
27417
|
}
|
|
26900
27418
|
const noNgZone = this.inject(ComponentFixtureNoNgZone, false);
|
|
26901
27419
|
const autoDetect = this.inject(ComponentFixtureAutoDetect, false);
|
|
26902
|
-
const ngZone = noNgZone ? null : this.inject(NgZone, null);
|
|
27420
|
+
const ngZone = noNgZone ? null : this.inject(NgZone$1, null);
|
|
26903
27421
|
const componentFactory = new ɵRender3ComponentFactory(componentDef);
|
|
26904
27422
|
const initComponent = () => {
|
|
26905
27423
|
const componentRef = componentFactory.create(Injector$1.NULL, [], `#${rootElId}`, this.testModuleRef);
|