@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.
Files changed (35) hide show
  1. package/esm2022/src/linker/view_container_ref.mjs +35 -1
  2. package/esm2022/src/render3/component.mjs +4 -3
  3. package/esm2022/src/render3/di.mjs +1 -1
  4. package/esm2022/src/render3/instructions/change_detection.mjs +4 -4
  5. package/esm2022/src/render3/instructions/shared.mjs +20 -14
  6. package/esm2022/src/render3/interfaces/injector.mjs +1 -1
  7. package/esm2022/src/render3/interfaces/styling.mjs +4 -7
  8. package/esm2022/src/render3/node_manipulation.mjs +4 -3
  9. package/esm2022/src/render3/reactive_lview_consumer.mjs +25 -45
  10. package/esm2022/src/render3/reactivity/effect.mjs +8 -8
  11. package/esm2022/src/render3/util/injector_utils.mjs +1 -1
  12. package/esm2022/src/signals/index.mjs +4 -4
  13. package/esm2022/src/signals/src/api.mjs +2 -11
  14. package/esm2022/src/signals/src/computed.mjs +43 -93
  15. package/esm2022/src/signals/src/graph.mjs +238 -162
  16. package/esm2022/src/signals/src/signal.mjs +59 -79
  17. package/esm2022/src/signals/src/watch.mjs +38 -52
  18. package/esm2022/src/signals/src/weak_ref.mjs +2 -29
  19. package/esm2022/src/util/security/trusted_type_defs.mjs +1 -1
  20. package/esm2022/src/util/security/trusted_types.mjs +1 -1
  21. package/esm2022/src/version.mjs +1 -1
  22. package/esm2022/src/zone/ng_zone.mjs +16 -1
  23. package/esm2022/testing/src/logger.mjs +3 -3
  24. package/fesm2022/core.mjs +476 -483
  25. package/fesm2022/core.mjs.map +1 -1
  26. package/fesm2022/rxjs-interop.mjs +373 -413
  27. package/fesm2022/rxjs-interop.mjs.map +1 -1
  28. package/fesm2022/testing.mjs +474 -482
  29. package/fesm2022/testing.mjs.map +1 -1
  30. package/index.d.ts +112 -102
  31. package/package.json +1 -1
  32. package/rxjs-interop/index.d.ts +1 -1
  33. package/schematics/ng-generate/standalone-migration/bundle.js +9 -9
  34. package/schematics/ng-generate/standalone-migration/bundle.js.map +1 -1
  35. package/testing/index.d.ts +1 -1
package/fesm2022/core.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v16.2.3
2
+ * @license Angular v16.2.4
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -2303,15 +2303,6 @@ const SIGNAL = Symbol('SIGNAL');
2303
2303
  function isSignal(value) {
2304
2304
  return typeof value === 'function' && value[SIGNAL] !== undefined;
2305
2305
  }
2306
- /**
2307
- * Converts `fn` into a marked signal function (where `isSignal(fn)` will be `true`), and
2308
- * potentially add some set of extra properties (passed as an object record `extraApi`).
2309
- */
2310
- function createSignalFromFunction(node, fn, extraApi = {}) {
2311
- fn[SIGNAL] = node;
2312
- // Copy properties from `extraApi` to `fn` to complete the desired API of the `Signal`.
2313
- return Object.assign(fn, extraApi);
2314
- }
2315
2306
  /**
2316
2307
  * The default equality function used for `signal` and `computed`, which treats objects and arrays
2317
2308
  * as never equal, and all other primitive values using identity semantics.
@@ -2332,216 +2323,265 @@ function defaultEquals(a, b) {
2332
2323
 
2333
2324
  // Required as the signals library is in a separate package, so we need to explicitly ensure the
2334
2325
  /**
2335
- * A `WeakRef`-compatible reference that fakes the API with a strong reference
2336
- * internally.
2326
+ * The currently active consumer `ReactiveNode`, if running code in a reactive context.
2327
+ *
2328
+ * Change this via `setActiveConsumer`.
2329
+ */
2330
+ let activeConsumer = null;
2331
+ let inNotificationPhase = false;
2332
+ function setActiveConsumer(consumer) {
2333
+ const prev = activeConsumer;
2334
+ activeConsumer = consumer;
2335
+ return prev;
2336
+ }
2337
+ const REACTIVE_NODE = {
2338
+ version: 0,
2339
+ dirty: false,
2340
+ producerNode: undefined,
2341
+ producerLastReadVersion: undefined,
2342
+ producerIndexOfThis: undefined,
2343
+ nextProducerIndex: 0,
2344
+ liveConsumerNode: undefined,
2345
+ liveConsumerIndexOfThis: undefined,
2346
+ consumerAllowSignalWrites: false,
2347
+ consumerIsAlwaysLive: false,
2348
+ producerMustRecompute: () => false,
2349
+ producerRecomputeValue: () => { },
2350
+ consumerMarkedDirty: () => { },
2351
+ };
2352
+ /**
2353
+ * Called by implementations when a producer's signal is read.
2337
2354
  */
2338
- class LeakyRef {
2339
- constructor(ref) {
2340
- this.ref = ref;
2355
+ function producerAccessed(node) {
2356
+ if (inNotificationPhase) {
2357
+ throw new Error(typeof ngDevMode !== 'undefined' && ngDevMode ?
2358
+ `Assertion error: signal read during notification phase` :
2359
+ '');
2341
2360
  }
2342
- deref() {
2343
- return this.ref;
2361
+ if (activeConsumer === null) {
2362
+ // Accessed outside of a reactive context, so nothing to record.
2363
+ return;
2344
2364
  }
2345
- }
2346
- // `WeakRef` is not always defined in every TS environment where Angular is compiled. Instead,
2347
- // read it off of the global context if available.
2348
- // tslint:disable-next-line: no-toplevel-property-access
2349
- let WeakRefImpl = _global['WeakRef'] ?? LeakyRef;
2350
- function newWeakRef(value) {
2351
- if (typeof ngDevMode !== 'undefined' && ngDevMode && WeakRefImpl === undefined) {
2352
- throw new Error(`Angular requires a browser which supports the 'WeakRef' API`);
2365
+ // This producer is the `idx`th dependency of `activeConsumer`.
2366
+ const idx = activeConsumer.nextProducerIndex++;
2367
+ assertConsumerNode(activeConsumer);
2368
+ if (idx < activeConsumer.producerNode.length && activeConsumer.producerNode[idx] !== node) {
2369
+ // There's been a change in producers since the last execution of `activeConsumer`.
2370
+ // `activeConsumer.producerNode[idx]` holds a stale dependency which will be be removed and
2371
+ // replaced with `this`.
2372
+ //
2373
+ // If `activeConsumer` isn't live, then this is a no-op, since we can replace the producer in
2374
+ // `activeConsumer.producerNode` directly. However, if `activeConsumer` is live, then we need
2375
+ // to remove it from the stale producer's `liveConsumer`s.
2376
+ if (consumerIsLive(activeConsumer)) {
2377
+ const staleProducer = activeConsumer.producerNode[idx];
2378
+ producerRemoveLiveConsumerAtIndex(staleProducer, activeConsumer.producerIndexOfThis[idx]);
2379
+ // At this point, the only record of `staleProducer` is the reference at
2380
+ // `activeConsumer.producerNode[idx]` which will be overwritten below.
2381
+ }
2353
2382
  }
2354
- return new WeakRefImpl(value);
2355
- }
2356
- function setAlternateWeakRefImpl(impl) {
2357
- // no-op since the alternate impl is included by default by the framework. Remove once internal
2358
- // migration is complete.
2383
+ if (activeConsumer.producerNode[idx] !== node) {
2384
+ // We're a new dependency of the consumer (at `idx`).
2385
+ activeConsumer.producerNode[idx] = node;
2386
+ // If the active consumer is live, then add it as a live consumer. If not, then use 0 as a
2387
+ // placeholder value.
2388
+ activeConsumer.producerIndexOfThis[idx] =
2389
+ consumerIsLive(activeConsumer) ? producerAddLiveConsumer(node, activeConsumer, idx) : 0;
2390
+ }
2391
+ activeConsumer.producerLastReadVersion[idx] = node.version;
2359
2392
  }
2360
-
2361
- // Required as the signals library is in a separate package, so we need to explicitly ensure the
2362
2393
  /**
2363
- * Counter tracking the next `ProducerId` or `ConsumerId`.
2394
+ * Ensure this producer's `version` is up-to-date.
2364
2395
  */
2365
- let _nextReactiveId = 0;
2396
+ function producerUpdateValueVersion(node) {
2397
+ if (consumerIsLive(node) && !node.dirty) {
2398
+ // A live consumer will be marked dirty by producers, so a clean state means that its version
2399
+ // is guaranteed to be up-to-date.
2400
+ return;
2401
+ }
2402
+ if (!node.producerMustRecompute(node) && !consumerPollProducersForChange(node)) {
2403
+ // None of our producers report a change since the last time they were read, so no
2404
+ // recomputation of our value is necessary, and we can consider ourselves clean.
2405
+ node.dirty = false;
2406
+ return;
2407
+ }
2408
+ node.producerRecomputeValue(node);
2409
+ // After recomputing the value, we're no longer dirty.
2410
+ node.dirty = false;
2411
+ }
2366
2412
  /**
2367
- * Tracks the currently active reactive consumer (or `null` if there is no active
2368
- * consumer).
2413
+ * Propagate a dirty notification to live consumers of this producer.
2369
2414
  */
2370
- let activeConsumer = null;
2415
+ function producerNotifyConsumers(node) {
2416
+ if (node.liveConsumerNode === undefined) {
2417
+ return;
2418
+ }
2419
+ // Prevent signal reads when we're updating the graph
2420
+ const prev = inNotificationPhase;
2421
+ inNotificationPhase = true;
2422
+ try {
2423
+ for (const consumer of node.liveConsumerNode) {
2424
+ if (!consumer.dirty) {
2425
+ consumerMarkDirty(consumer);
2426
+ }
2427
+ }
2428
+ }
2429
+ finally {
2430
+ inNotificationPhase = prev;
2431
+ }
2432
+ }
2371
2433
  /**
2372
- * Whether the graph is currently propagating change notifications.
2434
+ * Whether this `ReactiveNode` in its producer capacity is currently allowed to initiate updates,
2435
+ * based on the current consumer context.
2373
2436
  */
2374
- let inNotificationPhase = false;
2375
- function setActiveConsumer(consumer) {
2376
- const prev = activeConsumer;
2377
- activeConsumer = consumer;
2378
- return prev;
2437
+ function producerUpdatesAllowed() {
2438
+ return activeConsumer?.consumerAllowSignalWrites !== false;
2439
+ }
2440
+ function consumerMarkDirty(node) {
2441
+ node.dirty = true;
2442
+ producerNotifyConsumers(node);
2443
+ node.consumerMarkedDirty?.(node);
2379
2444
  }
2380
2445
  /**
2381
- * A node in the reactive graph.
2446
+ * Prepare this consumer to run a computation in its reactive context.
2382
2447
  *
2383
- * Nodes can be producers of reactive values, consumers of other reactive values, or both.
2384
- *
2385
- * Producers are nodes that produce values, and can be depended upon by consumer nodes.
2386
- *
2387
- * Producers expose a monotonic `valueVersion` counter, and are responsible for incrementing this
2388
- * version when their value semantically changes. Some producers may produce their values lazily and
2389
- * thus at times need to be polled for potential updates to their value (and by extension their
2390
- * `valueVersion`). This is accomplished via the `onProducerUpdateValueVersion` method for
2391
- * implemented by producers, which should perform whatever calculations are necessary to ensure
2392
- * `valueVersion` is up to date.
2393
- *
2394
- * Consumers are nodes that depend on the values of producers and are notified when those values
2395
- * might have changed.
2396
- *
2397
- * Consumers do not wrap the reads they consume themselves, but rather can be set as the active
2398
- * reader via `setActiveConsumer`. Reads of producers that happen while a consumer is active will
2399
- * result in those producers being added as dependencies of that consumer node.
2400
- *
2401
- * The set of dependencies of a consumer is dynamic. Implementers expose a monotonically increasing
2402
- * `trackingVersion` counter, which increments whenever the consumer is about to re-run any reactive
2403
- * reads it needs and establish a new set of dependencies as a result.
2448
+ * Must be called by subclasses which represent reactive computations, before those computations
2449
+ * begin.
2450
+ */
2451
+ function consumerBeforeComputation(node) {
2452
+ node && (node.nextProducerIndex = 0);
2453
+ return setActiveConsumer(node);
2454
+ }
2455
+ /**
2456
+ * Finalize this consumer's state after a reactive computation has run.
2404
2457
  *
2405
- * Producers store the last `trackingVersion` they've seen from `Consumer`s which have read them.
2406
- * This allows a producer to identify whether its record of the dependency is current or stale, by
2407
- * comparing the consumer's `trackingVersion` to the version at which the dependency was
2408
- * last observed.
2458
+ * Must be called by subclasses which represent reactive computations, after those computations
2459
+ * have finished.
2409
2460
  */
2410
- class ReactiveNode {
2411
- constructor() {
2412
- this.id = _nextReactiveId++;
2413
- /**
2414
- * A cached weak reference to this node, which will be used in `ReactiveEdge`s.
2415
- */
2416
- this.ref = newWeakRef(this);
2417
- /**
2418
- * Edges to producers on which this node depends (in its consumer capacity).
2419
- */
2420
- this.producers = new Map();
2421
- /**
2422
- * Edges to consumers on which this node depends (in its producer capacity).
2423
- */
2424
- this.consumers = new Map();
2425
- /**
2426
- * Monotonically increasing counter representing a version of this `Consumer`'s
2427
- * dependencies.
2428
- */
2429
- this.trackingVersion = 0;
2430
- /**
2431
- * Monotonically increasing counter which increases when the value of this `Producer`
2432
- * semantically changes.
2433
- */
2434
- this.valueVersion = 0;
2461
+ function consumerAfterComputation(node, prevConsumer) {
2462
+ setActiveConsumer(prevConsumer);
2463
+ if (!node || node.producerNode === undefined || node.producerIndexOfThis === undefined ||
2464
+ node.producerLastReadVersion === undefined) {
2465
+ return;
2435
2466
  }
2436
- /**
2437
- * Polls dependencies of a consumer to determine if they have actually changed.
2438
- *
2439
- * If this returns `false`, then even though the consumer may have previously been notified of a
2440
- * change, the values of its dependencies have not actually changed and the consumer should not
2441
- * rerun any reactions.
2442
- */
2443
- consumerPollProducersForChange() {
2444
- for (const [producerId, edge] of this.producers) {
2445
- const producer = edge.producerNode.deref();
2446
- // On Safari < 16.1 deref can return null, we need to check for null also.
2447
- // See https://github.com/WebKit/WebKit/commit/44c15ba58912faab38b534fef909dd9e13e095e0
2448
- if (producer == null || edge.atTrackingVersion !== this.trackingVersion) {
2449
- // This dependency edge is stale, so remove it.
2450
- this.producers.delete(producerId);
2451
- producer?.consumers.delete(this.id);
2452
- continue;
2453
- }
2454
- if (producer.producerPollStatus(edge.seenValueVersion)) {
2455
- // One of the dependencies reports a real value change.
2456
- return true;
2457
- }
2467
+ if (consumerIsLive(node)) {
2468
+ // For live consumers, we need to remove the producer -> consumer edge for any stale producers
2469
+ // which weren't dependencies after the recomputation.
2470
+ for (let i = node.nextProducerIndex; i < node.producerNode.length; i++) {
2471
+ producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
2458
2472
  }
2459
- // No dependency reported a real value change, so the `Consumer` has also not been
2460
- // impacted.
2461
- return false;
2462
2473
  }
2463
- /**
2464
- * Notify all consumers of this producer that its value may have changed.
2465
- */
2466
- producerMayHaveChanged() {
2467
- // Prevent signal reads when we're updating the graph
2468
- const prev = inNotificationPhase;
2469
- inNotificationPhase = true;
2470
- try {
2471
- for (const [consumerId, edge] of this.consumers) {
2472
- const consumer = edge.consumerNode.deref();
2473
- // On Safari < 16.1 deref can return null, we need to check for null also.
2474
- // See https://github.com/WebKit/WebKit/commit/44c15ba58912faab38b534fef909dd9e13e095e0
2475
- if (consumer == null || consumer.trackingVersion !== edge.atTrackingVersion) {
2476
- this.consumers.delete(consumerId);
2477
- consumer?.producers.delete(this.id);
2478
- continue;
2479
- }
2480
- consumer.onConsumerDependencyMayHaveChanged();
2481
- }
2482
- }
2483
- finally {
2484
- inNotificationPhase = prev;
2485
- }
2474
+ // Truncate the producer tracking arrays.
2475
+ for (let i = node.nextProducerIndex; i < node.producerNode.length; i++) {
2476
+ node.producerNode.pop();
2477
+ node.producerLastReadVersion.pop();
2478
+ node.producerIndexOfThis.pop();
2486
2479
  }
2487
- /**
2488
- * Mark that this producer node has been accessed in the current reactive context.
2489
- */
2490
- producerAccessed() {
2491
- if (inNotificationPhase) {
2492
- throw new Error(typeof ngDevMode !== 'undefined' && ngDevMode ?
2493
- `Assertion error: signal read during notification phase` :
2494
- '');
2495
- }
2496
- if (activeConsumer === null) {
2497
- return;
2480
+ }
2481
+ /**
2482
+ * Determine whether this consumer has any dependencies which have changed since the last time
2483
+ * they were read.
2484
+ */
2485
+ function consumerPollProducersForChange(node) {
2486
+ assertConsumerNode(node);
2487
+ // Poll producers for change.
2488
+ for (let i = 0; i < node.producerNode.length; i++) {
2489
+ const producer = node.producerNode[i];
2490
+ const seenVersion = node.producerLastReadVersion[i];
2491
+ // First check the versions. A mismatch means that the producer's value is known to have
2492
+ // changed since the last time we read it.
2493
+ if (seenVersion !== producer.version) {
2494
+ return true;
2498
2495
  }
2499
- // Either create or update the dependency `Edge` in both directions.
2500
- let edge = activeConsumer.producers.get(this.id);
2501
- if (edge === undefined) {
2502
- edge = {
2503
- consumerNode: activeConsumer.ref,
2504
- producerNode: this.ref,
2505
- seenValueVersion: this.valueVersion,
2506
- atTrackingVersion: activeConsumer.trackingVersion,
2507
- };
2508
- activeConsumer.producers.set(this.id, edge);
2509
- this.consumers.set(activeConsumer.id, edge);
2496
+ // The producer's version is the same as the last time we read it, but it might itself be
2497
+ // stale. Force the producer to recompute its version (calculating a new value if necessary).
2498
+ producerUpdateValueVersion(producer);
2499
+ // Now when we do this check, `producer.version` is guaranteed to be up to date, so if the
2500
+ // versions still match then it has not changed since the last time we read it.
2501
+ if (seenVersion !== producer.version) {
2502
+ return true;
2510
2503
  }
2511
- else {
2512
- edge.seenValueVersion = this.valueVersion;
2513
- edge.atTrackingVersion = activeConsumer.trackingVersion;
2504
+ }
2505
+ return false;
2506
+ }
2507
+ /**
2508
+ * Disconnect this consumer from the graph.
2509
+ */
2510
+ function consumerDestroy(node) {
2511
+ assertConsumerNode(node);
2512
+ if (consumerIsLive(node)) {
2513
+ // Drop all connections from the graph to this node.
2514
+ for (let i = 0; i < node.producerNode.length; i++) {
2515
+ producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
2514
2516
  }
2515
2517
  }
2516
- /**
2517
- * Whether this consumer currently has any producers registered.
2518
- */
2519
- get hasProducers() {
2520
- return this.producers.size > 0;
2518
+ // Truncate all the arrays to drop all connection from this node to the graph.
2519
+ node.producerNode.length = node.producerLastReadVersion.length = node.producerIndexOfThis.length =
2520
+ 0;
2521
+ if (node.liveConsumerNode) {
2522
+ node.liveConsumerNode.length = node.liveConsumerIndexOfThis.length = 0;
2521
2523
  }
2522
- /**
2523
- * Whether this `ReactiveNode` in its producer capacity is currently allowed to initiate updates,
2524
- * based on the current consumer context.
2525
- */
2526
- get producerUpdatesAllowed() {
2527
- return activeConsumer?.consumerAllowSignalWrites !== false;
2524
+ }
2525
+ /**
2526
+ * Add `consumer` as a live consumer of this node.
2527
+ *
2528
+ * Note that this operation is potentially transitive. If this node becomes live, then it becomes
2529
+ * a live consumer of all of its current producers.
2530
+ */
2531
+ function producerAddLiveConsumer(node, consumer, indexOfThis) {
2532
+ assertProducerNode(node);
2533
+ assertConsumerNode(node);
2534
+ if (node.liveConsumerNode.length === 0) {
2535
+ // When going from 0 to 1 live consumers, we become a live consumer to our producers.
2536
+ for (let i = 0; i < node.producerNode.length; i++) {
2537
+ node.producerIndexOfThis[i] = producerAddLiveConsumer(node.producerNode[i], node, i);
2538
+ }
2528
2539
  }
2529
- /**
2530
- * Checks if a `Producer` has a current value which is different than the value
2531
- * last seen at a specific version by a `Consumer` which recorded a dependency on
2532
- * this `Producer`.
2533
- */
2534
- producerPollStatus(lastSeenValueVersion) {
2535
- // `producer.valueVersion` may be stale, but a mismatch still means that the value
2536
- // last seen by the `Consumer` is also stale.
2537
- if (this.valueVersion !== lastSeenValueVersion) {
2538
- return true;
2540
+ node.liveConsumerIndexOfThis.push(indexOfThis);
2541
+ return node.liveConsumerNode.push(consumer) - 1;
2542
+ }
2543
+ /**
2544
+ * Remove the live consumer at `idx`.
2545
+ */
2546
+ function producerRemoveLiveConsumerAtIndex(node, idx) {
2547
+ assertProducerNode(node);
2548
+ assertConsumerNode(node);
2549
+ if (node.liveConsumerNode.length === 1) {
2550
+ // When removing the last live consumer, we will no longer be live. We need to remove
2551
+ // ourselves from our producers' tracking (which may cause consumer-producers to lose
2552
+ // liveness as well).
2553
+ for (let i = 0; i < node.producerNode.length; i++) {
2554
+ producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
2539
2555
  }
2540
- // Trigger the `Producer` to update its `valueVersion` if necessary.
2541
- this.onProducerUpdateValueVersion();
2542
- // At this point, we can trust `producer.valueVersion`.
2543
- return this.valueVersion !== lastSeenValueVersion;
2544
2556
  }
2557
+ // Move the last value of `liveConsumers` into `idx`. Note that if there's only a single
2558
+ // live consumer, this is a no-op.
2559
+ const lastIdx = node.liveConsumerNode.length - 1;
2560
+ node.liveConsumerNode[idx] = node.liveConsumerNode[lastIdx];
2561
+ node.liveConsumerIndexOfThis[idx] = node.liveConsumerIndexOfThis[lastIdx];
2562
+ // Truncate the array.
2563
+ node.liveConsumerNode.length--;
2564
+ node.liveConsumerIndexOfThis.length--;
2565
+ // If the index is still valid, then we need to fix the index pointer from the producer to this
2566
+ // consumer, and update it from `lastIdx` to `idx` (accounting for the move above).
2567
+ if (idx < node.liveConsumerNode.length) {
2568
+ const idxProducer = node.liveConsumerIndexOfThis[idx];
2569
+ const consumer = node.liveConsumerNode[idx];
2570
+ assertConsumerNode(consumer);
2571
+ consumer.producerIndexOfThis[idxProducer] = idx;
2572
+ }
2573
+ }
2574
+ function consumerIsLive(node) {
2575
+ return node.consumerIsAlwaysLive || (node?.liveConsumerNode?.length ?? 0) > 0;
2576
+ }
2577
+ function assertConsumerNode(node) {
2578
+ node.producerNode ??= [];
2579
+ node.producerIndexOfThis ??= [];
2580
+ node.producerLastReadVersion ??= [];
2581
+ }
2582
+ function assertProducerNode(node) {
2583
+ node.liveConsumerNode ??= [];
2584
+ node.liveConsumerIndexOfThis ??= [];
2545
2585
  }
2546
2586
 
2547
2587
  /**
@@ -2550,10 +2590,21 @@ class ReactiveNode {
2550
2590
  * @developerPreview
2551
2591
  */
2552
2592
  function computed(computation, options) {
2553
- const node = new ComputedImpl(computation, options?.equal ?? defaultEquals);
2554
- // Casting here is required for g3, as TS inference behavior is slightly different between our
2555
- // version/options and g3's.
2556
- return createSignalFromFunction(node, node.signal.bind(node));
2593
+ const node = Object.create(COMPUTED_NODE);
2594
+ node.computation = computation;
2595
+ options?.equal && (node.equal = options.equal);
2596
+ const computed = () => {
2597
+ // Check if the value needs updating before returning it.
2598
+ producerUpdateValueVersion(node);
2599
+ // Record that someone looked at this signal.
2600
+ producerAccessed(node);
2601
+ if (node.value === ERRORED) {
2602
+ throw node.error;
2603
+ }
2604
+ return node.value;
2605
+ };
2606
+ computed[SIGNAL] = node;
2607
+ return computed;
2557
2608
  }
2558
2609
  /**
2559
2610
  * A dedicated symbol used before a computed value has been calculated for the first time.
@@ -2572,108 +2623,47 @@ const COMPUTING = Symbol('COMPUTING');
2572
2623
  * Explicitly typed as `any` so we can use it as signal's value.
2573
2624
  */
2574
2625
  const ERRORED = Symbol('ERRORED');
2575
- /**
2576
- * A computation, which derives a value from a declarative reactive expression.
2577
- *
2578
- * `Computed`s are both producers and consumers of reactivity.
2579
- */
2580
- class ComputedImpl extends ReactiveNode {
2581
- constructor(computation, equal) {
2582
- super();
2583
- this.computation = computation;
2584
- this.equal = equal;
2585
- /**
2586
- * Current value of the computation.
2587
- *
2588
- * This can also be one of the special values `UNSET`, `COMPUTING`, or `ERRORED`.
2589
- */
2590
- this.value = UNSET;
2591
- /**
2592
- * If `value` is `ERRORED`, the error caught from the last computation attempt which will
2593
- * be re-thrown.
2594
- */
2595
- this.error = null;
2596
- /**
2597
- * Flag indicating that the computation is currently stale, meaning that one of the
2598
- * dependencies has notified of a potential change.
2599
- *
2600
- * It's possible that no dependency has _actually_ changed, in which case the `stale`
2601
- * state can be resolved without recomputing the value.
2602
- */
2603
- this.stale = true;
2604
- this.consumerAllowSignalWrites = false;
2605
- }
2606
- onConsumerDependencyMayHaveChanged() {
2607
- if (this.stale) {
2608
- // We've already notified consumers that this value has potentially changed.
2609
- return;
2610
- }
2611
- // Record that the currently cached value may be stale.
2612
- this.stale = true;
2613
- // Notify any consumers about the potential change.
2614
- this.producerMayHaveChanged();
2615
- }
2616
- onProducerUpdateValueVersion() {
2617
- if (!this.stale) {
2618
- // The current value and its version are already up to date.
2619
- return;
2620
- }
2621
- // The current value is stale. Check whether we need to produce a new one.
2622
- if (this.value !== UNSET && this.value !== COMPUTING &&
2623
- !this.consumerPollProducersForChange()) {
2624
- // Even though we were previously notified of a potential dependency update, all of
2625
- // our dependencies report that they have not actually changed in value, so we can
2626
- // resolve the stale state without needing to recompute the current value.
2627
- this.stale = false;
2628
- return;
2629
- }
2630
- // The current value is stale, and needs to be recomputed. It still may not change -
2631
- // that depends on whether the newly computed value is equal to the old.
2632
- this.recomputeValue();
2633
- }
2634
- recomputeValue() {
2635
- if (this.value === COMPUTING) {
2626
+ const COMPUTED_NODE = {
2627
+ ...REACTIVE_NODE,
2628
+ value: UNSET,
2629
+ dirty: true,
2630
+ error: null,
2631
+ equal: defaultEquals,
2632
+ producerMustRecompute(node) {
2633
+ // Force a recomputation if there's no current value, or if the current value is in the process
2634
+ // of being calculated (which should throw an error).
2635
+ return node.value === UNSET || node.value === COMPUTING;
2636
+ },
2637
+ producerRecomputeValue(node) {
2638
+ if (node.value === COMPUTING) {
2636
2639
  // Our computation somehow led to a cyclic read of itself.
2637
2640
  throw new Error('Detected cycle in computations.');
2638
2641
  }
2639
- const oldValue = this.value;
2640
- this.value = COMPUTING;
2641
- // As we're re-running the computation, update our dependent tracking version number.
2642
- this.trackingVersion++;
2643
- const prevConsumer = setActiveConsumer(this);
2642
+ const oldValue = node.value;
2643
+ node.value = COMPUTING;
2644
+ const prevConsumer = consumerBeforeComputation(node);
2644
2645
  let newValue;
2645
2646
  try {
2646
- newValue = this.computation();
2647
+ newValue = node.computation();
2647
2648
  }
2648
2649
  catch (err) {
2649
2650
  newValue = ERRORED;
2650
- this.error = err;
2651
+ node.error = err;
2651
2652
  }
2652
2653
  finally {
2653
- setActiveConsumer(prevConsumer);
2654
+ consumerAfterComputation(node, prevConsumer);
2654
2655
  }
2655
- this.stale = false;
2656
2656
  if (oldValue !== UNSET && oldValue !== ERRORED && newValue !== ERRORED &&
2657
- this.equal(oldValue, newValue)) {
2657
+ node.equal(oldValue, newValue)) {
2658
2658
  // No change to `valueVersion` - old and new values are
2659
2659
  // semantically equivalent.
2660
- this.value = oldValue;
2660
+ node.value = oldValue;
2661
2661
  return;
2662
2662
  }
2663
- this.value = newValue;
2664
- this.valueVersion++;
2665
- }
2666
- signal() {
2667
- // Check if the value needs updating before returning it.
2668
- this.onProducerUpdateValueVersion();
2669
- // Record that someone looked at this signal.
2670
- this.producerAccessed();
2671
- if (this.value === ERRORED) {
2672
- throw this.error;
2673
- }
2674
- return this.value;
2675
- }
2676
- }
2663
+ node.value = newValue;
2664
+ node.version++;
2665
+ },
2666
+ };
2677
2667
 
2678
2668
  function defaultThrowError() {
2679
2669
  throw new Error();
@@ -2693,88 +2683,24 @@ function setThrowInvalidWriteToSignalError(fn) {
2693
2683
  * of setting a signal.
2694
2684
  */
2695
2685
  let postSignalSetFn = null;
2696
- class WritableSignalImpl extends ReactiveNode {
2697
- constructor(value, equal) {
2698
- super();
2699
- this.value = value;
2700
- this.equal = equal;
2701
- this.consumerAllowSignalWrites = false;
2702
- }
2703
- onConsumerDependencyMayHaveChanged() {
2704
- // This never happens for writable signals as they're not consumers.
2705
- }
2706
- onProducerUpdateValueVersion() {
2707
- // Writable signal value versions are always up to date.
2708
- }
2709
- /**
2710
- * Directly update the value of the signal to a new value, which may or may not be
2711
- * equal to the previous.
2712
- *
2713
- * In the event that `newValue` is semantically equal to the current value, `set` is
2714
- * a no-op.
2715
- */
2716
- set(newValue) {
2717
- if (!this.producerUpdatesAllowed) {
2718
- throwInvalidWriteToSignalError();
2719
- }
2720
- if (!this.equal(this.value, newValue)) {
2721
- this.value = newValue;
2722
- this.valueVersion++;
2723
- this.producerMayHaveChanged();
2724
- postSignalSetFn?.();
2725
- }
2726
- }
2727
- /**
2728
- * Derive a new value for the signal from its current value using the `updater` function.
2729
- *
2730
- * This is equivalent to calling `set` on the result of running `updater` on the current
2731
- * value.
2732
- */
2733
- update(updater) {
2734
- if (!this.producerUpdatesAllowed) {
2735
- throwInvalidWriteToSignalError();
2736
- }
2737
- this.set(updater(this.value));
2738
- }
2739
- /**
2740
- * Calls `mutator` on the current value and assumes that it has been mutated.
2741
- */
2742
- mutate(mutator) {
2743
- if (!this.producerUpdatesAllowed) {
2744
- throwInvalidWriteToSignalError();
2745
- }
2746
- // Mutate bypasses equality checks as it's by definition changing the value.
2747
- mutator(this.value);
2748
- this.valueVersion++;
2749
- this.producerMayHaveChanged();
2750
- postSignalSetFn?.();
2751
- }
2752
- asReadonly() {
2753
- if (this.readonlySignal === undefined) {
2754
- this.readonlySignal = createSignalFromFunction(this, () => this.signal());
2755
- }
2756
- return this.readonlySignal;
2757
- }
2758
- signal() {
2759
- this.producerAccessed();
2760
- return this.value;
2761
- }
2762
- }
2763
2686
  /**
2764
2687
  * Create a `Signal` that can be set or updated directly.
2765
2688
  *
2766
2689
  * @developerPreview
2767
2690
  */
2768
2691
  function signal(initialValue, options) {
2769
- const signalNode = new WritableSignalImpl(initialValue, options?.equal ?? defaultEquals);
2770
- // Casting here is required for g3, as TS inference behavior is slightly different between our
2771
- // version/options and g3's.
2772
- const signalFn = createSignalFromFunction(signalNode, signalNode.signal.bind(signalNode), {
2773
- set: signalNode.set.bind(signalNode),
2774
- update: signalNode.update.bind(signalNode),
2775
- mutate: signalNode.mutate.bind(signalNode),
2776
- asReadonly: signalNode.asReadonly.bind(signalNode)
2777
- });
2692
+ const node = Object.create(SIGNAL_NODE);
2693
+ node.value = initialValue;
2694
+ options?.equal && (node.equal = options.equal);
2695
+ function signalFn() {
2696
+ producerAccessed(node);
2697
+ return node.value;
2698
+ }
2699
+ signalFn.set = signalSetFn;
2700
+ signalFn.update = signalUpdateFn;
2701
+ signalFn.mutate = signalMutateFn;
2702
+ signalFn.asReadonly = signalAsReadonlyFn;
2703
+ signalFn[SIGNAL] = node;
2778
2704
  return signalFn;
2779
2705
  }
2780
2706
  function setPostSignalSetFn(fn) {
@@ -2782,6 +2708,50 @@ function setPostSignalSetFn(fn) {
2782
2708
  postSignalSetFn = fn;
2783
2709
  return prev;
2784
2710
  }
2711
+ const SIGNAL_NODE = {
2712
+ ...REACTIVE_NODE,
2713
+ equal: defaultEquals,
2714
+ readonlyFn: undefined,
2715
+ };
2716
+ function signalValueChanged(node) {
2717
+ node.version++;
2718
+ producerNotifyConsumers(node);
2719
+ postSignalSetFn?.();
2720
+ }
2721
+ function signalSetFn(newValue) {
2722
+ const node = this[SIGNAL];
2723
+ if (!producerUpdatesAllowed()) {
2724
+ throwInvalidWriteToSignalError();
2725
+ }
2726
+ if (!node.equal(node.value, newValue)) {
2727
+ node.value = newValue;
2728
+ signalValueChanged(node);
2729
+ }
2730
+ }
2731
+ function signalUpdateFn(updater) {
2732
+ if (!producerUpdatesAllowed()) {
2733
+ throwInvalidWriteToSignalError();
2734
+ }
2735
+ signalSetFn.call(this, updater(this[SIGNAL].value));
2736
+ }
2737
+ function signalMutateFn(mutator) {
2738
+ const node = this[SIGNAL];
2739
+ if (!producerUpdatesAllowed()) {
2740
+ throwInvalidWriteToSignalError();
2741
+ }
2742
+ // Mutate bypasses equality checks as it's by definition changing the value.
2743
+ mutator(node.value);
2744
+ signalValueChanged(node);
2745
+ }
2746
+ function signalAsReadonlyFn() {
2747
+ const node = this[SIGNAL];
2748
+ if (node.readonlyFn === undefined) {
2749
+ const readonlyFn = () => this();
2750
+ readonlyFn[SIGNAL] = node;
2751
+ node.readonlyFn = readonlyFn;
2752
+ }
2753
+ return node.readonlyFn;
2754
+ }
2785
2755
 
2786
2756
  /**
2787
2757
  * Execute an arbitrary function in a non-reactive (non-tracking) context. The executed function
@@ -2801,63 +2771,53 @@ function untracked(nonReactiveReadsFn) {
2801
2771
  }
2802
2772
  }
2803
2773
 
2804
- const NOOP_CLEANUP_FN = () => { };
2805
- /**
2806
- * Watches a reactive expression and allows it to be scheduled to re-run
2807
- * when any dependencies notify of a change.
2808
- *
2809
- * `Watch` doesn't run reactive expressions itself, but relies on a consumer-
2810
- * provided scheduling operation to coordinate calling `Watch.run()`.
2811
- */
2812
- class Watch extends ReactiveNode {
2813
- constructor(watch, schedule, allowSignalWrites) {
2814
- super();
2815
- this.watch = watch;
2816
- this.schedule = schedule;
2817
- this.dirty = false;
2818
- this.cleanupFn = NOOP_CLEANUP_FN;
2819
- this.registerOnCleanup = (cleanupFn) => {
2820
- this.cleanupFn = cleanupFn;
2821
- };
2822
- this.consumerAllowSignalWrites = allowSignalWrites;
2823
- }
2824
- notify() {
2825
- if (!this.dirty) {
2826
- this.schedule(this);
2827
- }
2828
- this.dirty = true;
2829
- }
2830
- onConsumerDependencyMayHaveChanged() {
2831
- this.notify();
2774
+ function watch(fn, schedule, allowSignalWrites) {
2775
+ const node = Object.create(WATCH_NODE);
2776
+ if (allowSignalWrites) {
2777
+ node.consumerAllowSignalWrites = true;
2832
2778
  }
2833
- onProducerUpdateValueVersion() {
2834
- // Watches are not producers.
2835
- }
2836
- /**
2837
- * Execute the reactive expression in the context of this `Watch` consumer.
2838
- *
2839
- * Should be called by the user scheduling algorithm when the provided
2840
- * `schedule` hook is called by `Watch`.
2841
- */
2842
- run() {
2843
- this.dirty = false;
2844
- if (this.trackingVersion !== 0 && !this.consumerPollProducersForChange()) {
2779
+ node.fn = fn;
2780
+ node.schedule = schedule;
2781
+ const registerOnCleanup = (cleanupFn) => {
2782
+ node.cleanupFn = cleanupFn;
2783
+ };
2784
+ const run = () => {
2785
+ node.dirty = false;
2786
+ if (node.hasRun && !consumerPollProducersForChange(node)) {
2845
2787
  return;
2846
2788
  }
2847
- const prevConsumer = setActiveConsumer(this);
2848
- this.trackingVersion++;
2789
+ node.hasRun = true;
2790
+ const prevConsumer = consumerBeforeComputation(node);
2849
2791
  try {
2850
- this.cleanupFn();
2851
- this.cleanupFn = NOOP_CLEANUP_FN;
2852
- this.watch(this.registerOnCleanup);
2792
+ node.cleanupFn();
2793
+ node.cleanupFn = NOOP_CLEANUP_FN;
2794
+ node.fn(registerOnCleanup);
2853
2795
  }
2854
2796
  finally {
2855
- setActiveConsumer(prevConsumer);
2797
+ consumerAfterComputation(node, prevConsumer);
2856
2798
  }
2857
- }
2858
- cleanup() {
2859
- this.cleanupFn();
2860
- }
2799
+ };
2800
+ node.ref = {
2801
+ notify: () => consumerMarkDirty(node),
2802
+ run,
2803
+ cleanup: () => node.cleanupFn(),
2804
+ };
2805
+ return node.ref;
2806
+ }
2807
+ const NOOP_CLEANUP_FN = () => { };
2808
+ const WATCH_NODE = {
2809
+ ...REACTIVE_NODE,
2810
+ consumerIsAlwaysLive: true,
2811
+ consumerAllowSignalWrites: false,
2812
+ consumerMarkedDirty: (node) => {
2813
+ node.schedule(node.ref);
2814
+ },
2815
+ hasRun: false,
2816
+ cleanupFn: NOOP_CLEANUP_FN,
2817
+ };
2818
+
2819
+ function setAlternateWeakRefImpl(impl) {
2820
+ // TODO: remove this function
2861
2821
  }
2862
2822
 
2863
2823
  /**
@@ -7153,8 +7113,8 @@ function detachView(lContainer, removeIndex) {
7153
7113
  function destroyLView(tView, lView) {
7154
7114
  if (!(lView[FLAGS] & 256 /* LViewFlags.Destroyed */)) {
7155
7115
  const renderer = lView[RENDERER];
7156
- lView[REACTIVE_TEMPLATE_CONSUMER]?.destroy();
7157
- lView[REACTIVE_HOST_BINDING_CONSUMER]?.destroy();
7116
+ lView[REACTIVE_TEMPLATE_CONSUMER] && consumerDestroy(lView[REACTIVE_TEMPLATE_CONSUMER]);
7117
+ lView[REACTIVE_HOST_BINDING_CONSUMER] && consumerDestroy(lView[REACTIVE_HOST_BINDING_CONSUMER]);
7158
7118
  if (renderer.destroyNode) {
7159
7119
  applyView(tView, lView, renderer, 3 /* WalkTNodeTreeAction.Destroy */, null, null);
7160
7120
  }
@@ -10296,7 +10256,7 @@ class Version {
10296
10256
  /**
10297
10257
  * @publicApi
10298
10258
  */
10299
- const VERSION = new Version('16.2.3');
10259
+ const VERSION = new Version('16.2.4');
10300
10260
 
10301
10261
  // This default value is when checking the hierarchy for a token.
10302
10262
  //
@@ -11000,6 +10960,9 @@ function forkInnerZoneWithAngularBehavior(zone) {
11000
10960
  name: 'angular',
11001
10961
  properties: { 'isAngularZone': true },
11002
10962
  onInvokeTask: (delegate, current, target, task, applyThis, applyArgs) => {
10963
+ if (shouldBeIgnoredByZone(applyArgs)) {
10964
+ return delegate.invokeTask(target, task, applyThis, applyArgs);
10965
+ }
11003
10966
  try {
11004
10967
  onEnter(zone);
11005
10968
  return delegate.invokeTask(target, task, applyThis, applyArgs);
@@ -11151,6 +11114,18 @@ function isStableFactory() {
11151
11114
  });
11152
11115
  return merge$1(isCurrentlyStable, isStable.pipe(share()));
11153
11116
  }
11117
+ function shouldBeIgnoredByZone(applyArgs) {
11118
+ if (!Array.isArray(applyArgs)) {
11119
+ return false;
11120
+ }
11121
+ // We should only ever get 1 arg passed through to invokeTask.
11122
+ // Short circuit here incase that behavior changes.
11123
+ if (applyArgs.length !== 1) {
11124
+ return false;
11125
+ }
11126
+ // Prevent triggering change detection when the __ignore_ng_zone__ flag is detected.
11127
+ return applyArgs[0].data?.['__ignore_ng_zone__'] === true;
11128
+ }
11154
11129
 
11155
11130
  // Public API for Zone
11156
11131
 
@@ -11637,48 +11612,11 @@ function getExpressionChangedErrorDetails(lView, bindingIndex, oldValue, newValu
11637
11612
  return { propName: undefined, oldValue, newValue };
11638
11613
  }
11639
11614
 
11640
- class ReactiveLViewConsumer extends ReactiveNode {
11641
- constructor() {
11642
- super(...arguments);
11643
- this.consumerAllowSignalWrites = false;
11644
- this._lView = null;
11645
- }
11646
- set lView(lView) {
11647
- (typeof ngDevMode === 'undefined' || ngDevMode) &&
11648
- assertEqual(this._lView, null, 'Consumer already associated with a view.');
11649
- this._lView = lView;
11650
- }
11651
- onConsumerDependencyMayHaveChanged() {
11652
- (typeof ngDevMode === 'undefined' || ngDevMode) &&
11653
- assertDefined(this._lView, 'Updating a signal during template or host binding execution is not allowed.');
11654
- markViewDirty(this._lView);
11655
- }
11656
- onProducerUpdateValueVersion() {
11657
- // This type doesn't implement the producer side of a `ReactiveNode`.
11658
- }
11659
- get hasReadASignal() {
11660
- return this.hasProducers;
11661
- }
11662
- runInContext(fn, rf, ctx) {
11663
- const prevConsumer = setActiveConsumer(this);
11664
- this.trackingVersion++;
11665
- try {
11666
- fn(rf, ctx);
11667
- }
11668
- finally {
11669
- setActiveConsumer(prevConsumer);
11670
- }
11671
- }
11672
- destroy() {
11673
- // Incrementing the version means that every producer which tries to update this consumer will
11674
- // consider its record stale, and not notify.
11675
- this.trackingVersion++;
11676
- }
11677
- }
11678
11615
  let currentConsumer = null;
11679
- function getOrCreateCurrentLViewConsumer() {
11680
- currentConsumer ??= new ReactiveLViewConsumer();
11681
- return currentConsumer;
11616
+ function setLViewForConsumer(node, lView) {
11617
+ (typeof ngDevMode === 'undefined' || ngDevMode) &&
11618
+ assertEqual(node.lView, null, 'Consumer already associated with a view.');
11619
+ node.lView = lView;
11682
11620
  }
11683
11621
  /**
11684
11622
  * Create a new template consumer pointing at the specified LView.
@@ -11700,12 +11638,29 @@ function getReactiveLViewConsumer(lView, slot) {
11700
11638
  */
11701
11639
  function commitLViewConsumerIfHasProducers(lView, slot) {
11702
11640
  const consumer = getOrCreateCurrentLViewConsumer();
11703
- if (!consumer.hasReadASignal) {
11641
+ if (!consumer.producerNode?.length) {
11704
11642
  return;
11705
11643
  }
11706
11644
  lView[slot] = currentConsumer;
11707
11645
  consumer.lView = lView;
11708
- currentConsumer = new ReactiveLViewConsumer();
11646
+ currentConsumer = createLViewConsumer();
11647
+ }
11648
+ const REACTIVE_LVIEW_CONSUMER_NODE = {
11649
+ ...REACTIVE_NODE,
11650
+ consumerIsAlwaysLive: true,
11651
+ consumerMarkedDirty: (node) => {
11652
+ (typeof ngDevMode === 'undefined' || ngDevMode) &&
11653
+ assertDefined(node.lView, 'Updating a signal during template or host binding execution is not allowed.');
11654
+ markViewDirty(node.lView);
11655
+ },
11656
+ lView: null,
11657
+ };
11658
+ function createLViewConsumer() {
11659
+ return Object.create(REACTIVE_LVIEW_CONSUMER_NODE);
11660
+ }
11661
+ function getOrCreateCurrentLViewConsumer() {
11662
+ currentConsumer ??= createLViewConsumer();
11663
+ return currentConsumer;
11709
11664
  }
11710
11665
 
11711
11666
  /** A special value which designates that a value has not changed. */
@@ -11822,8 +11777,15 @@ function processHostBindingOpCodes(tView, lView) {
11822
11777
  const bindingRootIndx = hostBindingOpCodes[++i];
11823
11778
  const hostBindingFn = hostBindingOpCodes[++i];
11824
11779
  setBindingRootForHostBindings(bindingRootIndx, directiveIdx);
11825
- const context = lView[directiveIdx];
11826
- consumer.runInContext(hostBindingFn, 2 /* RenderFlags.Update */, context);
11780
+ consumer.dirty = false;
11781
+ const prevConsumer = consumerBeforeComputation(consumer);
11782
+ try {
11783
+ const context = lView[directiveIdx];
11784
+ hostBindingFn(2 /* RenderFlags.Update */, context);
11785
+ }
11786
+ finally {
11787
+ consumerAfterComputation(consumer, prevConsumer);
11788
+ }
11827
11789
  }
11828
11790
  }
11829
11791
  }
@@ -11963,17 +11925,16 @@ function executeTemplate(tView, lView, templateFn, rf, context) {
11963
11925
  }
11964
11926
  const preHookType = isUpdatePhase ? 2 /* ProfilerEvent.TemplateUpdateStart */ : 0 /* ProfilerEvent.TemplateCreateStart */;
11965
11927
  profiler(preHookType, context);
11966
- if (isUpdatePhase) {
11967
- consumer.runInContext(templateFn, rf, context);
11968
- }
11969
- else {
11970
- const prevConsumer = setActiveConsumer(null);
11971
- try {
11972
- templateFn(rf, context);
11973
- }
11974
- finally {
11975
- setActiveConsumer(prevConsumer);
11928
+ const effectiveConsumer = isUpdatePhase ? consumer : null;
11929
+ const prevConsumer = consumerBeforeComputation(effectiveConsumer);
11930
+ try {
11931
+ if (effectiveConsumer !== null) {
11932
+ effectiveConsumer.dirty = false;
11976
11933
  }
11934
+ templateFn(rf, context);
11935
+ }
11936
+ finally {
11937
+ consumerAfterComputation(effectiveConsumer, prevConsumer);
11977
11938
  }
11978
11939
  }
11979
11940
  finally {
@@ -13227,21 +13188,21 @@ class EffectManager {
13227
13188
  }
13228
13189
  create(effectFn, destroyRef, allowSignalWrites) {
13229
13190
  const zone = (typeof Zone === 'undefined') ? null : Zone.current;
13230
- const watch = new Watch(effectFn, (watch) => {
13191
+ const w = watch(effectFn, (watch) => {
13231
13192
  if (!this.all.has(watch)) {
13232
13193
  return;
13233
13194
  }
13234
13195
  this.queue.set(watch, zone);
13235
13196
  }, allowSignalWrites);
13236
- this.all.add(watch);
13197
+ this.all.add(w);
13237
13198
  // Effects start dirty.
13238
- watch.notify();
13199
+ w.notify();
13239
13200
  let unregisterOnDestroy;
13240
13201
  const destroy = () => {
13241
- watch.cleanup();
13202
+ w.cleanup();
13242
13203
  unregisterOnDestroy?.();
13243
- this.all.delete(watch);
13244
- this.queue.delete(watch);
13204
+ this.all.delete(w);
13205
+ this.queue.delete(w);
13245
13206
  };
13246
13207
  unregisterOnDestroy = destroyRef?.onDestroy(destroy);
13247
13208
  return {
@@ -13625,15 +13586,15 @@ function detectChangesInView(lView, mode) {
13625
13586
  return;
13626
13587
  }
13627
13588
  const tView = lView[TVIEW];
13628
- if ((lView[FLAGS] & (16 /* LViewFlags.CheckAlways */ | 64 /* LViewFlags.Dirty */) &&
13589
+ const flags = lView[FLAGS];
13590
+ if ((flags & (16 /* LViewFlags.CheckAlways */ | 64 /* LViewFlags.Dirty */) &&
13629
13591
  mode === 0 /* ChangeDetectionMode.Global */) ||
13630
- lView[FLAGS] & 1024 /* LViewFlags.RefreshView */ ||
13592
+ flags & 1024 /* LViewFlags.RefreshView */ ||
13631
13593
  mode === 2 /* ChangeDetectionMode.BugToForceRefreshAndIgnoreViewFlags */) {
13632
13594
  refreshView(tView, lView, tView.template, lView[CONTEXT]);
13633
13595
  }
13634
13596
  else if (lView[DESCENDANT_VIEWS_TO_REFRESH] > 0) {
13635
13597
  detectChangesInEmbeddedViews(lView, 1 /* ChangeDetectionMode.Targeted */);
13636
- const tView = lView[TVIEW];
13637
13598
  const components = tView.components;
13638
13599
  if (components !== null) {
13639
13600
  detectChangesInChildComponents(lView, components, 1 /* ChangeDetectionMode.Targeted */);
@@ -17409,14 +17370,12 @@ function getTStylingRangePrev(tStylingRange) {
17409
17370
  }
17410
17371
  function getTStylingRangePrevDuplicate(tStylingRange) {
17411
17372
  ngDevMode && assertNumber(tStylingRange, 'expected number');
17412
- return (tStylingRange & 2 /* StylingRange.PREV_DUPLICATE */) ==
17413
- 2 /* StylingRange.PREV_DUPLICATE */;
17373
+ return (tStylingRange & 2 /* StylingRange.PREV_DUPLICATE */) == 2 /* StylingRange.PREV_DUPLICATE */;
17414
17374
  }
17415
17375
  function setTStylingRangePrev(tStylingRange, previous) {
17416
17376
  ngDevMode && assertNumber(tStylingRange, 'expected number');
17417
17377
  ngDevMode && assertNumberInRange(previous, 0, 32767 /* StylingRange.UNSIGNED_MASK */);
17418
- return ((tStylingRange & ~4294836224 /* StylingRange.PREV_MASK */) |
17419
- (previous << 17 /* StylingRange.PREV_SHIFT */));
17378
+ return ((tStylingRange & ~4294836224 /* StylingRange.PREV_MASK */) | (previous << 17 /* StylingRange.PREV_SHIFT */));
17420
17379
  }
17421
17380
  function setTStylingRangePrevDuplicate(tStylingRange) {
17422
17381
  ngDevMode && assertNumber(tStylingRange, 'expected number');
@@ -17434,8 +17393,7 @@ function setTStylingRangeNext(tStylingRange, next) {
17434
17393
  }
17435
17394
  function getTStylingRangeNextDuplicate(tStylingRange) {
17436
17395
  ngDevMode && assertNumber(tStylingRange, 'expected number');
17437
- return (tStylingRange & 1 /* StylingRange.NEXT_DUPLICATE */) ===
17438
- 1 /* StylingRange.NEXT_DUPLICATE */;
17396
+ return ((tStylingRange) & 1 /* StylingRange.NEXT_DUPLICATE */) === 1 /* StylingRange.NEXT_DUPLICATE */;
17439
17397
  }
17440
17398
  function setTStylingRangeNextDuplicate(tStylingRange) {
17441
17399
  ngDevMode && assertNumber(tStylingRange, 'expected number');
@@ -24233,6 +24191,40 @@ function findMatchingDehydratedView(lContainer, template) {
24233
24191
  * A view container instance can contain other view containers,
24234
24192
  * creating a [view hierarchy](guide/glossary#view-hierarchy).
24235
24193
  *
24194
+ * @usageNotes
24195
+ *
24196
+ * The example below demonstrates how the `createComponent` function can be used
24197
+ * to create an instance of a ComponentRef dynamically and attach it to an ApplicationRef,
24198
+ * so that it gets included into change detection cycles.
24199
+ *
24200
+ * Note: the example uses standalone components, but the function can also be used for
24201
+ * non-standalone components (declared in an NgModule) as well.
24202
+ *
24203
+ * ```typescript
24204
+ * @Component({
24205
+ * standalone: true,
24206
+ * selector: 'dynamic',
24207
+ * template: `<span>This is a content of a dynamic component.</span>`,
24208
+ * })
24209
+ * class DynamicComponent {
24210
+ * vcr = inject(ViewContainerRef);
24211
+ * }
24212
+ *
24213
+ * @Component({
24214
+ * standalone: true,
24215
+ * selector: 'app',
24216
+ * template: `<main>Hi! This is the main content.</main>`,
24217
+ * })
24218
+ * class AppComponent {
24219
+ * vcr = inject(ViewContainerRef);
24220
+ *
24221
+ * ngAfterViewInit() {
24222
+ * const compRef = this.vcr.createComponent(DynamicComponent);
24223
+ * compRef.changeDetectorRef.detectChanges();
24224
+ * }
24225
+ * }
24226
+ * ```
24227
+ *
24236
24228
  * @see {@link ComponentRef}
24237
24229
  * @see {@link EmbeddedViewRef}
24238
24230
  *
@@ -31536,17 +31528,18 @@ function ɵɵngDeclarePipe(decl) {
31536
31528
  * const applicationRef = await bootstrapApplication(RootComponent);
31537
31529
  *
31538
31530
  * // Locate a DOM node that would be used as a host.
31539
- * const host = document.getElementById('hello-component-host');
31531
+ * const hostElement = document.getElementById('hello-component-host');
31540
31532
  *
31541
31533
  * // Get an `EnvironmentInjector` instance from the `ApplicationRef`.
31542
31534
  * const environmentInjector = applicationRef.injector;
31543
31535
  *
31544
31536
  * // We can now create a `ComponentRef` instance.
31545
- * const componentRef = createComponent(HelloComponent, {host, environmentInjector});
31537
+ * const componentRef = createComponent(HelloComponent, {hostElement, environmentInjector});
31546
31538
  *
31547
31539
  * // Last step is to register the newly created ref using the `ApplicationRef` instance
31548
31540
  * // to include the component view into change detection cycles.
31549
31541
  * applicationRef.attachView(componentRef.hostView);
31542
+ * componentRef.changeDetectorRef.detectChanges();
31550
31543
  * ```
31551
31544
  *
31552
31545
  * @param component Component class reference.