@angular/core 16.2.3 → 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/linker/view_container_ref.mjs +35 -1
- 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/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 +476 -483
- 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 +474 -482
- package/fesm2022/testing.mjs.map +1 -1
- package/index.d.ts +112 -102
- 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,5 +1,5 @@
|
|
|
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
|
*/
|
|
@@ -3565,15 +3565,6 @@ const SIGNAL = Symbol('SIGNAL');
|
|
|
3565
3565
|
function isSignal(value) {
|
|
3566
3566
|
return typeof value === 'function' && value[SIGNAL] !== undefined;
|
|
3567
3567
|
}
|
|
3568
|
-
/**
|
|
3569
|
-
* Converts `fn` into a marked signal function (where `isSignal(fn)` will be `true`), and
|
|
3570
|
-
* potentially add some set of extra properties (passed as an object record `extraApi`).
|
|
3571
|
-
*/
|
|
3572
|
-
function createSignalFromFunction(node, fn, extraApi = {}) {
|
|
3573
|
-
fn[SIGNAL] = node;
|
|
3574
|
-
// Copy properties from `extraApi` to `fn` to complete the desired API of the `Signal`.
|
|
3575
|
-
return Object.assign(fn, extraApi);
|
|
3576
|
-
}
|
|
3577
3568
|
/**
|
|
3578
3569
|
* The default equality function used for `signal` and `computed`, which treats objects and arrays
|
|
3579
3570
|
* as never equal, and all other primitive values using identity semantics.
|
|
@@ -3594,217 +3585,266 @@ function defaultEquals(a, b) {
|
|
|
3594
3585
|
|
|
3595
3586
|
// Required as the signals library is in a separate package, so we need to explicitly ensure the
|
|
3596
3587
|
/**
|
|
3597
|
-
*
|
|
3598
|
-
*
|
|
3588
|
+
* The currently active consumer `ReactiveNode`, if running code in a reactive context.
|
|
3589
|
+
*
|
|
3590
|
+
* Change this via `setActiveConsumer`.
|
|
3591
|
+
*/
|
|
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.
|
|
3599
3616
|
*/
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3617
|
+
function producerAccessed(node) {
|
|
3618
|
+
if (inNotificationPhase) {
|
|
3619
|
+
throw new Error(typeof ngDevMode !== 'undefined' && ngDevMode ?
|
|
3620
|
+
`Assertion error: signal read during notification phase` :
|
|
3621
|
+
'');
|
|
3603
3622
|
}
|
|
3604
|
-
|
|
3605
|
-
|
|
3623
|
+
if (activeConsumer === null) {
|
|
3624
|
+
// Accessed outside of a reactive context, so nothing to record.
|
|
3625
|
+
return;
|
|
3606
3626
|
}
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
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
|
+
}
|
|
3615
3644
|
}
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
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;
|
|
3621
3654
|
}
|
|
3622
|
-
|
|
3623
|
-
// Required as the signals library is in a separate package, so we need to explicitly ensure the
|
|
3624
3655
|
/**
|
|
3625
|
-
*
|
|
3656
|
+
* Ensure this producer's `version` is up-to-date.
|
|
3626
3657
|
*/
|
|
3627
|
-
|
|
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
|
+
}
|
|
3628
3674
|
/**
|
|
3629
|
-
*
|
|
3630
|
-
* consumer).
|
|
3675
|
+
* Propagate a dirty notification to live consumers of this producer.
|
|
3631
3676
|
*/
|
|
3632
|
-
|
|
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
|
+
}
|
|
3633
3695
|
/**
|
|
3634
|
-
* Whether
|
|
3696
|
+
* Whether this `ReactiveNode` in its producer capacity is currently allowed to initiate updates,
|
|
3697
|
+
* based on the current consumer context.
|
|
3635
3698
|
*/
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3699
|
+
function producerUpdatesAllowed() {
|
|
3700
|
+
return activeConsumer?.consumerAllowSignalWrites !== false;
|
|
3701
|
+
}
|
|
3702
|
+
function consumerMarkDirty(node) {
|
|
3703
|
+
node.dirty = true;
|
|
3704
|
+
producerNotifyConsumers(node);
|
|
3705
|
+
node.consumerMarkedDirty?.(node);
|
|
3641
3706
|
}
|
|
3642
3707
|
/**
|
|
3643
|
-
*
|
|
3644
|
-
*
|
|
3645
|
-
* Nodes can be producers of reactive values, consumers of other reactive values, or both.
|
|
3646
|
-
*
|
|
3647
|
-
* Producers are nodes that produce values, and can be depended upon by consumer nodes.
|
|
3648
|
-
*
|
|
3649
|
-
* Producers expose a monotonic `valueVersion` counter, and are responsible for incrementing this
|
|
3650
|
-
* version when their value semantically changes. Some producers may produce their values lazily and
|
|
3651
|
-
* thus at times need to be polled for potential updates to their value (and by extension their
|
|
3652
|
-
* `valueVersion`). This is accomplished via the `onProducerUpdateValueVersion` method for
|
|
3653
|
-
* implemented by producers, which should perform whatever calculations are necessary to ensure
|
|
3654
|
-
* `valueVersion` is up to date.
|
|
3655
|
-
*
|
|
3656
|
-
* Consumers are nodes that depend on the values of producers and are notified when those values
|
|
3657
|
-
* might have changed.
|
|
3708
|
+
* Prepare this consumer to run a computation in its reactive context.
|
|
3658
3709
|
*
|
|
3659
|
-
*
|
|
3660
|
-
*
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
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.
|
|
3666
3719
|
*
|
|
3667
|
-
*
|
|
3668
|
-
*
|
|
3669
|
-
* comparing the consumer's `trackingVersion` to the version at which the dependency was
|
|
3670
|
-
* last observed.
|
|
3720
|
+
* Must be called by subclasses which represent reactive computations, after those computations
|
|
3721
|
+
* have finished.
|
|
3671
3722
|
*/
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
*/
|
|
3678
|
-
this.ref = newWeakRef(this);
|
|
3679
|
-
/**
|
|
3680
|
-
* Edges to producers on which this node depends (in its consumer capacity).
|
|
3681
|
-
*/
|
|
3682
|
-
this.producers = new Map();
|
|
3683
|
-
/**
|
|
3684
|
-
* Edges to consumers on which this node depends (in its producer capacity).
|
|
3685
|
-
*/
|
|
3686
|
-
this.consumers = new Map();
|
|
3687
|
-
/**
|
|
3688
|
-
* Monotonically increasing counter representing a version of this `Consumer`'s
|
|
3689
|
-
* dependencies.
|
|
3690
|
-
*/
|
|
3691
|
-
this.trackingVersion = 0;
|
|
3692
|
-
/**
|
|
3693
|
-
* Monotonically increasing counter which increases when the value of this `Producer`
|
|
3694
|
-
* semantically changes.
|
|
3695
|
-
*/
|
|
3696
|
-
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;
|
|
3697
3728
|
}
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
* rerun any reactions.
|
|
3704
|
-
*/
|
|
3705
|
-
consumerPollProducersForChange() {
|
|
3706
|
-
for (const [producerId, edge] of this.producers) {
|
|
3707
|
-
const producer = edge.producerNode.deref();
|
|
3708
|
-
// On Safari < 16.1 deref can return null, we need to check for null also.
|
|
3709
|
-
// See https://github.com/WebKit/WebKit/commit/44c15ba58912faab38b534fef909dd9e13e095e0
|
|
3710
|
-
if (producer == null || edge.atTrackingVersion !== this.trackingVersion) {
|
|
3711
|
-
// This dependency edge is stale, so remove it.
|
|
3712
|
-
this.producers.delete(producerId);
|
|
3713
|
-
producer?.consumers.delete(this.id);
|
|
3714
|
-
continue;
|
|
3715
|
-
}
|
|
3716
|
-
if (producer.producerPollStatus(edge.seenValueVersion)) {
|
|
3717
|
-
// One of the dependencies reports a real value change.
|
|
3718
|
-
return true;
|
|
3719
|
-
}
|
|
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]);
|
|
3720
3734
|
}
|
|
3721
|
-
// No dependency reported a real value change, so the `Consumer` has also not been
|
|
3722
|
-
// impacted.
|
|
3723
|
-
return false;
|
|
3724
3735
|
}
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
const prev = inNotificationPhase;
|
|
3731
|
-
inNotificationPhase = true;
|
|
3732
|
-
try {
|
|
3733
|
-
for (const [consumerId, edge] of this.consumers) {
|
|
3734
|
-
const consumer = edge.consumerNode.deref();
|
|
3735
|
-
// On Safari < 16.1 deref can return null, we need to check for null also.
|
|
3736
|
-
// See https://github.com/WebKit/WebKit/commit/44c15ba58912faab38b534fef909dd9e13e095e0
|
|
3737
|
-
if (consumer == null || consumer.trackingVersion !== edge.atTrackingVersion) {
|
|
3738
|
-
this.consumers.delete(consumerId);
|
|
3739
|
-
consumer?.producers.delete(this.id);
|
|
3740
|
-
continue;
|
|
3741
|
-
}
|
|
3742
|
-
consumer.onConsumerDependencyMayHaveChanged();
|
|
3743
|
-
}
|
|
3744
|
-
}
|
|
3745
|
-
finally {
|
|
3746
|
-
inNotificationPhase = prev;
|
|
3747
|
-
}
|
|
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();
|
|
3748
3741
|
}
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
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;
|
|
3760
3757
|
}
|
|
3761
|
-
//
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
atTrackingVersion: activeConsumer.trackingVersion,
|
|
3769
|
-
};
|
|
3770
|
-
activeConsumer.producers.set(this.id, edge);
|
|
3771
|
-
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;
|
|
3772
3765
|
}
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
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]);
|
|
3776
3778
|
}
|
|
3777
3779
|
}
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
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;
|
|
3783
3785
|
}
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
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
|
+
}
|
|
3790
3801
|
}
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
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]);
|
|
3801
3817
|
}
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
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;
|
|
3806
3834
|
}
|
|
3807
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 ??= [];
|
|
3847
|
+
}
|
|
3808
3848
|
|
|
3809
3849
|
/**
|
|
3810
3850
|
* Create a computed `Signal` which derives a reactive value from an expression.
|
|
@@ -3812,10 +3852,21 @@ class ReactiveNode {
|
|
|
3812
3852
|
* @developerPreview
|
|
3813
3853
|
*/
|
|
3814
3854
|
function computed(computation, options) {
|
|
3815
|
-
const node =
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
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;
|
|
3819
3870
|
}
|
|
3820
3871
|
/**
|
|
3821
3872
|
* A dedicated symbol used before a computed value has been calculated for the first time.
|
|
@@ -3834,108 +3885,47 @@ const COMPUTING = Symbol('COMPUTING');
|
|
|
3834
3885
|
* Explicitly typed as `any` so we can use it as signal's value.
|
|
3835
3886
|
*/
|
|
3836
3887
|
const ERRORED = Symbol('ERRORED');
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
* This can also be one of the special values `UNSET`, `COMPUTING`, or `ERRORED`.
|
|
3851
|
-
*/
|
|
3852
|
-
this.value = UNSET;
|
|
3853
|
-
/**
|
|
3854
|
-
* If `value` is `ERRORED`, the error caught from the last computation attempt which will
|
|
3855
|
-
* be re-thrown.
|
|
3856
|
-
*/
|
|
3857
|
-
this.error = null;
|
|
3858
|
-
/**
|
|
3859
|
-
* Flag indicating that the computation is currently stale, meaning that one of the
|
|
3860
|
-
* dependencies has notified of a potential change.
|
|
3861
|
-
*
|
|
3862
|
-
* It's possible that no dependency has _actually_ changed, in which case the `stale`
|
|
3863
|
-
* state can be resolved without recomputing the value.
|
|
3864
|
-
*/
|
|
3865
|
-
this.stale = true;
|
|
3866
|
-
this.consumerAllowSignalWrites = false;
|
|
3867
|
-
}
|
|
3868
|
-
onConsumerDependencyMayHaveChanged() {
|
|
3869
|
-
if (this.stale) {
|
|
3870
|
-
// We've already notified consumers that this value has potentially changed.
|
|
3871
|
-
return;
|
|
3872
|
-
}
|
|
3873
|
-
// Record that the currently cached value may be stale.
|
|
3874
|
-
this.stale = true;
|
|
3875
|
-
// Notify any consumers about the potential change.
|
|
3876
|
-
this.producerMayHaveChanged();
|
|
3877
|
-
}
|
|
3878
|
-
onProducerUpdateValueVersion() {
|
|
3879
|
-
if (!this.stale) {
|
|
3880
|
-
// The current value and its version are already up to date.
|
|
3881
|
-
return;
|
|
3882
|
-
}
|
|
3883
|
-
// The current value is stale. Check whether we need to produce a new one.
|
|
3884
|
-
if (this.value !== UNSET && this.value !== COMPUTING &&
|
|
3885
|
-
!this.consumerPollProducersForChange()) {
|
|
3886
|
-
// Even though we were previously notified of a potential dependency update, all of
|
|
3887
|
-
// our dependencies report that they have not actually changed in value, so we can
|
|
3888
|
-
// resolve the stale state without needing to recompute the current value.
|
|
3889
|
-
this.stale = false;
|
|
3890
|
-
return;
|
|
3891
|
-
}
|
|
3892
|
-
// The current value is stale, and needs to be recomputed. It still may not change -
|
|
3893
|
-
// that depends on whether the newly computed value is equal to the old.
|
|
3894
|
-
this.recomputeValue();
|
|
3895
|
-
}
|
|
3896
|
-
recomputeValue() {
|
|
3897
|
-
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) {
|
|
3898
3901
|
// Our computation somehow led to a cyclic read of itself.
|
|
3899
3902
|
throw new Error('Detected cycle in computations.');
|
|
3900
3903
|
}
|
|
3901
|
-
const oldValue =
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
this.trackingVersion++;
|
|
3905
|
-
const prevConsumer = setActiveConsumer(this);
|
|
3904
|
+
const oldValue = node.value;
|
|
3905
|
+
node.value = COMPUTING;
|
|
3906
|
+
const prevConsumer = consumerBeforeComputation(node);
|
|
3906
3907
|
let newValue;
|
|
3907
3908
|
try {
|
|
3908
|
-
newValue =
|
|
3909
|
+
newValue = node.computation();
|
|
3909
3910
|
}
|
|
3910
3911
|
catch (err) {
|
|
3911
3912
|
newValue = ERRORED;
|
|
3912
|
-
|
|
3913
|
+
node.error = err;
|
|
3913
3914
|
}
|
|
3914
3915
|
finally {
|
|
3915
|
-
|
|
3916
|
+
consumerAfterComputation(node, prevConsumer);
|
|
3916
3917
|
}
|
|
3917
|
-
this.stale = false;
|
|
3918
3918
|
if (oldValue !== UNSET && oldValue !== ERRORED && newValue !== ERRORED &&
|
|
3919
|
-
|
|
3919
|
+
node.equal(oldValue, newValue)) {
|
|
3920
3920
|
// No change to `valueVersion` - old and new values are
|
|
3921
3921
|
// semantically equivalent.
|
|
3922
|
-
|
|
3922
|
+
node.value = oldValue;
|
|
3923
3923
|
return;
|
|
3924
3924
|
}
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
}
|
|
3928
|
-
|
|
3929
|
-
// Check if the value needs updating before returning it.
|
|
3930
|
-
this.onProducerUpdateValueVersion();
|
|
3931
|
-
// Record that someone looked at this signal.
|
|
3932
|
-
this.producerAccessed();
|
|
3933
|
-
if (this.value === ERRORED) {
|
|
3934
|
-
throw this.error;
|
|
3935
|
-
}
|
|
3936
|
-
return this.value;
|
|
3937
|
-
}
|
|
3938
|
-
}
|
|
3925
|
+
node.value = newValue;
|
|
3926
|
+
node.version++;
|
|
3927
|
+
},
|
|
3928
|
+
};
|
|
3939
3929
|
|
|
3940
3930
|
function defaultThrowError() {
|
|
3941
3931
|
throw new Error();
|
|
@@ -3955,88 +3945,24 @@ function setThrowInvalidWriteToSignalError(fn) {
|
|
|
3955
3945
|
* of setting a signal.
|
|
3956
3946
|
*/
|
|
3957
3947
|
let postSignalSetFn = null;
|
|
3958
|
-
class WritableSignalImpl extends ReactiveNode {
|
|
3959
|
-
constructor(value, equal) {
|
|
3960
|
-
super();
|
|
3961
|
-
this.value = value;
|
|
3962
|
-
this.equal = equal;
|
|
3963
|
-
this.consumerAllowSignalWrites = false;
|
|
3964
|
-
}
|
|
3965
|
-
onConsumerDependencyMayHaveChanged() {
|
|
3966
|
-
// This never happens for writable signals as they're not consumers.
|
|
3967
|
-
}
|
|
3968
|
-
onProducerUpdateValueVersion() {
|
|
3969
|
-
// Writable signal value versions are always up to date.
|
|
3970
|
-
}
|
|
3971
|
-
/**
|
|
3972
|
-
* Directly update the value of the signal to a new value, which may or may not be
|
|
3973
|
-
* equal to the previous.
|
|
3974
|
-
*
|
|
3975
|
-
* In the event that `newValue` is semantically equal to the current value, `set` is
|
|
3976
|
-
* a no-op.
|
|
3977
|
-
*/
|
|
3978
|
-
set(newValue) {
|
|
3979
|
-
if (!this.producerUpdatesAllowed) {
|
|
3980
|
-
throwInvalidWriteToSignalError();
|
|
3981
|
-
}
|
|
3982
|
-
if (!this.equal(this.value, newValue)) {
|
|
3983
|
-
this.value = newValue;
|
|
3984
|
-
this.valueVersion++;
|
|
3985
|
-
this.producerMayHaveChanged();
|
|
3986
|
-
postSignalSetFn?.();
|
|
3987
|
-
}
|
|
3988
|
-
}
|
|
3989
|
-
/**
|
|
3990
|
-
* Derive a new value for the signal from its current value using the `updater` function.
|
|
3991
|
-
*
|
|
3992
|
-
* This is equivalent to calling `set` on the result of running `updater` on the current
|
|
3993
|
-
* value.
|
|
3994
|
-
*/
|
|
3995
|
-
update(updater) {
|
|
3996
|
-
if (!this.producerUpdatesAllowed) {
|
|
3997
|
-
throwInvalidWriteToSignalError();
|
|
3998
|
-
}
|
|
3999
|
-
this.set(updater(this.value));
|
|
4000
|
-
}
|
|
4001
|
-
/**
|
|
4002
|
-
* Calls `mutator` on the current value and assumes that it has been mutated.
|
|
4003
|
-
*/
|
|
4004
|
-
mutate(mutator) {
|
|
4005
|
-
if (!this.producerUpdatesAllowed) {
|
|
4006
|
-
throwInvalidWriteToSignalError();
|
|
4007
|
-
}
|
|
4008
|
-
// Mutate bypasses equality checks as it's by definition changing the value.
|
|
4009
|
-
mutator(this.value);
|
|
4010
|
-
this.valueVersion++;
|
|
4011
|
-
this.producerMayHaveChanged();
|
|
4012
|
-
postSignalSetFn?.();
|
|
4013
|
-
}
|
|
4014
|
-
asReadonly() {
|
|
4015
|
-
if (this.readonlySignal === undefined) {
|
|
4016
|
-
this.readonlySignal = createSignalFromFunction(this, () => this.signal());
|
|
4017
|
-
}
|
|
4018
|
-
return this.readonlySignal;
|
|
4019
|
-
}
|
|
4020
|
-
signal() {
|
|
4021
|
-
this.producerAccessed();
|
|
4022
|
-
return this.value;
|
|
4023
|
-
}
|
|
4024
|
-
}
|
|
4025
3948
|
/**
|
|
4026
3949
|
* Create a `Signal` that can be set or updated directly.
|
|
4027
3950
|
*
|
|
4028
3951
|
* @developerPreview
|
|
4029
3952
|
*/
|
|
4030
3953
|
function signal(initialValue, options) {
|
|
4031
|
-
const
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
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;
|
|
4040
3966
|
return signalFn;
|
|
4041
3967
|
}
|
|
4042
3968
|
function setPostSignalSetFn(fn) {
|
|
@@ -4044,6 +3970,50 @@ function setPostSignalSetFn(fn) {
|
|
|
4044
3970
|
postSignalSetFn = fn;
|
|
4045
3971
|
return prev;
|
|
4046
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
|
+
}
|
|
4047
4017
|
|
|
4048
4018
|
/**
|
|
4049
4019
|
* Execute an arbitrary function in a non-reactive (non-tracking) context. The executed function
|
|
@@ -4063,63 +4033,53 @@ function untracked(nonReactiveReadsFn) {
|
|
|
4063
4033
|
}
|
|
4064
4034
|
}
|
|
4065
4035
|
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
this.dirty = false;
|
|
4080
|
-
this.cleanupFn = NOOP_CLEANUP_FN;
|
|
4081
|
-
this.registerOnCleanup = (cleanupFn) => {
|
|
4082
|
-
this.cleanupFn = cleanupFn;
|
|
4083
|
-
};
|
|
4084
|
-
this.consumerAllowSignalWrites = allowSignalWrites;
|
|
4085
|
-
}
|
|
4086
|
-
notify() {
|
|
4087
|
-
if (!this.dirty) {
|
|
4088
|
-
this.schedule(this);
|
|
4089
|
-
}
|
|
4090
|
-
this.dirty = true;
|
|
4091
|
-
}
|
|
4092
|
-
onConsumerDependencyMayHaveChanged() {
|
|
4093
|
-
this.notify();
|
|
4094
|
-
}
|
|
4095
|
-
onProducerUpdateValueVersion() {
|
|
4096
|
-
// Watches are not producers.
|
|
4097
|
-
}
|
|
4098
|
-
/**
|
|
4099
|
-
* Execute the reactive expression in the context of this `Watch` consumer.
|
|
4100
|
-
*
|
|
4101
|
-
* Should be called by the user scheduling algorithm when the provided
|
|
4102
|
-
* `schedule` hook is called by `Watch`.
|
|
4103
|
-
*/
|
|
4104
|
-
run() {
|
|
4105
|
-
this.dirty = false;
|
|
4106
|
-
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)) {
|
|
4107
4049
|
return;
|
|
4108
4050
|
}
|
|
4109
|
-
|
|
4110
|
-
|
|
4051
|
+
node.hasRun = true;
|
|
4052
|
+
const prevConsumer = consumerBeforeComputation(node);
|
|
4111
4053
|
try {
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4054
|
+
node.cleanupFn();
|
|
4055
|
+
node.cleanupFn = NOOP_CLEANUP_FN;
|
|
4056
|
+
node.fn(registerOnCleanup);
|
|
4115
4057
|
}
|
|
4116
4058
|
finally {
|
|
4117
|
-
|
|
4059
|
+
consumerAfterComputation(node, prevConsumer);
|
|
4118
4060
|
}
|
|
4119
|
-
}
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
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
|
|
4123
4083
|
}
|
|
4124
4084
|
|
|
4125
4085
|
/**
|
|
@@ -8782,8 +8742,8 @@ function detachView(lContainer, removeIndex) {
|
|
|
8782
8742
|
function destroyLView(tView, lView) {
|
|
8783
8743
|
if (!(lView[FLAGS] & 256 /* LViewFlags.Destroyed */)) {
|
|
8784
8744
|
const renderer = lView[RENDERER];
|
|
8785
|
-
lView[REACTIVE_TEMPLATE_CONSUMER]
|
|
8786
|
-
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]);
|
|
8787
8747
|
if (renderer.destroyNode) {
|
|
8788
8748
|
applyView(tView, lView, renderer, 3 /* WalkTNodeTreeAction.Destroy */, null, null);
|
|
8789
8749
|
}
|
|
@@ -10945,7 +10905,7 @@ class Version {
|
|
|
10945
10905
|
/**
|
|
10946
10906
|
* @publicApi
|
|
10947
10907
|
*/
|
|
10948
|
-
const VERSION = new Version('16.2.
|
|
10908
|
+
const VERSION = new Version('16.2.4');
|
|
10949
10909
|
|
|
10950
10910
|
// This default value is when checking the hierarchy for a token.
|
|
10951
10911
|
//
|
|
@@ -11402,6 +11362,9 @@ function forkInnerZoneWithAngularBehavior(zone) {
|
|
|
11402
11362
|
name: 'angular',
|
|
11403
11363
|
properties: { 'isAngularZone': true },
|
|
11404
11364
|
onInvokeTask: (delegate, current, target, task, applyThis, applyArgs) => {
|
|
11365
|
+
if (shouldBeIgnoredByZone(applyArgs)) {
|
|
11366
|
+
return delegate.invokeTask(target, task, applyThis, applyArgs);
|
|
11367
|
+
}
|
|
11405
11368
|
try {
|
|
11406
11369
|
onEnter(zone);
|
|
11407
11370
|
return delegate.invokeTask(target, task, applyThis, applyArgs);
|
|
@@ -11553,6 +11516,18 @@ function isStableFactory() {
|
|
|
11553
11516
|
});
|
|
11554
11517
|
return merge$1(isCurrentlyStable, isStable.pipe(share()));
|
|
11555
11518
|
}
|
|
11519
|
+
function shouldBeIgnoredByZone(applyArgs) {
|
|
11520
|
+
if (!Array.isArray(applyArgs)) {
|
|
11521
|
+
return false;
|
|
11522
|
+
}
|
|
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;
|
|
11527
|
+
}
|
|
11528
|
+
// Prevent triggering change detection when the __ignore_ng_zone__ flag is detected.
|
|
11529
|
+
return applyArgs[0].data?.['__ignore_ng_zone__'] === true;
|
|
11530
|
+
}
|
|
11556
11531
|
|
|
11557
11532
|
// Public API for Zone
|
|
11558
11533
|
|
|
@@ -11983,48 +11958,11 @@ function getExpressionChangedErrorDetails(lView, bindingIndex, oldValue, newValu
|
|
|
11983
11958
|
return { propName: undefined, oldValue, newValue };
|
|
11984
11959
|
}
|
|
11985
11960
|
|
|
11986
|
-
class ReactiveLViewConsumer extends ReactiveNode {
|
|
11987
|
-
constructor() {
|
|
11988
|
-
super(...arguments);
|
|
11989
|
-
this.consumerAllowSignalWrites = false;
|
|
11990
|
-
this._lView = null;
|
|
11991
|
-
}
|
|
11992
|
-
set lView(lView) {
|
|
11993
|
-
(typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
11994
|
-
assertEqual(this._lView, null, 'Consumer already associated with a view.');
|
|
11995
|
-
this._lView = lView;
|
|
11996
|
-
}
|
|
11997
|
-
onConsumerDependencyMayHaveChanged() {
|
|
11998
|
-
(typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
11999
|
-
assertDefined(this._lView, 'Updating a signal during template or host binding execution is not allowed.');
|
|
12000
|
-
markViewDirty(this._lView);
|
|
12001
|
-
}
|
|
12002
|
-
onProducerUpdateValueVersion() {
|
|
12003
|
-
// This type doesn't implement the producer side of a `ReactiveNode`.
|
|
12004
|
-
}
|
|
12005
|
-
get hasReadASignal() {
|
|
12006
|
-
return this.hasProducers;
|
|
12007
|
-
}
|
|
12008
|
-
runInContext(fn, rf, ctx) {
|
|
12009
|
-
const prevConsumer = setActiveConsumer(this);
|
|
12010
|
-
this.trackingVersion++;
|
|
12011
|
-
try {
|
|
12012
|
-
fn(rf, ctx);
|
|
12013
|
-
}
|
|
12014
|
-
finally {
|
|
12015
|
-
setActiveConsumer(prevConsumer);
|
|
12016
|
-
}
|
|
12017
|
-
}
|
|
12018
|
-
destroy() {
|
|
12019
|
-
// Incrementing the version means that every producer which tries to update this consumer will
|
|
12020
|
-
// consider its record stale, and not notify.
|
|
12021
|
-
this.trackingVersion++;
|
|
12022
|
-
}
|
|
12023
|
-
}
|
|
12024
11961
|
let currentConsumer = null;
|
|
12025
|
-
function
|
|
12026
|
-
|
|
12027
|
-
|
|
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;
|
|
12028
11966
|
}
|
|
12029
11967
|
/**
|
|
12030
11968
|
* Create a new template consumer pointing at the specified LView.
|
|
@@ -12046,12 +11984,29 @@ function getReactiveLViewConsumer(lView, slot) {
|
|
|
12046
11984
|
*/
|
|
12047
11985
|
function commitLViewConsumerIfHasProducers(lView, slot) {
|
|
12048
11986
|
const consumer = getOrCreateCurrentLViewConsumer();
|
|
12049
|
-
if (!consumer.
|
|
11987
|
+
if (!consumer.producerNode?.length) {
|
|
12050
11988
|
return;
|
|
12051
11989
|
}
|
|
12052
11990
|
lView[slot] = currentConsumer;
|
|
12053
11991
|
consumer.lView = lView;
|
|
12054
|
-
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;
|
|
12055
12010
|
}
|
|
12056
12011
|
|
|
12057
12012
|
/** A special value which designates that a value has not changed. */
|
|
@@ -12168,8 +12123,15 @@ function processHostBindingOpCodes(tView, lView) {
|
|
|
12168
12123
|
const bindingRootIndx = hostBindingOpCodes[++i];
|
|
12169
12124
|
const hostBindingFn = hostBindingOpCodes[++i];
|
|
12170
12125
|
setBindingRootForHostBindings(bindingRootIndx, directiveIdx);
|
|
12171
|
-
|
|
12172
|
-
|
|
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
|
+
}
|
|
12173
12135
|
}
|
|
12174
12136
|
}
|
|
12175
12137
|
}
|
|
@@ -12309,17 +12271,16 @@ function executeTemplate(tView, lView, templateFn, rf, context) {
|
|
|
12309
12271
|
}
|
|
12310
12272
|
const preHookType = isUpdatePhase ? 2 /* ProfilerEvent.TemplateUpdateStart */ : 0 /* ProfilerEvent.TemplateCreateStart */;
|
|
12311
12273
|
profiler(preHookType, context);
|
|
12312
|
-
|
|
12313
|
-
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
try {
|
|
12318
|
-
templateFn(rf, context);
|
|
12319
|
-
}
|
|
12320
|
-
finally {
|
|
12321
|
-
setActiveConsumer(prevConsumer);
|
|
12274
|
+
const effectiveConsumer = isUpdatePhase ? consumer : null;
|
|
12275
|
+
const prevConsumer = consumerBeforeComputation(effectiveConsumer);
|
|
12276
|
+
try {
|
|
12277
|
+
if (effectiveConsumer !== null) {
|
|
12278
|
+
effectiveConsumer.dirty = false;
|
|
12322
12279
|
}
|
|
12280
|
+
templateFn(rf, context);
|
|
12281
|
+
}
|
|
12282
|
+
finally {
|
|
12283
|
+
consumerAfterComputation(effectiveConsumer, prevConsumer);
|
|
12323
12284
|
}
|
|
12324
12285
|
}
|
|
12325
12286
|
finally {
|
|
@@ -13573,21 +13534,21 @@ class EffectManager {
|
|
|
13573
13534
|
}
|
|
13574
13535
|
create(effectFn, destroyRef, allowSignalWrites) {
|
|
13575
13536
|
const zone = (typeof Zone === 'undefined') ? null : Zone.current;
|
|
13576
|
-
const
|
|
13537
|
+
const w = watch(effectFn, (watch) => {
|
|
13577
13538
|
if (!this.all.has(watch)) {
|
|
13578
13539
|
return;
|
|
13579
13540
|
}
|
|
13580
13541
|
this.queue.set(watch, zone);
|
|
13581
13542
|
}, allowSignalWrites);
|
|
13582
|
-
this.all.add(
|
|
13543
|
+
this.all.add(w);
|
|
13583
13544
|
// Effects start dirty.
|
|
13584
|
-
|
|
13545
|
+
w.notify();
|
|
13585
13546
|
let unregisterOnDestroy;
|
|
13586
13547
|
const destroy = () => {
|
|
13587
|
-
|
|
13548
|
+
w.cleanup();
|
|
13588
13549
|
unregisterOnDestroy?.();
|
|
13589
|
-
this.all.delete(
|
|
13590
|
-
this.queue.delete(
|
|
13550
|
+
this.all.delete(w);
|
|
13551
|
+
this.queue.delete(w);
|
|
13591
13552
|
};
|
|
13592
13553
|
unregisterOnDestroy = destroyRef?.onDestroy(destroy);
|
|
13593
13554
|
return {
|
|
@@ -13971,15 +13932,15 @@ function detectChangesInView(lView, mode) {
|
|
|
13971
13932
|
return;
|
|
13972
13933
|
}
|
|
13973
13934
|
const tView = lView[TVIEW];
|
|
13974
|
-
|
|
13935
|
+
const flags = lView[FLAGS];
|
|
13936
|
+
if ((flags & (16 /* LViewFlags.CheckAlways */ | 64 /* LViewFlags.Dirty */) &&
|
|
13975
13937
|
mode === 0 /* ChangeDetectionMode.Global */) ||
|
|
13976
|
-
|
|
13938
|
+
flags & 1024 /* LViewFlags.RefreshView */ ||
|
|
13977
13939
|
mode === 2 /* ChangeDetectionMode.BugToForceRefreshAndIgnoreViewFlags */) {
|
|
13978
13940
|
refreshView(tView, lView, tView.template, lView[CONTEXT]);
|
|
13979
13941
|
}
|
|
13980
13942
|
else if (lView[DESCENDANT_VIEWS_TO_REFRESH] > 0) {
|
|
13981
13943
|
detectChangesInEmbeddedViews(lView, 1 /* ChangeDetectionMode.Targeted */);
|
|
13982
|
-
const tView = lView[TVIEW];
|
|
13983
13944
|
const components = tView.components;
|
|
13984
13945
|
if (components !== null) {
|
|
13985
13946
|
detectChangesInChildComponents(lView, components, 1 /* ChangeDetectionMode.Targeted */);
|
|
@@ -17755,14 +17716,12 @@ function getTStylingRangePrev(tStylingRange) {
|
|
|
17755
17716
|
}
|
|
17756
17717
|
function getTStylingRangePrevDuplicate(tStylingRange) {
|
|
17757
17718
|
ngDevMode && assertNumber(tStylingRange, 'expected number');
|
|
17758
|
-
return (tStylingRange & 2 /* StylingRange.PREV_DUPLICATE */) ==
|
|
17759
|
-
2 /* StylingRange.PREV_DUPLICATE */;
|
|
17719
|
+
return (tStylingRange & 2 /* StylingRange.PREV_DUPLICATE */) == 2 /* StylingRange.PREV_DUPLICATE */;
|
|
17760
17720
|
}
|
|
17761
17721
|
function setTStylingRangePrev(tStylingRange, previous) {
|
|
17762
17722
|
ngDevMode && assertNumber(tStylingRange, 'expected number');
|
|
17763
17723
|
ngDevMode && assertNumberInRange(previous, 0, 32767 /* StylingRange.UNSIGNED_MASK */);
|
|
17764
|
-
return ((tStylingRange & ~4294836224 /* StylingRange.PREV_MASK */) |
|
|
17765
|
-
(previous << 17 /* StylingRange.PREV_SHIFT */));
|
|
17724
|
+
return ((tStylingRange & ~4294836224 /* StylingRange.PREV_MASK */) | (previous << 17 /* StylingRange.PREV_SHIFT */));
|
|
17766
17725
|
}
|
|
17767
17726
|
function setTStylingRangePrevDuplicate(tStylingRange) {
|
|
17768
17727
|
ngDevMode && assertNumber(tStylingRange, 'expected number');
|
|
@@ -17780,8 +17739,7 @@ function setTStylingRangeNext(tStylingRange, next) {
|
|
|
17780
17739
|
}
|
|
17781
17740
|
function getTStylingRangeNextDuplicate(tStylingRange) {
|
|
17782
17741
|
ngDevMode && assertNumber(tStylingRange, 'expected number');
|
|
17783
|
-
return (tStylingRange & 1 /* StylingRange.NEXT_DUPLICATE */) ===
|
|
17784
|
-
1 /* StylingRange.NEXT_DUPLICATE */;
|
|
17742
|
+
return ((tStylingRange) & 1 /* StylingRange.NEXT_DUPLICATE */) === 1 /* StylingRange.NEXT_DUPLICATE */;
|
|
17785
17743
|
}
|
|
17786
17744
|
function setTStylingRangeNextDuplicate(tStylingRange) {
|
|
17787
17745
|
ngDevMode && assertNumber(tStylingRange, 'expected number');
|
|
@@ -24579,6 +24537,40 @@ function findMatchingDehydratedView(lContainer, template) {
|
|
|
24579
24537
|
* A view container instance can contain other view containers,
|
|
24580
24538
|
* creating a [view hierarchy](guide/glossary#view-hierarchy).
|
|
24581
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
|
+
*
|
|
24582
24574
|
* @see {@link ComponentRef}
|
|
24583
24575
|
* @see {@link EmbeddedViewRef}
|
|
24584
24576
|
*
|