@angular/core 16.2.3 → 16.2.5
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/after_render_hooks.mjs +83 -49
- 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 +616 -591
- 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 +614 -590
- package/fesm2022/testing.mjs.map +1 -1
- package/index.d.ts +117 -121
- 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/core.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v16.2.
|
|
2
|
+
* @license Angular v16.2.5
|
|
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
|
-
*
|
|
2336
|
-
*
|
|
2326
|
+
* The currently active consumer `ReactiveNode`, if running code in a reactive context.
|
|
2327
|
+
*
|
|
2328
|
+
* Change this via `setActiveConsumer`.
|
|
2337
2329
|
*/
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
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.
|
|
2354
|
+
*/
|
|
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
|
-
|
|
2343
|
-
|
|
2361
|
+
if (activeConsumer === null) {
|
|
2362
|
+
// Accessed outside of a reactive context, so nothing to record.
|
|
2363
|
+
return;
|
|
2344
2364
|
}
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
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
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
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
|
-
*
|
|
2394
|
+
* Ensure this producer's `version` is up-to-date.
|
|
2364
2395
|
*/
|
|
2365
|
-
|
|
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
|
-
*
|
|
2368
|
-
* consumer).
|
|
2413
|
+
* Propagate a dirty notification to live consumers of this producer.
|
|
2369
2414
|
*/
|
|
2370
|
-
|
|
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
|
|
2434
|
+
* Whether this `ReactiveNode` in its producer capacity is currently allowed to initiate updates,
|
|
2435
|
+
* based on the current consumer context.
|
|
2373
2436
|
*/
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
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
|
-
*
|
|
2382
|
-
*
|
|
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.
|
|
2446
|
+
* Prepare this consumer to run a computation in its reactive context.
|
|
2393
2447
|
*
|
|
2394
|
-
*
|
|
2395
|
-
*
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
*
|
|
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
|
-
*
|
|
2406
|
-
*
|
|
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
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
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
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
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
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
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
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
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
|
-
//
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
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
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
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
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
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
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
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
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
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 =
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
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
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
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 =
|
|
2640
|
-
|
|
2641
|
-
|
|
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 =
|
|
2647
|
+
newValue = node.computation();
|
|
2647
2648
|
}
|
|
2648
2649
|
catch (err) {
|
|
2649
2650
|
newValue = ERRORED;
|
|
2650
|
-
|
|
2651
|
+
node.error = err;
|
|
2651
2652
|
}
|
|
2652
2653
|
finally {
|
|
2653
|
-
|
|
2654
|
+
consumerAfterComputation(node, prevConsumer);
|
|
2654
2655
|
}
|
|
2655
|
-
this.stale = false;
|
|
2656
2656
|
if (oldValue !== UNSET && oldValue !== ERRORED && newValue !== ERRORED &&
|
|
2657
|
-
|
|
2657
|
+
node.equal(oldValue, newValue)) {
|
|
2658
2658
|
// No change to `valueVersion` - old and new values are
|
|
2659
2659
|
// semantically equivalent.
|
|
2660
|
-
|
|
2660
|
+
node.value = oldValue;
|
|
2661
2661
|
return;
|
|
2662
2662
|
}
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
}
|
|
2666
|
-
|
|
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
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
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
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
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
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
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
|
-
|
|
2848
|
-
|
|
2789
|
+
node.hasRun = true;
|
|
2790
|
+
const prevConsumer = consumerBeforeComputation(node);
|
|
2849
2791
|
try {
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2792
|
+
node.cleanupFn();
|
|
2793
|
+
node.cleanupFn = NOOP_CLEANUP_FN;
|
|
2794
|
+
node.fn(registerOnCleanup);
|
|
2853
2795
|
}
|
|
2854
2796
|
finally {
|
|
2855
|
-
|
|
2797
|
+
consumerAfterComputation(node, prevConsumer);
|
|
2856
2798
|
}
|
|
2857
|
-
}
|
|
2858
|
-
|
|
2859
|
-
|
|
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]
|
|
7157
|
-
lView[REACTIVE_HOST_BINDING_CONSUMER]
|
|
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.
|
|
10259
|
+
const VERSION = new Version('16.2.5');
|
|
10300
10260
|
|
|
10301
10261
|
// This default value is when checking the hierarchy for a token.
|
|
10302
10262
|
//
|
|
@@ -10564,6 +10524,66 @@ class Injector {
|
|
|
10564
10524
|
* safe to delete this file.
|
|
10565
10525
|
*/
|
|
10566
10526
|
|
|
10527
|
+
const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
|
|
10528
|
+
function wrappedError(message, originalError) {
|
|
10529
|
+
const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
|
|
10530
|
+
const error = Error(msg);
|
|
10531
|
+
error[ERROR_ORIGINAL_ERROR] = originalError;
|
|
10532
|
+
return error;
|
|
10533
|
+
}
|
|
10534
|
+
function getOriginalError(error) {
|
|
10535
|
+
return error[ERROR_ORIGINAL_ERROR];
|
|
10536
|
+
}
|
|
10537
|
+
|
|
10538
|
+
/**
|
|
10539
|
+
* Provides a hook for centralized exception handling.
|
|
10540
|
+
*
|
|
10541
|
+
* The default implementation of `ErrorHandler` prints error messages to the `console`. To
|
|
10542
|
+
* intercept error handling, write a custom exception handler that replaces this default as
|
|
10543
|
+
* appropriate for your app.
|
|
10544
|
+
*
|
|
10545
|
+
* @usageNotes
|
|
10546
|
+
* ### Example
|
|
10547
|
+
*
|
|
10548
|
+
* ```
|
|
10549
|
+
* class MyErrorHandler implements ErrorHandler {
|
|
10550
|
+
* handleError(error) {
|
|
10551
|
+
* // do something with the exception
|
|
10552
|
+
* }
|
|
10553
|
+
* }
|
|
10554
|
+
*
|
|
10555
|
+
* @NgModule({
|
|
10556
|
+
* providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
|
|
10557
|
+
* })
|
|
10558
|
+
* class MyModule {}
|
|
10559
|
+
* ```
|
|
10560
|
+
*
|
|
10561
|
+
* @publicApi
|
|
10562
|
+
*/
|
|
10563
|
+
class ErrorHandler {
|
|
10564
|
+
constructor() {
|
|
10565
|
+
/**
|
|
10566
|
+
* @internal
|
|
10567
|
+
*/
|
|
10568
|
+
this._console = console;
|
|
10569
|
+
}
|
|
10570
|
+
handleError(error) {
|
|
10571
|
+
const originalError = this._findOriginalError(error);
|
|
10572
|
+
this._console.error('ERROR', error);
|
|
10573
|
+
if (originalError) {
|
|
10574
|
+
this._console.error('ORIGINAL ERROR', originalError);
|
|
10575
|
+
}
|
|
10576
|
+
}
|
|
10577
|
+
/** @internal */
|
|
10578
|
+
_findOriginalError(error) {
|
|
10579
|
+
let e = error && getOriginalError(error);
|
|
10580
|
+
while (e && getOriginalError(e)) {
|
|
10581
|
+
e = getOriginalError(e);
|
|
10582
|
+
}
|
|
10583
|
+
return e || null;
|
|
10584
|
+
}
|
|
10585
|
+
}
|
|
10586
|
+
|
|
10567
10587
|
/**
|
|
10568
10588
|
* `DestroyRef` lets you set callbacks to run for any cleanup or destruction behavior.
|
|
10569
10589
|
* The scope of this destruction depends on where `DestroyRef` is injected. If `DestroyRef`
|
|
@@ -11000,6 +11020,9 @@ function forkInnerZoneWithAngularBehavior(zone) {
|
|
|
11000
11020
|
name: 'angular',
|
|
11001
11021
|
properties: { 'isAngularZone': true },
|
|
11002
11022
|
onInvokeTask: (delegate, current, target, task, applyThis, applyArgs) => {
|
|
11023
|
+
if (shouldBeIgnoredByZone(applyArgs)) {
|
|
11024
|
+
return delegate.invokeTask(target, task, applyThis, applyArgs);
|
|
11025
|
+
}
|
|
11003
11026
|
try {
|
|
11004
11027
|
onEnter(zone);
|
|
11005
11028
|
return delegate.invokeTask(target, task, applyThis, applyArgs);
|
|
@@ -11151,6 +11174,18 @@ function isStableFactory() {
|
|
|
11151
11174
|
});
|
|
11152
11175
|
return merge$1(isCurrentlyStable, isStable.pipe(share()));
|
|
11153
11176
|
}
|
|
11177
|
+
function shouldBeIgnoredByZone(applyArgs) {
|
|
11178
|
+
if (!Array.isArray(applyArgs)) {
|
|
11179
|
+
return false;
|
|
11180
|
+
}
|
|
11181
|
+
// We should only ever get 1 arg passed through to invokeTask.
|
|
11182
|
+
// Short circuit here incase that behavior changes.
|
|
11183
|
+
if (applyArgs.length !== 1) {
|
|
11184
|
+
return false;
|
|
11185
|
+
}
|
|
11186
|
+
// Prevent triggering change detection when the __ignore_ng_zone__ flag is detected.
|
|
11187
|
+
return applyArgs[0].data?.['__ignore_ng_zone__'] === true;
|
|
11188
|
+
}
|
|
11154
11189
|
|
|
11155
11190
|
// Public API for Zone
|
|
11156
11191
|
|
|
@@ -11259,14 +11294,18 @@ function afterRender(callback, options) {
|
|
|
11259
11294
|
}
|
|
11260
11295
|
let destroy;
|
|
11261
11296
|
const unregisterFn = injector.get(DestroyRef).onDestroy(() => destroy?.());
|
|
11262
|
-
const
|
|
11297
|
+
const afterRenderEventManager = injector.get(AfterRenderEventManager);
|
|
11298
|
+
// Lazily initialize the handler implementation, if necessary. This is so that it can be
|
|
11299
|
+
// tree-shaken if `afterRender` and `afterNextRender` aren't used.
|
|
11300
|
+
const callbackHandler = afterRenderEventManager.handler ??= new AfterRenderCallbackHandlerImpl();
|
|
11263
11301
|
const ngZone = injector.get(NgZone);
|
|
11264
|
-
const
|
|
11302
|
+
const errorHandler = injector.get(ErrorHandler, null, { optional: true });
|
|
11303
|
+
const instance = new AfterRenderCallback(ngZone, errorHandler, callback);
|
|
11265
11304
|
destroy = () => {
|
|
11266
|
-
|
|
11305
|
+
callbackHandler.unregister(instance);
|
|
11267
11306
|
unregisterFn();
|
|
11268
11307
|
};
|
|
11269
|
-
|
|
11308
|
+
callbackHandler.register(instance);
|
|
11270
11309
|
return { destroy };
|
|
11271
11310
|
}
|
|
11272
11311
|
/**
|
|
@@ -11319,89 +11358,117 @@ function afterNextRender(callback, options) {
|
|
|
11319
11358
|
}
|
|
11320
11359
|
let destroy;
|
|
11321
11360
|
const unregisterFn = injector.get(DestroyRef).onDestroy(() => destroy?.());
|
|
11322
|
-
const
|
|
11361
|
+
const afterRenderEventManager = injector.get(AfterRenderEventManager);
|
|
11362
|
+
// Lazily initialize the handler implementation, if necessary. This is so that it can be
|
|
11363
|
+
// tree-shaken if `afterRender` and `afterNextRender` aren't used.
|
|
11364
|
+
const callbackHandler = afterRenderEventManager.handler ??= new AfterRenderCallbackHandlerImpl();
|
|
11323
11365
|
const ngZone = injector.get(NgZone);
|
|
11324
|
-
const
|
|
11366
|
+
const errorHandler = injector.get(ErrorHandler, null, { optional: true });
|
|
11367
|
+
const instance = new AfterRenderCallback(ngZone, errorHandler, () => {
|
|
11325
11368
|
destroy?.();
|
|
11326
|
-
|
|
11369
|
+
callback();
|
|
11327
11370
|
});
|
|
11328
11371
|
destroy = () => {
|
|
11329
|
-
|
|
11372
|
+
callbackHandler.unregister(instance);
|
|
11330
11373
|
unregisterFn();
|
|
11331
11374
|
};
|
|
11332
|
-
|
|
11375
|
+
callbackHandler.register(instance);
|
|
11333
11376
|
return { destroy };
|
|
11334
11377
|
}
|
|
11335
11378
|
/**
|
|
11336
11379
|
* A wrapper around a function to be used as an after render callback.
|
|
11337
|
-
* @private
|
|
11338
11380
|
*/
|
|
11339
11381
|
class AfterRenderCallback {
|
|
11340
|
-
constructor(
|
|
11341
|
-
this.
|
|
11382
|
+
constructor(zone, errorHandler, callbackFn) {
|
|
11383
|
+
this.zone = zone;
|
|
11384
|
+
this.errorHandler = errorHandler;
|
|
11385
|
+
this.callbackFn = callbackFn;
|
|
11342
11386
|
}
|
|
11343
11387
|
invoke() {
|
|
11344
|
-
|
|
11388
|
+
try {
|
|
11389
|
+
this.zone.runOutsideAngular(this.callbackFn);
|
|
11390
|
+
}
|
|
11391
|
+
catch (err) {
|
|
11392
|
+
this.errorHandler?.handleError(err);
|
|
11393
|
+
}
|
|
11345
11394
|
}
|
|
11346
11395
|
}
|
|
11347
11396
|
/**
|
|
11348
|
-
*
|
|
11397
|
+
* Core functionality for `afterRender` and `afterNextRender`. Kept separate from
|
|
11398
|
+
* `AfterRenderEventManager` for tree-shaking.
|
|
11349
11399
|
*/
|
|
11350
|
-
class
|
|
11400
|
+
class AfterRenderCallbackHandlerImpl {
|
|
11351
11401
|
constructor() {
|
|
11402
|
+
this.executingCallbacks = false;
|
|
11352
11403
|
this.callbacks = new Set();
|
|
11353
11404
|
this.deferredCallbacks = new Set();
|
|
11354
|
-
this.renderDepth = 0;
|
|
11355
|
-
this.runningCallbacks = false;
|
|
11356
11405
|
}
|
|
11357
|
-
|
|
11358
|
-
|
|
11359
|
-
* Throws if called from an `afterRender` callback.
|
|
11360
|
-
*/
|
|
11361
|
-
begin() {
|
|
11362
|
-
if (this.runningCallbacks) {
|
|
11406
|
+
validateBegin() {
|
|
11407
|
+
if (this.executingCallbacks) {
|
|
11363
11408
|
throw new RuntimeError(102 /* RuntimeErrorCode.RECURSIVE_APPLICATION_RENDER */, ngDevMode &&
|
|
11364
11409
|
'A new render operation began before the previous operation ended. ' +
|
|
11365
11410
|
'Did you trigger change detection from afterRender or afterNextRender?');
|
|
11366
11411
|
}
|
|
11367
|
-
this.renderDepth++;
|
|
11368
|
-
}
|
|
11369
|
-
/**
|
|
11370
|
-
* Mark the end of a render operation. Registered callbacks
|
|
11371
|
-
* are invoked if there are no more pending operations.
|
|
11372
|
-
*/
|
|
11373
|
-
end() {
|
|
11374
|
-
this.renderDepth--;
|
|
11375
|
-
if (this.renderDepth === 0) {
|
|
11376
|
-
try {
|
|
11377
|
-
this.runningCallbacks = true;
|
|
11378
|
-
for (const callback of this.callbacks) {
|
|
11379
|
-
callback.invoke();
|
|
11380
|
-
}
|
|
11381
|
-
}
|
|
11382
|
-
finally {
|
|
11383
|
-
this.runningCallbacks = false;
|
|
11384
|
-
for (const callback of this.deferredCallbacks) {
|
|
11385
|
-
this.callbacks.add(callback);
|
|
11386
|
-
}
|
|
11387
|
-
this.deferredCallbacks.clear();
|
|
11388
|
-
}
|
|
11389
|
-
}
|
|
11390
11412
|
}
|
|
11391
11413
|
register(callback) {
|
|
11392
11414
|
// If we're currently running callbacks, new callbacks should be deferred
|
|
11393
11415
|
// until the next render operation.
|
|
11394
|
-
const target = this.
|
|
11416
|
+
const target = this.executingCallbacks ? this.deferredCallbacks : this.callbacks;
|
|
11395
11417
|
target.add(callback);
|
|
11396
11418
|
}
|
|
11397
11419
|
unregister(callback) {
|
|
11398
11420
|
this.callbacks.delete(callback);
|
|
11399
11421
|
this.deferredCallbacks.delete(callback);
|
|
11400
11422
|
}
|
|
11401
|
-
|
|
11423
|
+
execute() {
|
|
11424
|
+
this.executingCallbacks = true;
|
|
11425
|
+
for (const callback of this.callbacks) {
|
|
11426
|
+
callback.invoke();
|
|
11427
|
+
}
|
|
11428
|
+
this.executingCallbacks = false;
|
|
11429
|
+
for (const callback of this.deferredCallbacks) {
|
|
11430
|
+
this.callbacks.add(callback);
|
|
11431
|
+
}
|
|
11432
|
+
this.deferredCallbacks.clear();
|
|
11433
|
+
}
|
|
11434
|
+
destroy() {
|
|
11402
11435
|
this.callbacks.clear();
|
|
11403
11436
|
this.deferredCallbacks.clear();
|
|
11404
11437
|
}
|
|
11438
|
+
}
|
|
11439
|
+
/**
|
|
11440
|
+
* Implements core timing for `afterRender` and `afterNextRender` events.
|
|
11441
|
+
* Delegates to an optional `AfterRenderCallbackHandler` for implementation.
|
|
11442
|
+
*/
|
|
11443
|
+
class AfterRenderEventManager {
|
|
11444
|
+
constructor() {
|
|
11445
|
+
this.renderDepth = 0;
|
|
11446
|
+
/* @internal */
|
|
11447
|
+
this.handler = null;
|
|
11448
|
+
}
|
|
11449
|
+
/**
|
|
11450
|
+
* Mark the beginning of a render operation (i.e. CD cycle).
|
|
11451
|
+
* Throws if called while executing callbacks.
|
|
11452
|
+
*/
|
|
11453
|
+
begin() {
|
|
11454
|
+
this.handler?.validateBegin();
|
|
11455
|
+
this.renderDepth++;
|
|
11456
|
+
}
|
|
11457
|
+
/**
|
|
11458
|
+
* Mark the end of a render operation. Callbacks will be
|
|
11459
|
+
* executed if there are no more pending operations.
|
|
11460
|
+
*/
|
|
11461
|
+
end() {
|
|
11462
|
+
ngDevMode && assertGreaterThan(this.renderDepth, 0, 'renderDepth must be greater than 0');
|
|
11463
|
+
this.renderDepth--;
|
|
11464
|
+
if (this.renderDepth === 0) {
|
|
11465
|
+
this.handler?.execute();
|
|
11466
|
+
}
|
|
11467
|
+
}
|
|
11468
|
+
ngOnDestroy() {
|
|
11469
|
+
this.handler?.destroy();
|
|
11470
|
+
this.handler = null;
|
|
11471
|
+
}
|
|
11405
11472
|
/** @nocollapse */
|
|
11406
11473
|
static { this.ɵprov = ɵɵdefineInjectable({
|
|
11407
11474
|
token: AfterRenderEventManager,
|
|
@@ -11435,66 +11502,6 @@ function markViewDirty(lView) {
|
|
|
11435
11502
|
return null;
|
|
11436
11503
|
}
|
|
11437
11504
|
|
|
11438
|
-
const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
|
|
11439
|
-
function wrappedError(message, originalError) {
|
|
11440
|
-
const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
|
|
11441
|
-
const error = Error(msg);
|
|
11442
|
-
error[ERROR_ORIGINAL_ERROR] = originalError;
|
|
11443
|
-
return error;
|
|
11444
|
-
}
|
|
11445
|
-
function getOriginalError(error) {
|
|
11446
|
-
return error[ERROR_ORIGINAL_ERROR];
|
|
11447
|
-
}
|
|
11448
|
-
|
|
11449
|
-
/**
|
|
11450
|
-
* Provides a hook for centralized exception handling.
|
|
11451
|
-
*
|
|
11452
|
-
* The default implementation of `ErrorHandler` prints error messages to the `console`. To
|
|
11453
|
-
* intercept error handling, write a custom exception handler that replaces this default as
|
|
11454
|
-
* appropriate for your app.
|
|
11455
|
-
*
|
|
11456
|
-
* @usageNotes
|
|
11457
|
-
* ### Example
|
|
11458
|
-
*
|
|
11459
|
-
* ```
|
|
11460
|
-
* class MyErrorHandler implements ErrorHandler {
|
|
11461
|
-
* handleError(error) {
|
|
11462
|
-
* // do something with the exception
|
|
11463
|
-
* }
|
|
11464
|
-
* }
|
|
11465
|
-
*
|
|
11466
|
-
* @NgModule({
|
|
11467
|
-
* providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
|
|
11468
|
-
* })
|
|
11469
|
-
* class MyModule {}
|
|
11470
|
-
* ```
|
|
11471
|
-
*
|
|
11472
|
-
* @publicApi
|
|
11473
|
-
*/
|
|
11474
|
-
class ErrorHandler {
|
|
11475
|
-
constructor() {
|
|
11476
|
-
/**
|
|
11477
|
-
* @internal
|
|
11478
|
-
*/
|
|
11479
|
-
this._console = console;
|
|
11480
|
-
}
|
|
11481
|
-
handleError(error) {
|
|
11482
|
-
const originalError = this._findOriginalError(error);
|
|
11483
|
-
this._console.error('ERROR', error);
|
|
11484
|
-
if (originalError) {
|
|
11485
|
-
this._console.error('ORIGINAL ERROR', originalError);
|
|
11486
|
-
}
|
|
11487
|
-
}
|
|
11488
|
-
/** @internal */
|
|
11489
|
-
_findOriginalError(error) {
|
|
11490
|
-
let e = error && getOriginalError(error);
|
|
11491
|
-
while (e && getOriginalError(e)) {
|
|
11492
|
-
e = getOriginalError(e);
|
|
11493
|
-
}
|
|
11494
|
-
return e || null;
|
|
11495
|
-
}
|
|
11496
|
-
}
|
|
11497
|
-
|
|
11498
11505
|
/**
|
|
11499
11506
|
* Internal token that specifies whether DOM reuse logic
|
|
11500
11507
|
* during hydration is enabled.
|
|
@@ -11637,48 +11644,11 @@ function getExpressionChangedErrorDetails(lView, bindingIndex, oldValue, newValu
|
|
|
11637
11644
|
return { propName: undefined, oldValue, newValue };
|
|
11638
11645
|
}
|
|
11639
11646
|
|
|
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
11647
|
let currentConsumer = null;
|
|
11679
|
-
function
|
|
11680
|
-
|
|
11681
|
-
|
|
11648
|
+
function setLViewForConsumer(node, lView) {
|
|
11649
|
+
(typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
11650
|
+
assertEqual(node.lView, null, 'Consumer already associated with a view.');
|
|
11651
|
+
node.lView = lView;
|
|
11682
11652
|
}
|
|
11683
11653
|
/**
|
|
11684
11654
|
* Create a new template consumer pointing at the specified LView.
|
|
@@ -11700,12 +11670,29 @@ function getReactiveLViewConsumer(lView, slot) {
|
|
|
11700
11670
|
*/
|
|
11701
11671
|
function commitLViewConsumerIfHasProducers(lView, slot) {
|
|
11702
11672
|
const consumer = getOrCreateCurrentLViewConsumer();
|
|
11703
|
-
if (!consumer.
|
|
11673
|
+
if (!consumer.producerNode?.length) {
|
|
11704
11674
|
return;
|
|
11705
11675
|
}
|
|
11706
11676
|
lView[slot] = currentConsumer;
|
|
11707
11677
|
consumer.lView = lView;
|
|
11708
|
-
currentConsumer =
|
|
11678
|
+
currentConsumer = createLViewConsumer();
|
|
11679
|
+
}
|
|
11680
|
+
const REACTIVE_LVIEW_CONSUMER_NODE = {
|
|
11681
|
+
...REACTIVE_NODE,
|
|
11682
|
+
consumerIsAlwaysLive: true,
|
|
11683
|
+
consumerMarkedDirty: (node) => {
|
|
11684
|
+
(typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
11685
|
+
assertDefined(node.lView, 'Updating a signal during template or host binding execution is not allowed.');
|
|
11686
|
+
markViewDirty(node.lView);
|
|
11687
|
+
},
|
|
11688
|
+
lView: null,
|
|
11689
|
+
};
|
|
11690
|
+
function createLViewConsumer() {
|
|
11691
|
+
return Object.create(REACTIVE_LVIEW_CONSUMER_NODE);
|
|
11692
|
+
}
|
|
11693
|
+
function getOrCreateCurrentLViewConsumer() {
|
|
11694
|
+
currentConsumer ??= createLViewConsumer();
|
|
11695
|
+
return currentConsumer;
|
|
11709
11696
|
}
|
|
11710
11697
|
|
|
11711
11698
|
/** A special value which designates that a value has not changed. */
|
|
@@ -11822,8 +11809,15 @@ function processHostBindingOpCodes(tView, lView) {
|
|
|
11822
11809
|
const bindingRootIndx = hostBindingOpCodes[++i];
|
|
11823
11810
|
const hostBindingFn = hostBindingOpCodes[++i];
|
|
11824
11811
|
setBindingRootForHostBindings(bindingRootIndx, directiveIdx);
|
|
11825
|
-
|
|
11826
|
-
|
|
11812
|
+
consumer.dirty = false;
|
|
11813
|
+
const prevConsumer = consumerBeforeComputation(consumer);
|
|
11814
|
+
try {
|
|
11815
|
+
const context = lView[directiveIdx];
|
|
11816
|
+
hostBindingFn(2 /* RenderFlags.Update */, context);
|
|
11817
|
+
}
|
|
11818
|
+
finally {
|
|
11819
|
+
consumerAfterComputation(consumer, prevConsumer);
|
|
11820
|
+
}
|
|
11827
11821
|
}
|
|
11828
11822
|
}
|
|
11829
11823
|
}
|
|
@@ -11963,17 +11957,16 @@ function executeTemplate(tView, lView, templateFn, rf, context) {
|
|
|
11963
11957
|
}
|
|
11964
11958
|
const preHookType = isUpdatePhase ? 2 /* ProfilerEvent.TemplateUpdateStart */ : 0 /* ProfilerEvent.TemplateCreateStart */;
|
|
11965
11959
|
profiler(preHookType, context);
|
|
11966
|
-
|
|
11967
|
-
|
|
11968
|
-
|
|
11969
|
-
|
|
11970
|
-
|
|
11971
|
-
try {
|
|
11972
|
-
templateFn(rf, context);
|
|
11973
|
-
}
|
|
11974
|
-
finally {
|
|
11975
|
-
setActiveConsumer(prevConsumer);
|
|
11960
|
+
const effectiveConsumer = isUpdatePhase ? consumer : null;
|
|
11961
|
+
const prevConsumer = consumerBeforeComputation(effectiveConsumer);
|
|
11962
|
+
try {
|
|
11963
|
+
if (effectiveConsumer !== null) {
|
|
11964
|
+
effectiveConsumer.dirty = false;
|
|
11976
11965
|
}
|
|
11966
|
+
templateFn(rf, context);
|
|
11967
|
+
}
|
|
11968
|
+
finally {
|
|
11969
|
+
consumerAfterComputation(effectiveConsumer, prevConsumer);
|
|
11977
11970
|
}
|
|
11978
11971
|
}
|
|
11979
11972
|
finally {
|
|
@@ -13227,21 +13220,21 @@ class EffectManager {
|
|
|
13227
13220
|
}
|
|
13228
13221
|
create(effectFn, destroyRef, allowSignalWrites) {
|
|
13229
13222
|
const zone = (typeof Zone === 'undefined') ? null : Zone.current;
|
|
13230
|
-
const
|
|
13223
|
+
const w = watch(effectFn, (watch) => {
|
|
13231
13224
|
if (!this.all.has(watch)) {
|
|
13232
13225
|
return;
|
|
13233
13226
|
}
|
|
13234
13227
|
this.queue.set(watch, zone);
|
|
13235
13228
|
}, allowSignalWrites);
|
|
13236
|
-
this.all.add(
|
|
13229
|
+
this.all.add(w);
|
|
13237
13230
|
// Effects start dirty.
|
|
13238
|
-
|
|
13231
|
+
w.notify();
|
|
13239
13232
|
let unregisterOnDestroy;
|
|
13240
13233
|
const destroy = () => {
|
|
13241
|
-
|
|
13234
|
+
w.cleanup();
|
|
13242
13235
|
unregisterOnDestroy?.();
|
|
13243
|
-
this.all.delete(
|
|
13244
|
-
this.queue.delete(
|
|
13236
|
+
this.all.delete(w);
|
|
13237
|
+
this.queue.delete(w);
|
|
13245
13238
|
};
|
|
13246
13239
|
unregisterOnDestroy = destroyRef?.onDestroy(destroy);
|
|
13247
13240
|
return {
|
|
@@ -13625,15 +13618,15 @@ function detectChangesInView(lView, mode) {
|
|
|
13625
13618
|
return;
|
|
13626
13619
|
}
|
|
13627
13620
|
const tView = lView[TVIEW];
|
|
13628
|
-
|
|
13621
|
+
const flags = lView[FLAGS];
|
|
13622
|
+
if ((flags & (16 /* LViewFlags.CheckAlways */ | 64 /* LViewFlags.Dirty */) &&
|
|
13629
13623
|
mode === 0 /* ChangeDetectionMode.Global */) ||
|
|
13630
|
-
|
|
13624
|
+
flags & 1024 /* LViewFlags.RefreshView */ ||
|
|
13631
13625
|
mode === 2 /* ChangeDetectionMode.BugToForceRefreshAndIgnoreViewFlags */) {
|
|
13632
13626
|
refreshView(tView, lView, tView.template, lView[CONTEXT]);
|
|
13633
13627
|
}
|
|
13634
13628
|
else if (lView[DESCENDANT_VIEWS_TO_REFRESH] > 0) {
|
|
13635
13629
|
detectChangesInEmbeddedViews(lView, 1 /* ChangeDetectionMode.Targeted */);
|
|
13636
|
-
const tView = lView[TVIEW];
|
|
13637
13630
|
const components = tView.components;
|
|
13638
13631
|
if (components !== null) {
|
|
13639
13632
|
detectChangesInChildComponents(lView, components, 1 /* ChangeDetectionMode.Targeted */);
|
|
@@ -17409,14 +17402,12 @@ function getTStylingRangePrev(tStylingRange) {
|
|
|
17409
17402
|
}
|
|
17410
17403
|
function getTStylingRangePrevDuplicate(tStylingRange) {
|
|
17411
17404
|
ngDevMode && assertNumber(tStylingRange, 'expected number');
|
|
17412
|
-
return (tStylingRange & 2 /* StylingRange.PREV_DUPLICATE */) ==
|
|
17413
|
-
2 /* StylingRange.PREV_DUPLICATE */;
|
|
17405
|
+
return (tStylingRange & 2 /* StylingRange.PREV_DUPLICATE */) == 2 /* StylingRange.PREV_DUPLICATE */;
|
|
17414
17406
|
}
|
|
17415
17407
|
function setTStylingRangePrev(tStylingRange, previous) {
|
|
17416
17408
|
ngDevMode && assertNumber(tStylingRange, 'expected number');
|
|
17417
17409
|
ngDevMode && assertNumberInRange(previous, 0, 32767 /* StylingRange.UNSIGNED_MASK */);
|
|
17418
|
-
return ((tStylingRange & ~4294836224 /* StylingRange.PREV_MASK */) |
|
|
17419
|
-
(previous << 17 /* StylingRange.PREV_SHIFT */));
|
|
17410
|
+
return ((tStylingRange & ~4294836224 /* StylingRange.PREV_MASK */) | (previous << 17 /* StylingRange.PREV_SHIFT */));
|
|
17420
17411
|
}
|
|
17421
17412
|
function setTStylingRangePrevDuplicate(tStylingRange) {
|
|
17422
17413
|
ngDevMode && assertNumber(tStylingRange, 'expected number');
|
|
@@ -17434,8 +17425,7 @@ function setTStylingRangeNext(tStylingRange, next) {
|
|
|
17434
17425
|
}
|
|
17435
17426
|
function getTStylingRangeNextDuplicate(tStylingRange) {
|
|
17436
17427
|
ngDevMode && assertNumber(tStylingRange, 'expected number');
|
|
17437
|
-
return (tStylingRange & 1 /* StylingRange.NEXT_DUPLICATE */) ===
|
|
17438
|
-
1 /* StylingRange.NEXT_DUPLICATE */;
|
|
17428
|
+
return ((tStylingRange) & 1 /* StylingRange.NEXT_DUPLICATE */) === 1 /* StylingRange.NEXT_DUPLICATE */;
|
|
17439
17429
|
}
|
|
17440
17430
|
function setTStylingRangeNextDuplicate(tStylingRange) {
|
|
17441
17431
|
ngDevMode && assertNumber(tStylingRange, 'expected number');
|
|
@@ -24233,6 +24223,40 @@ function findMatchingDehydratedView(lContainer, template) {
|
|
|
24233
24223
|
* A view container instance can contain other view containers,
|
|
24234
24224
|
* creating a [view hierarchy](guide/glossary#view-hierarchy).
|
|
24235
24225
|
*
|
|
24226
|
+
* @usageNotes
|
|
24227
|
+
*
|
|
24228
|
+
* The example below demonstrates how the `createComponent` function can be used
|
|
24229
|
+
* to create an instance of a ComponentRef dynamically and attach it to an ApplicationRef,
|
|
24230
|
+
* so that it gets included into change detection cycles.
|
|
24231
|
+
*
|
|
24232
|
+
* Note: the example uses standalone components, but the function can also be used for
|
|
24233
|
+
* non-standalone components (declared in an NgModule) as well.
|
|
24234
|
+
*
|
|
24235
|
+
* ```typescript
|
|
24236
|
+
* @Component({
|
|
24237
|
+
* standalone: true,
|
|
24238
|
+
* selector: 'dynamic',
|
|
24239
|
+
* template: `<span>This is a content of a dynamic component.</span>`,
|
|
24240
|
+
* })
|
|
24241
|
+
* class DynamicComponent {
|
|
24242
|
+
* vcr = inject(ViewContainerRef);
|
|
24243
|
+
* }
|
|
24244
|
+
*
|
|
24245
|
+
* @Component({
|
|
24246
|
+
* standalone: true,
|
|
24247
|
+
* selector: 'app',
|
|
24248
|
+
* template: `<main>Hi! This is the main content.</main>`,
|
|
24249
|
+
* })
|
|
24250
|
+
* class AppComponent {
|
|
24251
|
+
* vcr = inject(ViewContainerRef);
|
|
24252
|
+
*
|
|
24253
|
+
* ngAfterViewInit() {
|
|
24254
|
+
* const compRef = this.vcr.createComponent(DynamicComponent);
|
|
24255
|
+
* compRef.changeDetectorRef.detectChanges();
|
|
24256
|
+
* }
|
|
24257
|
+
* }
|
|
24258
|
+
* ```
|
|
24259
|
+
*
|
|
24236
24260
|
* @see {@link ComponentRef}
|
|
24237
24261
|
* @see {@link EmbeddedViewRef}
|
|
24238
24262
|
*
|
|
@@ -31536,17 +31560,18 @@ function ɵɵngDeclarePipe(decl) {
|
|
|
31536
31560
|
* const applicationRef = await bootstrapApplication(RootComponent);
|
|
31537
31561
|
*
|
|
31538
31562
|
* // Locate a DOM node that would be used as a host.
|
|
31539
|
-
* const
|
|
31563
|
+
* const hostElement = document.getElementById('hello-component-host');
|
|
31540
31564
|
*
|
|
31541
31565
|
* // Get an `EnvironmentInjector` instance from the `ApplicationRef`.
|
|
31542
31566
|
* const environmentInjector = applicationRef.injector;
|
|
31543
31567
|
*
|
|
31544
31568
|
* // We can now create a `ComponentRef` instance.
|
|
31545
|
-
* const componentRef = createComponent(HelloComponent, {
|
|
31569
|
+
* const componentRef = createComponent(HelloComponent, {hostElement, environmentInjector});
|
|
31546
31570
|
*
|
|
31547
31571
|
* // Last step is to register the newly created ref using the `ApplicationRef` instance
|
|
31548
31572
|
* // to include the component view into change detection cycles.
|
|
31549
31573
|
* applicationRef.attachView(componentRef.hostView);
|
|
31574
|
+
* componentRef.changeDetectorRef.detectChanges();
|
|
31550
31575
|
* ```
|
|
31551
31576
|
*
|
|
31552
31577
|
* @param component Component class reference.
|