@angular/core 17.0.0-next.4 → 17.0.0-next.6

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 (69) hide show
  1. package/esm2022/rxjs-interop/src/to_signal.mjs +13 -11
  2. package/esm2022/src/application_init.mjs +3 -3
  3. package/esm2022/src/application_module.mjs +3 -3
  4. package/esm2022/src/application_ref.mjs +23 -6
  5. package/esm2022/src/change_detection/differs/default_iterable_differ.mjs +1 -2
  6. package/esm2022/src/console.mjs +2 -2
  7. package/esm2022/src/core.mjs +2 -2
  8. package/esm2022/src/core_private_export.mjs +4 -2
  9. package/esm2022/src/core_render3_private_export.mjs +3 -2
  10. package/esm2022/src/errors.mjs +1 -1
  11. package/esm2022/src/hydration/api.mjs +7 -8
  12. package/esm2022/src/hydration/views.mjs +3 -3
  13. package/esm2022/src/initial_render_pending_tasks.mjs +2 -2
  14. package/esm2022/src/linker/compiler.mjs +2 -2
  15. package/esm2022/src/linker/query_list.mjs +7 -10
  16. package/esm2022/src/linker/view_container_ref.mjs +12 -10
  17. package/esm2022/src/metadata/ng_module_def.mjs +1 -1
  18. package/esm2022/src/render3/after_render_hooks.mjs +100 -13
  19. package/esm2022/src/render3/deps_tracker/api.mjs +1 -1
  20. package/esm2022/src/render3/deps_tracker/deps_tracker.mjs +16 -10
  21. package/esm2022/src/render3/index.mjs +2 -2
  22. package/esm2022/src/render3/instructions/control_flow.mjs +6 -4
  23. package/esm2022/src/render3/instructions/defer.mjs +495 -112
  24. package/esm2022/src/render3/instructions/defer_events.mjs +154 -0
  25. package/esm2022/src/render3/instructions/shared.mjs +1 -1
  26. package/esm2022/src/render3/instructions/template.mjs +9 -2
  27. package/esm2022/src/render3/interfaces/defer.mjs +64 -1
  28. package/esm2022/src/render3/interfaces/definition.mjs +1 -1
  29. package/esm2022/src/render3/local_compilation.mjs +8 -2
  30. package/esm2022/src/render3/metadata.mjs +2 -2
  31. package/esm2022/src/render3/reactive_lview_consumer.mjs +1 -1
  32. package/esm2022/src/render3/reactivity/effect.mjs +3 -15
  33. package/esm2022/src/render3/scope.mjs +10 -4
  34. package/esm2022/src/render3/state.mjs +2 -11
  35. package/esm2022/src/render3/util/view_utils.mjs +17 -3
  36. package/esm2022/src/signals/src/api.mjs +2 -2
  37. package/esm2022/src/signals/src/computed.mjs +50 -45
  38. package/esm2022/src/signals/src/graph.mjs +7 -2
  39. package/esm2022/src/signals/src/signal.mjs +11 -6
  40. package/esm2022/src/signals/src/watch.mjs +40 -12
  41. package/esm2022/src/testability/testability.mjs +5 -5
  42. package/esm2022/src/util/assert.mjs +6 -1
  43. package/esm2022/src/version.mjs +1 -1
  44. package/esm2022/testing/src/component_fixture.mjs +19 -2
  45. package/esm2022/testing/src/defer.mjs +84 -0
  46. package/esm2022/testing/src/logger.mjs +4 -4
  47. package/esm2022/testing/src/test_bed.mjs +12 -2
  48. package/esm2022/testing/src/test_bed_common.mjs +1 -1
  49. package/esm2022/testing/src/test_bed_compiler.mjs +5 -2
  50. package/esm2022/testing/src/testing.mjs +3 -1
  51. package/fesm2022/core.mjs +1691 -930
  52. package/fesm2022/core.mjs.map +1 -1
  53. package/fesm2022/rxjs-interop.mjs +13 -11
  54. package/fesm2022/rxjs-interop.mjs.map +1 -1
  55. package/fesm2022/testing.mjs +109 -3
  56. package/fesm2022/testing.mjs.map +1 -1
  57. package/index.d.ts +279 -49
  58. package/package.json +3 -3
  59. package/rxjs-interop/index.d.ts +1 -1
  60. package/schematics/migrations/block-template-entities/bundle.js +23249 -0
  61. package/schematics/migrations/block-template-entities/bundle.js.map +7 -0
  62. package/schematics/migrations.json +4 -9
  63. package/schematics/ng-generate/standalone-migration/bundle.js +2867 -2021
  64. package/schematics/ng-generate/standalone-migration/bundle.js.map +4 -4
  65. package/testing/index.d.ts +43 -1
  66. package/schematics/migrations/guard-and-resolve-interfaces/bundle.js +0 -694
  67. package/schematics/migrations/guard-and-resolve-interfaces/bundle.js.map +0 -7
  68. package/schematics/migrations/remove-module-id/bundle.js +0 -368
  69. package/schematics/migrations/remove-module-id/bundle.js.map +0 -7
package/fesm2022/core.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v17.0.0-next.4
2
+ * @license Angular v17.0.0-next.6
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -333,6 +333,11 @@ function assertDomNode(node) {
333
333
  throwError(`The provided value must be an instance of a DOM Node but got ${stringify(node)}`);
334
334
  }
335
335
  }
336
+ function assertElement(node) {
337
+ if (!(node instanceof Element)) {
338
+ throwError(`The provided value must be an element but got ${stringify(node)}`);
339
+ }
340
+ }
336
341
  function assertIndexInRange(arr, index) {
337
342
  assertDefined(arr, 'Array must be defined.');
338
343
  const maxLen = arr.length;
@@ -2264,7 +2269,7 @@ function getFactoryDef(type, throwNotFound) {
2264
2269
  *
2265
2270
  * This can be used to auto-unwrap signals in various cases, or to auto-wrap non-signal values.
2266
2271
  */
2267
- const SIGNAL = Symbol('SIGNAL');
2272
+ const SIGNAL = /* @__PURE__ */ Symbol('SIGNAL');
2268
2273
  /**
2269
2274
  * Checks if the given `value` is a reactive `Signal`.
2270
2275
  *
@@ -2445,7 +2450,9 @@ function consumerAfterComputation(node, prevConsumer) {
2445
2450
  }
2446
2451
  }
2447
2452
  // Truncate the producer tracking arrays.
2448
- for (let i = node.nextProducerIndex; i < node.producerNode.length; i++) {
2453
+ // Perf note: this is essentially truncating the length to `node.nextProducerIndex`, but
2454
+ // benchmarking has shown that individual pop operations are faster.
2455
+ while (node.producerNode.length > node.nextProducerIndex) {
2449
2456
  node.producerNode.pop();
2450
2457
  node.producerLastReadVersion.pop();
2451
2458
  node.producerIndexOfThis.pop();
@@ -2519,6 +2526,9 @@ function producerAddLiveConsumer(node, consumer, indexOfThis) {
2519
2526
  function producerRemoveLiveConsumerAtIndex(node, idx) {
2520
2527
  assertProducerNode(node);
2521
2528
  assertConsumerNode(node);
2529
+ if (typeof ngDevMode !== 'undefined' && ngDevMode && idx >= node.liveConsumerNode.length) {
2530
+ throw new Error(`Assertion error: active consumer index ${idx} is out of bounds of ${node.liveConsumerNode.length} consumers)`);
2531
+ }
2522
2532
  if (node.liveConsumerNode.length === 1) {
2523
2533
  // When removing the last live consumer, we will no longer be live. We need to remove
2524
2534
  // ourselves from our producers' tracking (which may cause consumer-producers to lose
@@ -2583,60 +2593,65 @@ function computed(computation, options) {
2583
2593
  * A dedicated symbol used before a computed value has been calculated for the first time.
2584
2594
  * Explicitly typed as `any` so we can use it as signal's value.
2585
2595
  */
2586
- const UNSET = Symbol('UNSET');
2596
+ const UNSET = /* @__PURE__ */ Symbol('UNSET');
2587
2597
  /**
2588
2598
  * A dedicated symbol used in place of a computed signal value to indicate that a given computation
2589
2599
  * is in progress. Used to detect cycles in computation chains.
2590
2600
  * Explicitly typed as `any` so we can use it as signal's value.
2591
2601
  */
2592
- const COMPUTING = Symbol('COMPUTING');
2602
+ const COMPUTING = /* @__PURE__ */ Symbol('COMPUTING');
2593
2603
  /**
2594
2604
  * A dedicated symbol used in place of a computed signal value to indicate that a given computation
2595
2605
  * failed. The thrown error is cached until the computation gets dirty again.
2596
2606
  * Explicitly typed as `any` so we can use it as signal's value.
2597
2607
  */
2598
- const ERRORED = Symbol('ERRORED');
2599
- const COMPUTED_NODE = {
2600
- ...REACTIVE_NODE,
2601
- value: UNSET,
2602
- dirty: true,
2603
- error: null,
2604
- equal: defaultEquals,
2605
- producerMustRecompute(node) {
2606
- // Force a recomputation if there's no current value, or if the current value is in the process
2607
- // of being calculated (which should throw an error).
2608
- return node.value === UNSET || node.value === COMPUTING;
2609
- },
2610
- producerRecomputeValue(node) {
2611
- if (node.value === COMPUTING) {
2612
- // Our computation somehow led to a cyclic read of itself.
2613
- throw new Error('Detected cycle in computations.');
2614
- }
2615
- const oldValue = node.value;
2616
- node.value = COMPUTING;
2617
- const prevConsumer = consumerBeforeComputation(node);
2618
- let newValue;
2619
- try {
2620
- newValue = node.computation();
2621
- }
2622
- catch (err) {
2623
- newValue = ERRORED;
2624
- node.error = err;
2625
- }
2626
- finally {
2627
- consumerAfterComputation(node, prevConsumer);
2628
- }
2629
- if (oldValue !== UNSET && oldValue !== ERRORED && newValue !== ERRORED &&
2630
- node.equal(oldValue, newValue)) {
2631
- // No change to `valueVersion` - old and new values are
2632
- // semantically equivalent.
2633
- node.value = oldValue;
2634
- return;
2635
- }
2636
- node.value = newValue;
2637
- node.version++;
2638
- },
2639
- };
2608
+ const ERRORED = /* @__PURE__ */ Symbol('ERRORED');
2609
+ // Note: Using an IIFE here to ensure that the spread assignment is not considered
2610
+ // a side-effect, ending up preserving `COMPUTED_NODE` and `REACTIVE_NODE`.
2611
+ // TODO: remove when https://github.com/evanw/esbuild/issues/3392 is resolved.
2612
+ const COMPUTED_NODE = /* @__PURE__ */ (() => {
2613
+ return {
2614
+ ...REACTIVE_NODE,
2615
+ value: UNSET,
2616
+ dirty: true,
2617
+ error: null,
2618
+ equal: defaultEquals,
2619
+ producerMustRecompute(node) {
2620
+ // Force a recomputation if there's no current value, or if the current value is in the
2621
+ // process of being calculated (which should throw an error).
2622
+ return node.value === UNSET || node.value === COMPUTING;
2623
+ },
2624
+ producerRecomputeValue(node) {
2625
+ if (node.value === COMPUTING) {
2626
+ // Our computation somehow led to a cyclic read of itself.
2627
+ throw new Error('Detected cycle in computations.');
2628
+ }
2629
+ const oldValue = node.value;
2630
+ node.value = COMPUTING;
2631
+ const prevConsumer = consumerBeforeComputation(node);
2632
+ let newValue;
2633
+ try {
2634
+ newValue = node.computation();
2635
+ }
2636
+ catch (err) {
2637
+ newValue = ERRORED;
2638
+ node.error = err;
2639
+ }
2640
+ finally {
2641
+ consumerAfterComputation(node, prevConsumer);
2642
+ }
2643
+ if (oldValue !== UNSET && oldValue !== ERRORED && newValue !== ERRORED &&
2644
+ node.equal(oldValue, newValue)) {
2645
+ // No change to `valueVersion` - old and new values are
2646
+ // semantically equivalent.
2647
+ node.value = oldValue;
2648
+ return;
2649
+ }
2650
+ node.value = newValue;
2651
+ node.version++;
2652
+ },
2653
+ };
2654
+ })();
2640
2655
 
2641
2656
  function defaultThrowError() {
2642
2657
  throw new Error();
@@ -2681,11 +2696,16 @@ function setPostSignalSetFn(fn) {
2681
2696
  postSignalSetFn = fn;
2682
2697
  return prev;
2683
2698
  }
2684
- const SIGNAL_NODE = {
2685
- ...REACTIVE_NODE,
2686
- equal: defaultEquals,
2687
- readonlyFn: undefined,
2688
- };
2699
+ // Note: Using an IIFE here to ensure that the spread assignment is not considered
2700
+ // a side-effect, ending up preserving `COMPUTED_NODE` and `REACTIVE_NODE`.
2701
+ // TODO: remove when https://github.com/evanw/esbuild/issues/3392 is resolved.
2702
+ const SIGNAL_NODE = /* @__PURE__ */ (() => {
2703
+ return {
2704
+ ...REACTIVE_NODE,
2705
+ equal: defaultEquals,
2706
+ readonlyFn: undefined,
2707
+ };
2708
+ })();
2689
2709
  function signalValueChanged(node) {
2690
2710
  node.version++;
2691
2711
  producerNotifyConsumers(node);
@@ -2754,7 +2774,27 @@ function watch(fn, schedule, allowSignalWrites) {
2754
2774
  const registerOnCleanup = (cleanupFn) => {
2755
2775
  node.cleanupFn = cleanupFn;
2756
2776
  };
2777
+ function isWatchNodeDestroyed(node) {
2778
+ return node.fn === null && node.schedule === null;
2779
+ }
2780
+ function destroyWatchNode(node) {
2781
+ if (!isWatchNodeDestroyed(node)) {
2782
+ consumerDestroy(node); // disconnect watcher from the reactive graph
2783
+ node.cleanupFn();
2784
+ // nullify references to the integration functions to mark node as destroyed
2785
+ node.fn = null;
2786
+ node.schedule = null;
2787
+ node.cleanupFn = NOOP_CLEANUP_FN;
2788
+ }
2789
+ }
2757
2790
  const run = () => {
2791
+ if (node.fn === null) {
2792
+ // trying to run a destroyed watch is noop
2793
+ return;
2794
+ }
2795
+ if (isInNotificationPhase()) {
2796
+ throw new Error(`Schedulers cannot synchronously execute watches while scheduling.`);
2797
+ }
2758
2798
  node.dirty = false;
2759
2799
  if (node.hasRun && !consumerPollProducersForChange(node)) {
2760
2800
  return;
@@ -2774,20 +2814,28 @@ function watch(fn, schedule, allowSignalWrites) {
2774
2814
  notify: () => consumerMarkDirty(node),
2775
2815
  run,
2776
2816
  cleanup: () => node.cleanupFn(),
2817
+ destroy: () => destroyWatchNode(node),
2777
2818
  };
2778
2819
  return node.ref;
2779
2820
  }
2780
2821
  const NOOP_CLEANUP_FN = () => { };
2781
- const WATCH_NODE = {
2782
- ...REACTIVE_NODE,
2783
- consumerIsAlwaysLive: true,
2784
- consumerAllowSignalWrites: false,
2785
- consumerMarkedDirty: (node) => {
2786
- node.schedule(node.ref);
2787
- },
2788
- hasRun: false,
2789
- cleanupFn: NOOP_CLEANUP_FN,
2790
- };
2822
+ // Note: Using an IIFE here to ensure that the spread assignment is not considered
2823
+ // a side-effect, ending up preserving `COMPUTED_NODE` and `REACTIVE_NODE`.
2824
+ // TODO: remove when https://github.com/evanw/esbuild/issues/3392 is resolved.
2825
+ const WATCH_NODE = /* @__PURE__ */ (() => {
2826
+ return {
2827
+ ...REACTIVE_NODE,
2828
+ consumerIsAlwaysLive: true,
2829
+ consumerAllowSignalWrites: false,
2830
+ consumerMarkedDirty: (node) => {
2831
+ if (node.schedule !== null) {
2832
+ node.schedule(node.ref);
2833
+ }
2834
+ },
2835
+ hasRun: false,
2836
+ cleanupFn: NOOP_CLEANUP_FN,
2837
+ };
2838
+ })();
2791
2839
 
2792
2840
  function setAlternateWeakRefImpl(impl) {
2793
2841
  // TODO: remove this function
@@ -3082,6 +3130,20 @@ function clearViewRefreshFlag(lView) {
3082
3130
  updateViewsToRefresh(lView, -1);
3083
3131
  }
3084
3132
  }
3133
+ /**
3134
+ * Walks up the LView hierarchy.
3135
+ * @param nestingLevel Number of times to walk up in hierarchy.
3136
+ * @param currentView View from which to start the lookup.
3137
+ */
3138
+ function walkUpViews(nestingLevel, currentView) {
3139
+ while (nestingLevel > 0) {
3140
+ ngDevMode &&
3141
+ assertDefined(currentView[DECLARATION_VIEW], 'Declaration view should be defined if nesting level is greater than 0.');
3142
+ currentView = currentView[DECLARATION_VIEW];
3143
+ nestingLevel--;
3144
+ }
3145
+ return currentView;
3146
+ }
3085
3147
  /**
3086
3148
  * Updates the `DESCENDANT_VIEWS_TO_REFRESH` counter on the parents of the `LView` as well as the
3087
3149
  * parents above that whose
@@ -3583,15 +3645,6 @@ function nextContextImpl(level) {
3583
3645
  walkUpViews(level, instructionState.lFrame.contextLView);
3584
3646
  return contextLView[CONTEXT];
3585
3647
  }
3586
- function walkUpViews(nestingLevel, currentView) {
3587
- while (nestingLevel > 0) {
3588
- ngDevMode &&
3589
- assertDefined(currentView[DECLARATION_VIEW], 'Declaration view should be defined if nesting level is greater than 0.');
3590
- currentView = currentView[DECLARATION_VIEW];
3591
- nestingLevel--;
3592
- }
3593
- return currentView;
3594
- }
3595
3648
  /**
3596
3649
  * Gets the currently selected element index.
3597
3650
  *
@@ -7272,7 +7325,7 @@ function isPlatformBrowser(injector) {
7272
7325
  *
7273
7326
  * @deprecated For migration purposes only, to be removed soon.
7274
7327
  */
7275
- const USE_RUNTIME_DEPS_TRACKER_FOR_JIT = false;
7328
+ const USE_RUNTIME_DEPS_TRACKER_FOR_JIT = true;
7276
7329
  /**
7277
7330
  * An implementation of DepsTrackerApi which will be used for JIT and local compilation.
7278
7331
  */
@@ -7320,12 +7373,13 @@ class DepsTracker {
7320
7373
  dependencies: [
7321
7374
  ...scope.compilation.directives,
7322
7375
  ...scope.compilation.pipes,
7376
+ ...scope.compilation.ngModules,
7323
7377
  ]
7324
7378
  };
7325
7379
  }
7326
7380
  else {
7327
7381
  if (!this.ownerNgModule.has(type)) {
7328
- return { dependencies: [] };
7382
+ throw new RuntimeError(1001 /* RuntimeErrorCode.RUNTIME_DEPS_ORPHAN_COMPONENT */, `Orphan component found! Trying to render the component ${type.name} without first loading the NgModule that declares it. Make sure that you import the component's NgModule in the NgModule or the standalone component in which you are trying to render this component. Also make sure the way the app is bundled and served always includes the component's NgModule before the component.`);
7329
7383
  }
7330
7384
  const scope = this.getNgModuleScope(this.ownerNgModule.get(type));
7331
7385
  if (scope.compilation.isPoisoned) {
@@ -7353,12 +7407,8 @@ class DepsTracker {
7353
7407
  }
7354
7408
  /** @override */
7355
7409
  clearScopeCacheFor(type) {
7356
- if (isNgModule(type)) {
7357
- this.ngModulesScopeCache.delete(type);
7358
- }
7359
- else if (isComponent(type)) {
7360
- this.standaloneComponentsScopeCache.delete(type);
7361
- }
7410
+ this.ngModulesScopeCache.delete(type);
7411
+ this.standaloneComponentsScopeCache.delete(type);
7362
7412
  }
7363
7413
  /** @override */
7364
7414
  getNgModuleScope(type) {
@@ -7432,6 +7482,12 @@ class DepsTracker {
7432
7482
  // check for it.
7433
7483
  addSet(exportedScope.exported.directives, scope.exported.directives);
7434
7484
  addSet(exportedScope.exported.pipes, scope.exported.pipes);
7485
+ // Some test toolings which run in JIT mode depend on this behavior that the exported scope
7486
+ // should also be present in the compilation scope, even though AoT does not support this
7487
+ // and it is also in odds with NgModule metadata definitions. Without this some tests in
7488
+ // Google will fail.
7489
+ addSet(exportedScope.exported.directives, scope.compilation.directives);
7490
+ addSet(exportedScope.exported.pipes, scope.compilation.pipes);
7435
7491
  }
7436
7492
  else if (isPipe(exported)) {
7437
7493
  scope.exported.pipes.add(exported);
@@ -7457,9 +7513,10 @@ class DepsTracker {
7457
7513
  // Standalone components are always able to self-reference.
7458
7514
  directives: new Set([type]),
7459
7515
  pipes: new Set(),
7516
+ ngModules: new Set(),
7460
7517
  },
7461
7518
  };
7462
- for (const rawImport of rawImports ?? []) {
7519
+ for (const rawImport of flatten(rawImports ?? [])) {
7463
7520
  const imported = resolveForwardRef(rawImport);
7464
7521
  try {
7465
7522
  verifyStandaloneImport(imported, type);
@@ -7470,6 +7527,7 @@ class DepsTracker {
7470
7527
  return ans;
7471
7528
  }
7472
7529
  if (isNgModule(imported)) {
7530
+ ans.compilation.ngModules.add(imported);
7473
7531
  const importedScope = this.getNgModuleScope(imported);
7474
7532
  // Short-circuit if an imported NgModule has corrupted exported scope.
7475
7533
  if (importedScope.exported.isPoisoned) {
@@ -10849,7 +10907,7 @@ class Version {
10849
10907
  /**
10850
10908
  * @publicApi
10851
10909
  */
10852
- const VERSION = new Version('17.0.0-next.4');
10910
+ const VERSION = new Version('17.0.0-next.6');
10853
10911
 
10854
10912
  // This default value is when checking the hierarchy for a token.
10855
10913
  //
@@ -11535,10 +11593,77 @@ function shouldBeIgnoredByZone(applyArgs) {
11535
11593
 
11536
11594
  // Public API for Zone
11537
11595
 
11596
+ /**
11597
+ * The phase to run an `afterRender` or `afterNextRender` callback in.
11598
+ *
11599
+ * Callbacks in the same phase run in the order they are registered. Phases run in the
11600
+ * following order after each render:
11601
+ *
11602
+ * 1. `AfterRenderPhase.EarlyRead`
11603
+ * 2. `AfterRenderPhase.Write`
11604
+ * 3. `AfterRenderPhase.MixedReadWrite`
11605
+ * 4. `AfterRenderPhase.Read`
11606
+ *
11607
+ * Angular is unable to verify or enforce that phases are used correctly, and instead
11608
+ * relies on each developer to follow the guidelines documented for each value and
11609
+ * carefully choose the appropriate one, refactoring their code if necessary. By doing
11610
+ * so, Angular is better able to minimize the performance degradation associated with
11611
+ * manual DOM access, ensuring the best experience for the end users of your application
11612
+ * or library.
11613
+ *
11614
+ * @developerPreview
11615
+ */
11616
+ var AfterRenderPhase;
11617
+ (function (AfterRenderPhase) {
11618
+ /**
11619
+ * Use `AfterRenderPhase.EarlyRead` for callbacks that only need to **read** from the
11620
+ * DOM before a subsequent `AfterRenderPhase.Write` callback, for example to perform
11621
+ * custom layout that the browser doesn't natively support. **Never** use this phase
11622
+ * for callbacks that can write to the DOM or when `AfterRenderPhase.Read` is adequate.
11623
+ *
11624
+ * <div class="alert is-important">
11625
+ *
11626
+ * Using this value can degrade performance.
11627
+ * Instead, prefer using built-in browser functionality when possible.
11628
+ *
11629
+ * </div>
11630
+ */
11631
+ AfterRenderPhase[AfterRenderPhase["EarlyRead"] = 0] = "EarlyRead";
11632
+ /**
11633
+ * Use `AfterRenderPhase.Write` for callbacks that only **write** to the DOM. **Never**
11634
+ * use this phase for callbacks that can read from the DOM.
11635
+ */
11636
+ AfterRenderPhase[AfterRenderPhase["Write"] = 1] = "Write";
11637
+ /**
11638
+ * Use `AfterRenderPhase.MixedReadWrite` for callbacks that read from or write to the
11639
+ * DOM, that haven't been refactored to use a different phase. **Never** use this phase
11640
+ * for callbacks that can use a different phase instead.
11641
+ *
11642
+ * <div class="alert is-critical">
11643
+ *
11644
+ * Using this value can **significantly** degrade performance.
11645
+ * Instead, prefer refactoring into multiple callbacks using a more specific phase.
11646
+ *
11647
+ * </div>
11648
+ */
11649
+ AfterRenderPhase[AfterRenderPhase["MixedReadWrite"] = 2] = "MixedReadWrite";
11650
+ /**
11651
+ * Use `AfterRenderPhase.Read` for callbacks that only **read** from the DOM. **Never**
11652
+ * use this phase for callbacks that can write to the DOM.
11653
+ */
11654
+ AfterRenderPhase[AfterRenderPhase["Read"] = 3] = "Read";
11655
+ })(AfterRenderPhase || (AfterRenderPhase = {}));
11538
11656
  /**
11539
11657
  * Register a callback to be invoked each time the application
11540
11658
  * finishes rendering.
11541
11659
  *
11660
+ * <div class="alert is-critical">
11661
+ *
11662
+ * You should always explicitly specify a non-default [phase](api/core/AfterRenderPhase), or you
11663
+ * risk significant performance degradation.
11664
+ *
11665
+ * </div>
11666
+ *
11542
11667
  * Note that the callback will run
11543
11668
  * - in the order it was registered
11544
11669
  * - once per render
@@ -11569,7 +11694,7 @@ function shouldBeIgnoredByZone(applyArgs) {
11569
11694
  * constructor() {
11570
11695
  * afterRender(() => {
11571
11696
  * console.log('content height: ' + this.contentRef.nativeElement.scrollHeight);
11572
- * });
11697
+ * }, {phase: AfterRenderPhase.Read});
11573
11698
  * }
11574
11699
  * }
11575
11700
  * ```
@@ -11590,7 +11715,8 @@ function afterRender(callback, options) {
11590
11715
  const callbackHandler = afterRenderEventManager.handler ??= new AfterRenderCallbackHandlerImpl();
11591
11716
  const ngZone = injector.get(NgZone);
11592
11717
  const errorHandler = injector.get(ErrorHandler, null, { optional: true });
11593
- const instance = new AfterRenderCallback(ngZone, errorHandler, callback);
11718
+ const phase = options?.phase ?? AfterRenderPhase.MixedReadWrite;
11719
+ const instance = new AfterRenderCallback(ngZone, errorHandler, phase, callback);
11594
11720
  destroy = () => {
11595
11721
  callbackHandler.unregister(instance);
11596
11722
  unregisterFn();
@@ -11602,6 +11728,13 @@ function afterRender(callback, options) {
11602
11728
  * Register a callback to be invoked the next time the application
11603
11729
  * finishes rendering.
11604
11730
  *
11731
+ * <div class="alert is-critical">
11732
+ *
11733
+ * You should always explicitly specify a non-default [phase](api/core/AfterRenderPhase), or you
11734
+ * risk significant performance degradation.
11735
+ *
11736
+ * </div>
11737
+ *
11605
11738
  * Note that the callback will run
11606
11739
  * - in the order it was registered
11607
11740
  * - on browser platforms only
@@ -11633,7 +11766,7 @@ function afterRender(callback, options) {
11633
11766
  * constructor() {
11634
11767
  * afterNextRender(() => {
11635
11768
  * this.chart = new MyChart(this.chartRef.nativeElement);
11636
- * });
11769
+ * }, {phase: AfterRenderPhase.Write});
11637
11770
  * }
11638
11771
  * }
11639
11772
  * ```
@@ -11654,7 +11787,8 @@ function afterNextRender(callback, options) {
11654
11787
  const callbackHandler = afterRenderEventManager.handler ??= new AfterRenderCallbackHandlerImpl();
11655
11788
  const ngZone = injector.get(NgZone);
11656
11789
  const errorHandler = injector.get(ErrorHandler, null, { optional: true });
11657
- const instance = new AfterRenderCallback(ngZone, errorHandler, () => {
11790
+ const phase = options?.phase ?? AfterRenderPhase.MixedReadWrite;
11791
+ const instance = new AfterRenderCallback(ngZone, errorHandler, phase, () => {
11658
11792
  destroy?.();
11659
11793
  callback();
11660
11794
  });
@@ -11669,9 +11803,10 @@ function afterNextRender(callback, options) {
11669
11803
  * A wrapper around a function to be used as an after render callback.
11670
11804
  */
11671
11805
  class AfterRenderCallback {
11672
- constructor(zone, errorHandler, callbackFn) {
11806
+ constructor(zone, errorHandler, phase, callbackFn) {
11673
11807
  this.zone = zone;
11674
11808
  this.errorHandler = errorHandler;
11809
+ this.phase = phase;
11675
11810
  this.callbackFn = callbackFn;
11676
11811
  }
11677
11812
  invoke() {
@@ -11690,7 +11825,13 @@ class AfterRenderCallback {
11690
11825
  class AfterRenderCallbackHandlerImpl {
11691
11826
  constructor() {
11692
11827
  this.executingCallbacks = false;
11693
- this.callbacks = new Set();
11828
+ this.buckets = {
11829
+ // Note: the order of these keys controls the order the phases are run.
11830
+ [AfterRenderPhase.EarlyRead]: new Set(),
11831
+ [AfterRenderPhase.Write]: new Set(),
11832
+ [AfterRenderPhase.MixedReadWrite]: new Set(),
11833
+ [AfterRenderPhase.Read]: new Set(),
11834
+ };
11694
11835
  this.deferredCallbacks = new Set();
11695
11836
  }
11696
11837
  validateBegin() {
@@ -11703,26 +11844,30 @@ class AfterRenderCallbackHandlerImpl {
11703
11844
  register(callback) {
11704
11845
  // If we're currently running callbacks, new callbacks should be deferred
11705
11846
  // until the next render operation.
11706
- const target = this.executingCallbacks ? this.deferredCallbacks : this.callbacks;
11847
+ const target = this.executingCallbacks ? this.deferredCallbacks : this.buckets[callback.phase];
11707
11848
  target.add(callback);
11708
11849
  }
11709
11850
  unregister(callback) {
11710
- this.callbacks.delete(callback);
11851
+ this.buckets[callback.phase].delete(callback);
11711
11852
  this.deferredCallbacks.delete(callback);
11712
11853
  }
11713
11854
  execute() {
11714
11855
  this.executingCallbacks = true;
11715
- for (const callback of this.callbacks) {
11716
- callback.invoke();
11856
+ for (const bucket of Object.values(this.buckets)) {
11857
+ for (const callback of bucket) {
11858
+ callback.invoke();
11859
+ }
11717
11860
  }
11718
11861
  this.executingCallbacks = false;
11719
11862
  for (const callback of this.deferredCallbacks) {
11720
- this.callbacks.add(callback);
11863
+ this.buckets[callback.phase].add(callback);
11721
11864
  }
11722
11865
  this.deferredCallbacks.clear();
11723
11866
  }
11724
11867
  destroy() {
11725
- this.callbacks.clear();
11868
+ for (const bucket of Object.values(this.buckets)) {
11869
+ bucket.clear();
11870
+ }
11726
11871
  this.deferredCallbacks.clear();
11727
11872
  }
11728
11873
  }
@@ -17636,7 +17781,6 @@ class DefaultIterableDiffer {
17636
17781
  this.length = index;
17637
17782
  }
17638
17783
  this._truncate(record);
17639
- // @ts-expect-error overwriting a readonly member
17640
17784
  this.collection = collection;
17641
17785
  return this.isDirty;
17642
17786
  }
@@ -18584,67 +18728,6 @@ const defaultKeyValueDiffers = new KeyValueDiffers(keyValDiff);
18584
18728
  * Change detection enables data binding in Angular.
18585
18729
  */
18586
18730
 
18587
- function createAndRenderEmbeddedLView(declarationLView, templateTNode, context, options) {
18588
- const embeddedTView = templateTNode.tView;
18589
- ngDevMode && assertDefined(embeddedTView, 'TView must be defined for a template node.');
18590
- ngDevMode && assertTNodeForLView(templateTNode, declarationLView);
18591
- // Embedded views follow the change detection strategy of the view they're declared in.
18592
- const isSignalView = declarationLView[FLAGS] & 4096 /* LViewFlags.SignalView */;
18593
- const viewFlags = isSignalView ? 4096 /* LViewFlags.SignalView */ : 16 /* LViewFlags.CheckAlways */;
18594
- const embeddedLView = createLView(declarationLView, embeddedTView, context, viewFlags, null, templateTNode, null, null, null, options?.injector ?? null, options?.dehydratedView ?? null);
18595
- const declarationLContainer = declarationLView[templateTNode.index];
18596
- ngDevMode && assertLContainer(declarationLContainer);
18597
- embeddedLView[DECLARATION_LCONTAINER] = declarationLContainer;
18598
- const declarationViewLQueries = declarationLView[QUERIES];
18599
- if (declarationViewLQueries !== null) {
18600
- embeddedLView[QUERIES] = declarationViewLQueries.createEmbeddedView(embeddedTView);
18601
- }
18602
- // execute creation mode of a view
18603
- renderView(embeddedTView, embeddedLView, context);
18604
- return embeddedLView;
18605
- }
18606
- function getLViewFromLContainer(lContainer, index) {
18607
- const adjustedIndex = CONTAINER_HEADER_OFFSET + index;
18608
- // avoid reading past the array boundaries
18609
- if (adjustedIndex < lContainer.length) {
18610
- const lView = lContainer[adjustedIndex];
18611
- ngDevMode && assertLView(lView);
18612
- return lView;
18613
- }
18614
- return undefined;
18615
- }
18616
- /**
18617
- * Returns whether an elements that belong to a view should be
18618
- * inserted into the DOM. For client-only cases, DOM elements are
18619
- * always inserted. For hydration cases, we check whether serialized
18620
- * info is available for a view and the view is not in a "skip hydration"
18621
- * block (in which case view contents was re-created, thus needing insertion).
18622
- */
18623
- function shouldAddViewToDom(tNode, dehydratedView) {
18624
- return !dehydratedView || hasInSkipHydrationBlockFlag(tNode);
18625
- }
18626
- function addLViewToLContainer(lContainer, lView, index, addToDOM = true) {
18627
- const tView = lView[TVIEW];
18628
- // insert to the view tree so the new view can be change-detected
18629
- insertView(tView, lView, lContainer, index);
18630
- // insert to the view to the DOM tree
18631
- if (addToDOM) {
18632
- const beforeNode = getBeforeNodeForView(index, lContainer);
18633
- const renderer = lView[RENDERER];
18634
- const parentRNode = nativeParentNode(renderer, lContainer[NATIVE]);
18635
- if (parentRNode !== null) {
18636
- addViewToDOM(tView, lContainer[T_HOST], renderer, lView, parentRNode, beforeNode);
18637
- }
18638
- }
18639
- }
18640
- function removeLViewFromLContainer(lContainer, index) {
18641
- const lView = detachView(lContainer, index);
18642
- if (lView !== undefined) {
18643
- destroyLView(lView[TVIEW], lView);
18644
- }
18645
- return lView;
18646
- }
18647
-
18648
18731
  const AT_THIS_LOCATION = '<-- AT THIS LOCATION';
18649
18732
  /**
18650
18733
  * Retrieves a user friendly string for a given TNodeType for use in
@@ -19005,6 +19088,95 @@ function shorten(input, maxLength = 50) {
19005
19088
  return input.length > maxLength ? `${input.substring(0, maxLength - 1)}…` : input;
19006
19089
  }
19007
19090
 
19091
+ /**
19092
+ * Removes all dehydrated views from a given LContainer:
19093
+ * both in internal data structure, as well as removing
19094
+ * corresponding DOM nodes that belong to that dehydrated view.
19095
+ */
19096
+ function removeDehydratedViews(lContainer) {
19097
+ const views = lContainer[DEHYDRATED_VIEWS] ?? [];
19098
+ const parentLView = lContainer[PARENT];
19099
+ const renderer = parentLView[RENDERER];
19100
+ for (const view of views) {
19101
+ removeDehydratedView(view, renderer);
19102
+ ngDevMode && ngDevMode.dehydratedViewsRemoved++;
19103
+ }
19104
+ // Reset the value to an empty array to indicate that no
19105
+ // further processing of dehydrated views is needed for
19106
+ // this view container (i.e. do not trigger the lookup process
19107
+ // once again in case a `ViewContainerRef` is created later).
19108
+ lContainer[DEHYDRATED_VIEWS] = EMPTY_ARRAY;
19109
+ }
19110
+ /**
19111
+ * Helper function to remove all nodes from a dehydrated view.
19112
+ */
19113
+ function removeDehydratedView(dehydratedView, renderer) {
19114
+ let nodesRemoved = 0;
19115
+ let currentRNode = dehydratedView.firstChild;
19116
+ if (currentRNode) {
19117
+ const numNodes = dehydratedView.data[NUM_ROOT_NODES];
19118
+ while (nodesRemoved < numNodes) {
19119
+ ngDevMode && validateSiblingNodeExists(currentRNode);
19120
+ const nextSibling = currentRNode.nextSibling;
19121
+ nativeRemoveNode(renderer, currentRNode, false);
19122
+ currentRNode = nextSibling;
19123
+ nodesRemoved++;
19124
+ }
19125
+ }
19126
+ }
19127
+ /**
19128
+ * Walks over all views within this LContainer invokes dehydrated views
19129
+ * cleanup function for each one.
19130
+ */
19131
+ function cleanupLContainer(lContainer) {
19132
+ removeDehydratedViews(lContainer);
19133
+ for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
19134
+ cleanupLView(lContainer[i]);
19135
+ }
19136
+ }
19137
+ /**
19138
+ * Walks over `LContainer`s and components registered within
19139
+ * this LView and invokes dehydrated views cleanup function for each one.
19140
+ */
19141
+ function cleanupLView(lView) {
19142
+ const tView = lView[TVIEW];
19143
+ for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
19144
+ if (isLContainer(lView[i])) {
19145
+ const lContainer = lView[i];
19146
+ cleanupLContainer(lContainer);
19147
+ }
19148
+ else if (isLView(lView[i])) {
19149
+ // This is a component, enter the `cleanupLView` recursively.
19150
+ cleanupLView(lView[i]);
19151
+ }
19152
+ }
19153
+ }
19154
+ /**
19155
+ * Walks over all views registered within the ApplicationRef and removes
19156
+ * all dehydrated views from all `LContainer`s along the way.
19157
+ */
19158
+ function cleanupDehydratedViews(appRef) {
19159
+ const viewRefs = appRef._views;
19160
+ for (const viewRef of viewRefs) {
19161
+ const lNode = getLNodeForHydration(viewRef);
19162
+ // An `lView` might be `null` if a `ViewRef` represents
19163
+ // an embedded view (not a component view).
19164
+ if (lNode !== null && lNode[HOST] !== null) {
19165
+ if (isLView(lNode)) {
19166
+ cleanupLView(lNode);
19167
+ }
19168
+ else {
19169
+ // Cleanup in the root component view
19170
+ const componentLView = lNode[HOST];
19171
+ cleanupLView(componentLView);
19172
+ // Cleanup in all views within this view container
19173
+ cleanupLContainer(lNode);
19174
+ }
19175
+ ngDevMode && ngDevMode.dehydratedViewsCleanupRuns++;
19176
+ }
19177
+ }
19178
+ }
19179
+
19008
19180
  /**
19009
19181
  * Regexp that extracts a reference node information from the compressed node location.
19010
19182
  * The reference node is represented as either:
@@ -19327,6 +19499,561 @@ function calcPathForNode(tNode, lView) {
19327
19499
  return path;
19328
19500
  }
19329
19501
 
19502
+ /**
19503
+ * Given a current DOM node and a serialized information about the views
19504
+ * in a container, walks over the DOM structure, collecting the list of
19505
+ * dehydrated views.
19506
+ */
19507
+ function locateDehydratedViewsInContainer(currentRNode, serializedViews) {
19508
+ const dehydratedViews = [];
19509
+ for (const serializedView of serializedViews) {
19510
+ // Repeats a view multiple times as needed, based on the serialized information
19511
+ // (for example, for *ngFor-produced views).
19512
+ for (let i = 0; i < (serializedView[MULTIPLIER] ?? 1); i++) {
19513
+ const view = {
19514
+ data: serializedView,
19515
+ firstChild: null,
19516
+ };
19517
+ if (serializedView[NUM_ROOT_NODES] > 0) {
19518
+ // Keep reference to the first node in this view,
19519
+ // so it can be accessed while invoking template instructions.
19520
+ view.firstChild = currentRNode;
19521
+ // Move over to the next node after this view, which can
19522
+ // either be a first node of the next view or an anchor comment
19523
+ // node after the last view in a container.
19524
+ currentRNode = siblingAfter(serializedView[NUM_ROOT_NODES], currentRNode);
19525
+ }
19526
+ dehydratedViews.push(view);
19527
+ }
19528
+ }
19529
+ return [currentRNode, dehydratedViews];
19530
+ }
19531
+ /**
19532
+ * Reference to a function that searches for a matching dehydrated views
19533
+ * stored on a given lContainer.
19534
+ * Returns `null` by default, when hydration is not enabled.
19535
+ */
19536
+ let _findMatchingDehydratedViewImpl = (lContainer, template) => null;
19537
+ /**
19538
+ * Retrieves the next dehydrated view from the LContainer and verifies that
19539
+ * it matches a given template id (from the TView that was used to create this
19540
+ * instance of a view). If the id doesn't match, that means that we are in an
19541
+ * unexpected state and can not complete the reconciliation process. Thus,
19542
+ * all dehydrated views from this LContainer are removed (including corresponding
19543
+ * DOM nodes) and the rendering is performed as if there were no dehydrated views
19544
+ * in this container.
19545
+ */
19546
+ function findMatchingDehydratedViewImpl(lContainer, template) {
19547
+ const views = lContainer[DEHYDRATED_VIEWS];
19548
+ if (!template || views === null || views.length === 0) {
19549
+ return null;
19550
+ }
19551
+ const view = views[0];
19552
+ // Verify whether the first dehydrated view in the container matches
19553
+ // the template id passed to this function (that originated from a TView
19554
+ // that was used to create an instance of an embedded or component views.
19555
+ if (view.data[TEMPLATE_ID] === template) {
19556
+ // If the template id matches - extract the first view and return it.
19557
+ return views.shift();
19558
+ }
19559
+ else {
19560
+ // Otherwise, we are at the state when reconciliation can not be completed,
19561
+ // thus we remove all dehydrated views within this container (remove them
19562
+ // from internal data structures as well as delete associated elements from
19563
+ // the DOM tree).
19564
+ removeDehydratedViews(lContainer);
19565
+ return null;
19566
+ }
19567
+ }
19568
+ function enableFindMatchingDehydratedViewImpl() {
19569
+ _findMatchingDehydratedViewImpl = findMatchingDehydratedViewImpl;
19570
+ }
19571
+ function findMatchingDehydratedView(lContainer, template) {
19572
+ return _findMatchingDehydratedViewImpl(lContainer, template);
19573
+ }
19574
+
19575
+ function createAndRenderEmbeddedLView(declarationLView, templateTNode, context, options) {
19576
+ const embeddedTView = templateTNode.tView;
19577
+ ngDevMode && assertDefined(embeddedTView, 'TView must be defined for a template node.');
19578
+ ngDevMode && assertTNodeForLView(templateTNode, declarationLView);
19579
+ // Embedded views follow the change detection strategy of the view they're declared in.
19580
+ const isSignalView = declarationLView[FLAGS] & 4096 /* LViewFlags.SignalView */;
19581
+ const viewFlags = isSignalView ? 4096 /* LViewFlags.SignalView */ : 16 /* LViewFlags.CheckAlways */;
19582
+ const embeddedLView = createLView(declarationLView, embeddedTView, context, viewFlags, null, templateTNode, null, null, null, options?.injector ?? null, options?.dehydratedView ?? null);
19583
+ const declarationLContainer = declarationLView[templateTNode.index];
19584
+ ngDevMode && assertLContainer(declarationLContainer);
19585
+ embeddedLView[DECLARATION_LCONTAINER] = declarationLContainer;
19586
+ const declarationViewLQueries = declarationLView[QUERIES];
19587
+ if (declarationViewLQueries !== null) {
19588
+ embeddedLView[QUERIES] = declarationViewLQueries.createEmbeddedView(embeddedTView);
19589
+ }
19590
+ // execute creation mode of a view
19591
+ renderView(embeddedTView, embeddedLView, context);
19592
+ return embeddedLView;
19593
+ }
19594
+ function getLViewFromLContainer(lContainer, index) {
19595
+ const adjustedIndex = CONTAINER_HEADER_OFFSET + index;
19596
+ // avoid reading past the array boundaries
19597
+ if (adjustedIndex < lContainer.length) {
19598
+ const lView = lContainer[adjustedIndex];
19599
+ ngDevMode && assertLView(lView);
19600
+ return lView;
19601
+ }
19602
+ return undefined;
19603
+ }
19604
+ /**
19605
+ * Returns whether an elements that belong to a view should be
19606
+ * inserted into the DOM. For client-only cases, DOM elements are
19607
+ * always inserted. For hydration cases, we check whether serialized
19608
+ * info is available for a view and the view is not in a "skip hydration"
19609
+ * block (in which case view contents was re-created, thus needing insertion).
19610
+ */
19611
+ function shouldAddViewToDom(tNode, dehydratedView) {
19612
+ return !dehydratedView || hasInSkipHydrationBlockFlag(tNode);
19613
+ }
19614
+ function addLViewToLContainer(lContainer, lView, index, addToDOM = true) {
19615
+ const tView = lView[TVIEW];
19616
+ // insert to the view tree so the new view can be change-detected
19617
+ insertView(tView, lView, lContainer, index);
19618
+ // insert to the view to the DOM tree
19619
+ if (addToDOM) {
19620
+ const beforeNode = getBeforeNodeForView(index, lContainer);
19621
+ const renderer = lView[RENDERER];
19622
+ const parentRNode = nativeParentNode(renderer, lContainer[NATIVE]);
19623
+ if (parentRNode !== null) {
19624
+ addViewToDOM(tView, lContainer[T_HOST], renderer, lView, parentRNode, beforeNode);
19625
+ }
19626
+ }
19627
+ }
19628
+ function removeLViewFromLContainer(lContainer, index) {
19629
+ const lView = detachView(lContainer, index);
19630
+ if (lView !== undefined) {
19631
+ destroyLView(lView[TVIEW], lView);
19632
+ }
19633
+ return lView;
19634
+ }
19635
+
19636
+ /**
19637
+ * Represents a container where one or more views can be attached to a component.
19638
+ *
19639
+ * Can contain *host views* (created by instantiating a
19640
+ * component with the `createComponent()` method), and *embedded views*
19641
+ * (created by instantiating a `TemplateRef` with the `createEmbeddedView()` method).
19642
+ *
19643
+ * A view container instance can contain other view containers,
19644
+ * creating a [view hierarchy](guide/glossary#view-hierarchy).
19645
+ *
19646
+ * @usageNotes
19647
+ *
19648
+ * The example below demonstrates how the `createComponent` function can be used
19649
+ * to create an instance of a ComponentRef dynamically and attach it to an ApplicationRef,
19650
+ * so that it gets included into change detection cycles.
19651
+ *
19652
+ * Note: the example uses standalone components, but the function can also be used for
19653
+ * non-standalone components (declared in an NgModule) as well.
19654
+ *
19655
+ * ```typescript
19656
+ * @Component({
19657
+ * standalone: true,
19658
+ * selector: 'dynamic',
19659
+ * template: `<span>This is a content of a dynamic component.</span>`,
19660
+ * })
19661
+ * class DynamicComponent {
19662
+ * vcr = inject(ViewContainerRef);
19663
+ * }
19664
+ *
19665
+ * @Component({
19666
+ * standalone: true,
19667
+ * selector: 'app',
19668
+ * template: `<main>Hi! This is the main content.</main>`,
19669
+ * })
19670
+ * class AppComponent {
19671
+ * vcr = inject(ViewContainerRef);
19672
+ *
19673
+ * ngAfterViewInit() {
19674
+ * const compRef = this.vcr.createComponent(DynamicComponent);
19675
+ * compRef.changeDetectorRef.detectChanges();
19676
+ * }
19677
+ * }
19678
+ * ```
19679
+ *
19680
+ * @see {@link ComponentRef}
19681
+ * @see {@link EmbeddedViewRef}
19682
+ *
19683
+ * @publicApi
19684
+ */
19685
+ class ViewContainerRef {
19686
+ /**
19687
+ * @internal
19688
+ * @nocollapse
19689
+ */
19690
+ static { this.__NG_ELEMENT_ID__ = injectViewContainerRef; }
19691
+ }
19692
+ /**
19693
+ * Creates a ViewContainerRef and stores it on the injector. Or, if the ViewContainerRef
19694
+ * already exists, retrieves the existing ViewContainerRef.
19695
+ *
19696
+ * @returns The ViewContainerRef instance to use
19697
+ */
19698
+ function injectViewContainerRef() {
19699
+ const previousTNode = getCurrentTNode();
19700
+ return createContainerRef(previousTNode, getLView());
19701
+ }
19702
+ const VE_ViewContainerRef = ViewContainerRef;
19703
+ // TODO(alxhub): cleaning up this indirection triggers a subtle bug in Closure in g3. Once the fix
19704
+ // for that lands, this can be cleaned up.
19705
+ const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
19706
+ constructor(_lContainer, _hostTNode, _hostLView) {
19707
+ super();
19708
+ this._lContainer = _lContainer;
19709
+ this._hostTNode = _hostTNode;
19710
+ this._hostLView = _hostLView;
19711
+ }
19712
+ get element() {
19713
+ return createElementRef(this._hostTNode, this._hostLView);
19714
+ }
19715
+ get injector() {
19716
+ return new NodeInjector(this._hostTNode, this._hostLView);
19717
+ }
19718
+ /** @deprecated No replacement */
19719
+ get parentInjector() {
19720
+ const parentLocation = getParentInjectorLocation(this._hostTNode, this._hostLView);
19721
+ if (hasParentInjector(parentLocation)) {
19722
+ const parentView = getParentInjectorView(parentLocation, this._hostLView);
19723
+ const injectorIndex = getParentInjectorIndex(parentLocation);
19724
+ ngDevMode && assertNodeInjector(parentView, injectorIndex);
19725
+ const parentTNode = parentView[TVIEW].data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */];
19726
+ return new NodeInjector(parentTNode, parentView);
19727
+ }
19728
+ else {
19729
+ return new NodeInjector(null, this._hostLView);
19730
+ }
19731
+ }
19732
+ clear() {
19733
+ while (this.length > 0) {
19734
+ this.remove(this.length - 1);
19735
+ }
19736
+ }
19737
+ get(index) {
19738
+ const viewRefs = getViewRefs(this._lContainer);
19739
+ return viewRefs !== null && viewRefs[index] || null;
19740
+ }
19741
+ get length() {
19742
+ return this._lContainer.length - CONTAINER_HEADER_OFFSET;
19743
+ }
19744
+ createEmbeddedView(templateRef, context, indexOrOptions) {
19745
+ let index;
19746
+ let injector;
19747
+ if (typeof indexOrOptions === 'number') {
19748
+ index = indexOrOptions;
19749
+ }
19750
+ else if (indexOrOptions != null) {
19751
+ index = indexOrOptions.index;
19752
+ injector = indexOrOptions.injector;
19753
+ }
19754
+ const dehydratedView = findMatchingDehydratedView(this._lContainer, templateRef.ssrId);
19755
+ const viewRef = templateRef.createEmbeddedViewImpl(context || {}, injector, dehydratedView);
19756
+ this.insertImpl(viewRef, index, shouldAddViewToDom(this._hostTNode, dehydratedView));
19757
+ return viewRef;
19758
+ }
19759
+ createComponent(componentFactoryOrType, indexOrOptions, injector, projectableNodes, environmentInjector) {
19760
+ const isComponentFactory = componentFactoryOrType && !isType(componentFactoryOrType);
19761
+ let index;
19762
+ // This function supports 2 signatures and we need to handle options correctly for both:
19763
+ // 1. When first argument is a Component type. This signature also requires extra
19764
+ // options to be provided as object (more ergonomic option).
19765
+ // 2. First argument is a Component factory. In this case extra options are represented as
19766
+ // positional arguments. This signature is less ergonomic and will be deprecated.
19767
+ if (isComponentFactory) {
19768
+ if (ngDevMode) {
19769
+ assertEqual(typeof indexOrOptions !== 'object', true, 'It looks like Component factory was provided as the first argument ' +
19770
+ 'and an options object as the second argument. This combination of arguments ' +
19771
+ 'is incompatible. You can either change the first argument to provide Component ' +
19772
+ 'type or change the second argument to be a number (representing an index at ' +
19773
+ 'which to insert the new component\'s host view into this container)');
19774
+ }
19775
+ index = indexOrOptions;
19776
+ }
19777
+ else {
19778
+ if (ngDevMode) {
19779
+ assertDefined(getComponentDef(componentFactoryOrType), `Provided Component class doesn't contain Component definition. ` +
19780
+ `Please check whether provided class has @Component decorator.`);
19781
+ assertEqual(typeof indexOrOptions !== 'number', true, 'It looks like Component type was provided as the first argument ' +
19782
+ 'and a number (representing an index at which to insert the new component\'s ' +
19783
+ 'host view into this container as the second argument. This combination of arguments ' +
19784
+ 'is incompatible. Please use an object as the second argument instead.');
19785
+ }
19786
+ const options = (indexOrOptions || {});
19787
+ if (ngDevMode && options.environmentInjector && options.ngModuleRef) {
19788
+ throwError(`Cannot pass both environmentInjector and ngModuleRef options to createComponent().`);
19789
+ }
19790
+ index = options.index;
19791
+ injector = options.injector;
19792
+ projectableNodes = options.projectableNodes;
19793
+ environmentInjector = options.environmentInjector || options.ngModuleRef;
19794
+ }
19795
+ const componentFactory = isComponentFactory ?
19796
+ componentFactoryOrType :
19797
+ new ComponentFactory(getComponentDef(componentFactoryOrType));
19798
+ const contextInjector = injector || this.parentInjector;
19799
+ // If an `NgModuleRef` is not provided explicitly, try retrieving it from the DI tree.
19800
+ if (!environmentInjector && componentFactory.ngModule == null) {
19801
+ // For the `ComponentFactory` case, entering this logic is very unlikely, since we expect that
19802
+ // an instance of a `ComponentFactory`, resolved via `ComponentFactoryResolver` would have an
19803
+ // `ngModule` field. This is possible in some test scenarios and potentially in some JIT-based
19804
+ // use-cases. For the `ComponentFactory` case we preserve backwards-compatibility and try
19805
+ // using a provided injector first, then fall back to the parent injector of this
19806
+ // `ViewContainerRef` instance.
19807
+ //
19808
+ // For the factory-less case, it's critical to establish a connection with the module
19809
+ // injector tree (by retrieving an instance of an `NgModuleRef` and accessing its injector),
19810
+ // so that a component can use DI tokens provided in MgModules. For this reason, we can not
19811
+ // rely on the provided injector, since it might be detached from the DI tree (for example, if
19812
+ // it was created via `Injector.create` without specifying a parent injector, or if an
19813
+ // injector is retrieved from an `NgModuleRef` created via `createNgModule` using an
19814
+ // NgModule outside of a module tree). Instead, we always use `ViewContainerRef`'s parent
19815
+ // injector, which is normally connected to the DI tree, which includes module injector
19816
+ // subtree.
19817
+ const _injector = isComponentFactory ? contextInjector : this.parentInjector;
19818
+ // DO NOT REFACTOR. The code here used to have a `injector.get(NgModuleRef, null) ||
19819
+ // undefined` expression which seems to cause internal google apps to fail. This is documented
19820
+ // in the following internal bug issue: go/b/142967802
19821
+ const result = _injector.get(EnvironmentInjector, null);
19822
+ if (result) {
19823
+ environmentInjector = result;
19824
+ }
19825
+ }
19826
+ const componentDef = getComponentDef(componentFactory.componentType ?? {});
19827
+ const dehydratedView = findMatchingDehydratedView(this._lContainer, componentDef?.id ?? null);
19828
+ const rNode = dehydratedView?.firstChild ?? null;
19829
+ const componentRef = componentFactory.create(contextInjector, projectableNodes, rNode, environmentInjector);
19830
+ this.insertImpl(componentRef.hostView, index, shouldAddViewToDom(this._hostTNode, dehydratedView));
19831
+ return componentRef;
19832
+ }
19833
+ insert(viewRef, index) {
19834
+ return this.insertImpl(viewRef, index, true);
19835
+ }
19836
+ insertImpl(viewRef, index, addToDOM) {
19837
+ const lView = viewRef._lView;
19838
+ if (ngDevMode && viewRef.destroyed) {
19839
+ throw new Error('Cannot insert a destroyed View in a ViewContainer!');
19840
+ }
19841
+ if (viewAttachedToContainer(lView)) {
19842
+ // If view is already attached, detach it first so we clean up references appropriately.
19843
+ const prevIdx = this.indexOf(viewRef);
19844
+ // A view might be attached either to this or a different container. The `prevIdx` for
19845
+ // those cases will be:
19846
+ // equal to -1 for views attached to this ViewContainerRef
19847
+ // >= 0 for views attached to a different ViewContainerRef
19848
+ if (prevIdx !== -1) {
19849
+ this.detach(prevIdx);
19850
+ }
19851
+ else {
19852
+ const prevLContainer = lView[PARENT];
19853
+ ngDevMode &&
19854
+ assertEqual(isLContainer(prevLContainer), true, 'An attached view should have its PARENT point to a container.');
19855
+ // We need to re-create a R3ViewContainerRef instance since those are not stored on
19856
+ // LView (nor anywhere else).
19857
+ const prevVCRef = new R3ViewContainerRef(prevLContainer, prevLContainer[T_HOST], prevLContainer[PARENT]);
19858
+ prevVCRef.detach(prevVCRef.indexOf(viewRef));
19859
+ }
19860
+ }
19861
+ // Logical operation of adding `LView` to `LContainer`
19862
+ const adjustedIdx = this._adjustIndex(index);
19863
+ const lContainer = this._lContainer;
19864
+ addLViewToLContainer(lContainer, lView, adjustedIdx, addToDOM);
19865
+ viewRef.attachToViewContainerRef();
19866
+ addToArray(getOrCreateViewRefs(lContainer), adjustedIdx, viewRef);
19867
+ return viewRef;
19868
+ }
19869
+ move(viewRef, newIndex) {
19870
+ if (ngDevMode && viewRef.destroyed) {
19871
+ throw new Error('Cannot move a destroyed View in a ViewContainer!');
19872
+ }
19873
+ return this.insert(viewRef, newIndex);
19874
+ }
19875
+ indexOf(viewRef) {
19876
+ const viewRefsArr = getViewRefs(this._lContainer);
19877
+ return viewRefsArr !== null ? viewRefsArr.indexOf(viewRef) : -1;
19878
+ }
19879
+ remove(index) {
19880
+ const adjustedIdx = this._adjustIndex(index, -1);
19881
+ const detachedView = detachView(this._lContainer, adjustedIdx);
19882
+ if (detachedView) {
19883
+ // Before destroying the view, remove it from the container's array of `ViewRef`s.
19884
+ // This ensures the view container length is updated before calling
19885
+ // `destroyLView`, which could recursively call view container methods that
19886
+ // rely on an accurate container length.
19887
+ // (e.g. a method on this view container being called by a child directive's OnDestroy
19888
+ // lifecycle hook)
19889
+ removeFromArray(getOrCreateViewRefs(this._lContainer), adjustedIdx);
19890
+ destroyLView(detachedView[TVIEW], detachedView);
19891
+ }
19892
+ }
19893
+ detach(index) {
19894
+ const adjustedIdx = this._adjustIndex(index, -1);
19895
+ const view = detachView(this._lContainer, adjustedIdx);
19896
+ const wasDetached = view && removeFromArray(getOrCreateViewRefs(this._lContainer), adjustedIdx) != null;
19897
+ return wasDetached ? new ViewRef$1(view) : null;
19898
+ }
19899
+ _adjustIndex(index, shift = 0) {
19900
+ if (index == null) {
19901
+ return this.length + shift;
19902
+ }
19903
+ if (ngDevMode) {
19904
+ assertGreaterThan(index, -1, `ViewRef index must be positive, got ${index}`);
19905
+ // +1 because it's legal to insert at the end.
19906
+ assertLessThan(index, this.length + 1 + shift, 'index');
19907
+ }
19908
+ return index;
19909
+ }
19910
+ };
19911
+ function getViewRefs(lContainer) {
19912
+ return lContainer[VIEW_REFS];
19913
+ }
19914
+ function getOrCreateViewRefs(lContainer) {
19915
+ return (lContainer[VIEW_REFS] || (lContainer[VIEW_REFS] = []));
19916
+ }
19917
+ /**
19918
+ * Creates a ViewContainerRef and stores it on the injector.
19919
+ *
19920
+ * @param hostTNode The node that is requesting a ViewContainerRef
19921
+ * @param hostLView The view to which the node belongs
19922
+ * @returns The ViewContainerRef instance to use
19923
+ */
19924
+ function createContainerRef(hostTNode, hostLView) {
19925
+ ngDevMode && assertTNodeType(hostTNode, 12 /* TNodeType.AnyContainer */ | 3 /* TNodeType.AnyRNode */);
19926
+ let lContainer;
19927
+ const slotValue = hostLView[hostTNode.index];
19928
+ if (isLContainer(slotValue)) {
19929
+ // If the host is a container, we don't need to create a new LContainer
19930
+ lContainer = slotValue;
19931
+ }
19932
+ else {
19933
+ // An LContainer anchor can not be `null`, but we set it here temporarily
19934
+ // and update to the actual value later in this function (see
19935
+ // `_locateOrCreateAnchorNode`).
19936
+ lContainer = createLContainer(slotValue, hostLView, null, hostTNode);
19937
+ hostLView[hostTNode.index] = lContainer;
19938
+ addToViewTree(hostLView, lContainer);
19939
+ }
19940
+ _locateOrCreateAnchorNode(lContainer, hostLView, hostTNode, slotValue);
19941
+ return new R3ViewContainerRef(lContainer, hostTNode, hostLView);
19942
+ }
19943
+ /**
19944
+ * Creates and inserts a comment node that acts as an anchor for a view container.
19945
+ *
19946
+ * If the host is a regular element, we have to insert a comment node manually which will
19947
+ * be used as an anchor when inserting elements. In this specific case we use low-level DOM
19948
+ * manipulation to insert it.
19949
+ */
19950
+ function insertAnchorNode(hostLView, hostTNode) {
19951
+ const renderer = hostLView[RENDERER];
19952
+ ngDevMode && ngDevMode.rendererCreateComment++;
19953
+ const commentNode = renderer.createComment(ngDevMode ? 'container' : '');
19954
+ const hostNative = getNativeByTNode(hostTNode, hostLView);
19955
+ const parentOfHostNative = nativeParentNode(renderer, hostNative);
19956
+ nativeInsertBefore(renderer, parentOfHostNative, commentNode, nativeNextSibling(renderer, hostNative), false);
19957
+ return commentNode;
19958
+ }
19959
+ let _locateOrCreateAnchorNode = createAnchorNode;
19960
+ let _populateDehydratedViewsInLContainer = (lContainer, tNode, hostLView) => false; // noop by default
19961
+ /**
19962
+ * Looks up dehydrated views that belong to a given LContainer and populates
19963
+ * this information into the `LContainer[DEHYDRATED_VIEWS]` slot. When running
19964
+ * in client-only mode, this function is a noop.
19965
+ *
19966
+ * @param lContainer LContainer that should be populated.
19967
+ * @param tNode Corresponding TNode.
19968
+ * @param hostLView LView that hosts LContainer.
19969
+ * @returns a boolean flag that indicates whether a populating operation
19970
+ * was successful. The operation might be unsuccessful in case is has completed
19971
+ * previously, we are rendering in client-only mode or this content is located
19972
+ * in a skip hydration section.
19973
+ */
19974
+ function populateDehydratedViewsInLContainer(lContainer, tNode, hostLView) {
19975
+ return _populateDehydratedViewsInLContainer(lContainer, tNode, hostLView);
19976
+ }
19977
+ /**
19978
+ * Regular creation mode: an anchor is created and
19979
+ * assigned to the `lContainer[NATIVE]` slot.
19980
+ */
19981
+ function createAnchorNode(lContainer, hostLView, hostTNode, slotValue) {
19982
+ // We already have a native element (anchor) set, return.
19983
+ if (lContainer[NATIVE])
19984
+ return;
19985
+ let commentNode;
19986
+ // If the host is an element container, the native host element is guaranteed to be a
19987
+ // comment and we can reuse that comment as anchor element for the new LContainer.
19988
+ // The comment node in question is already part of the DOM structure so we don't need to append
19989
+ // it again.
19990
+ if (hostTNode.type & 8 /* TNodeType.ElementContainer */) {
19991
+ commentNode = unwrapRNode(slotValue);
19992
+ }
19993
+ else {
19994
+ commentNode = insertAnchorNode(hostLView, hostTNode);
19995
+ }
19996
+ lContainer[NATIVE] = commentNode;
19997
+ }
19998
+ /**
19999
+ * Hydration logic that looks up all dehydrated views in this container
20000
+ * and puts them into `lContainer[DEHYDRATED_VIEWS]` slot.
20001
+ *
20002
+ * @returns a boolean flag that indicates whether a populating operation
20003
+ * was successful. The operation might be unsuccessful in case is has completed
20004
+ * previously, we are rendering in client-only mode or this content is located
20005
+ * in a skip hydration section.
20006
+ */
20007
+ function populateDehydratedViewsInLContainerImpl(lContainer, tNode, hostLView) {
20008
+ // We already have a native element (anchor) set and the process
20009
+ // of finding dehydrated views happened (so the `lContainer[DEHYDRATED_VIEWS]`
20010
+ // is not null), exit early.
20011
+ if (lContainer[NATIVE] && lContainer[DEHYDRATED_VIEWS]) {
20012
+ return true;
20013
+ }
20014
+ const hydrationInfo = hostLView[HYDRATION];
20015
+ const noOffsetIndex = tNode.index - HEADER_OFFSET;
20016
+ // TODO(akushnir): this should really be a single condition, refactor the code
20017
+ // to use `hasInSkipHydrationBlockFlag` logic inside `isInSkipHydrationBlock`.
20018
+ const skipHydration = isInSkipHydrationBlock(tNode) || hasInSkipHydrationBlockFlag(tNode);
20019
+ const isNodeCreationMode = !hydrationInfo || skipHydration || isDisconnectedNode$1(hydrationInfo, noOffsetIndex);
20020
+ // Regular creation mode.
20021
+ if (isNodeCreationMode) {
20022
+ return false;
20023
+ }
20024
+ // Hydration mode, looking up an anchor node and dehydrated views in DOM.
20025
+ const currentRNode = getSegmentHead(hydrationInfo, noOffsetIndex);
20026
+ const serializedViews = hydrationInfo.data[CONTAINERS]?.[noOffsetIndex];
20027
+ ngDevMode &&
20028
+ assertDefined(serializedViews, 'Unexpected state: no hydration info available for a given TNode, ' +
20029
+ 'which represents a view container.');
20030
+ const [commentNode, dehydratedViews] = locateDehydratedViewsInContainer(currentRNode, serializedViews);
20031
+ if (ngDevMode) {
20032
+ validateMatchingNode(commentNode, Node.COMMENT_NODE, null, hostLView, tNode, true);
20033
+ // Do not throw in case this node is already claimed (thus `false` as a second
20034
+ // argument). If this container is created based on an `<ng-template>`, the comment
20035
+ // node would be already claimed from the `template` instruction. If an element acts
20036
+ // as an anchor (e.g. <div #vcRef>), a separate comment node would be created/located,
20037
+ // so we need to claim it here.
20038
+ markRNodeAsClaimedByHydration(commentNode, false);
20039
+ }
20040
+ lContainer[NATIVE] = commentNode;
20041
+ lContainer[DEHYDRATED_VIEWS] = dehydratedViews;
20042
+ return true;
20043
+ }
20044
+ function locateOrCreateAnchorNode(lContainer, hostLView, hostTNode, slotValue) {
20045
+ if (!_populateDehydratedViewsInLContainer(lContainer, hostTNode, hostLView)) {
20046
+ // Populating dehydrated views operation returned `false`, which indicates
20047
+ // that the logic was running in client-only mode, this an anchor comment
20048
+ // node should be created for this container.
20049
+ createAnchorNode(lContainer, hostLView, hostTNode, slotValue);
20050
+ }
20051
+ }
20052
+ function enableLocateOrCreateContainerRefImpl() {
20053
+ _locateOrCreateAnchorNode = locateOrCreateAnchorNode;
20054
+ _populateDehydratedViewsInLContainer = populateDehydratedViewsInLContainerImpl;
20055
+ }
20056
+
19330
20057
  function templateFirstCreatePass(index, tView, lView, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex) {
19331
20058
  ngDevMode && assertFirstCreatePass(tView);
19332
20059
  ngDevMode && ngDevMode.firstCreatePass++;
@@ -19373,7 +20100,13 @@ function ɵɵtemplate(index, templateFn, decls, vars, tagName, attrsIndex, local
19373
20100
  appendChild(tView, lView, comment, tNode);
19374
20101
  }
19375
20102
  attachPatchData(comment, lView);
19376
- addToViewTree(lView, lView[adjustedIndex] = createLContainer(comment, lView, comment, tNode));
20103
+ const lContainer = createLContainer(comment, lView, comment, tNode);
20104
+ lView[adjustedIndex] = lContainer;
20105
+ addToViewTree(lView, lContainer);
20106
+ // If hydration is enabled, looks up dehydrated views in the DOM
20107
+ // using hydration annotation info and stores those views on LContainer.
20108
+ // In client-only mode, this function is a noop.
20109
+ populateDehydratedViewsInLContainer(lContainer, tNode, lView);
19377
20110
  if (isDirectiveHost(tNode)) {
19378
20111
  createDirectivesInstances(tView, lView, tNode);
19379
20112
  }
@@ -19461,8 +20194,9 @@ function ɵɵconditional(containerIndex, matchingTemplateIndex, value) {
19461
20194
  // a truthy value and as the consequence we've got no view to show.
19462
20195
  if (matchingTemplateIndex !== -1) {
19463
20196
  const templateTNode = getExistingTNode(hostLView[TVIEW], matchingTemplateIndex);
19464
- const embeddedLView = createAndRenderEmbeddedLView(hostLView, templateTNode, value);
19465
- addLViewToLContainer(lContainer, embeddedLView, viewInContainerIdx);
20197
+ const dehydratedView = findMatchingDehydratedView(lContainer, templateTNode.tView.ssrId);
20198
+ const embeddedLView = createAndRenderEmbeddedLView(hostLView, templateTNode, value, { dehydratedView });
20199
+ addLViewToLContainer(lContainer, embeddedLView, viewInContainerIdx, shouldAddViewToDom(templateTNode, dehydratedView));
19466
20200
  }
19467
20201
  }
19468
20202
  else {
@@ -19584,656 +20318,290 @@ function ɵɵrepeater(metadataSlotIdx, collection) {
19584
20318
  }
19585
20319
  else if (currentIndex === null) {
19586
20320
  // remove
19587
- adjustedPreviousIndex = adjustToLastLContainerIndex(lContainer, adjustedPreviousIndex);
19588
- removeLViewFromLContainer(lContainer, adjustedPreviousIndex);
19589
- needsIndexUpdate = true;
19590
- }
19591
- else if (adjustedPreviousIndex !== null) {
19592
- // move
19593
- const existingLView = detachExistingView(lContainer, adjustedPreviousIndex);
19594
- addLViewToLContainer(lContainer, existingLView, currentIndex);
19595
- needsIndexUpdate = true;
19596
- }
19597
- });
19598
- // A trackBy function might return the same value even if the underlying item changed - re-bind
19599
- // it in the context.
19600
- changes.forEachIdentityChange((record) => {
19601
- const viewIdx = adjustToLastLContainerIndex(lContainer, record.currentIndex);
19602
- const lView = getExistingLViewFromLContainer(lContainer, viewIdx);
19603
- lView[CONTEXT].$implicit = record.item;
19604
- });
19605
- // moves in the container might caused context's index to get out of order, re-adjust
19606
- if (needsIndexUpdate) {
19607
- for (let i = 0; i < lContainer.length - CONTAINER_HEADER_OFFSET; i++) {
19608
- const lView = getExistingLViewFromLContainer(lContainer, i);
19609
- lView[CONTEXT].$index = i;
19610
- }
19611
- }
19612
- }
19613
- // handle empty blocks
19614
- const bindingIndex = nextBindingIndex();
19615
- if (metadata.hasEmptyBlock) {
19616
- const hasItemsInCollection = differ.length > 0;
19617
- if (bindingUpdated(hostLView, bindingIndex, hasItemsInCollection)) {
19618
- const emptyTemplateIndex = metadataSlotIdx + 2;
19619
- const lContainer = getLContainer(hostLView, HEADER_OFFSET + emptyTemplateIndex);
19620
- if (hasItemsInCollection) {
19621
- removeLViewFromLContainer(lContainer, 0);
19622
- }
19623
- else {
19624
- const emptyTemplateTNode = getExistingTNode(hostTView, emptyTemplateIndex);
19625
- const embeddedLView = createAndRenderEmbeddedLView(hostLView, emptyTemplateTNode, undefined);
19626
- addLViewToLContainer(lContainer, embeddedLView, 0);
19627
- }
19628
- }
19629
- }
19630
- }
19631
- function getLContainer(lView, index) {
19632
- const lContainer = lView[index];
19633
- ngDevMode && assertLContainer(lContainer);
19634
- return lContainer;
19635
- }
19636
- function adjustToLastLContainerIndex(lContainer, index) {
19637
- return index !== null ? index : lContainer.length - CONTAINER_HEADER_OFFSET;
19638
- }
19639
- function detachExistingView(lContainer, index) {
19640
- const existingLView = detachView(lContainer, index);
19641
- ngDevMode && assertLView(existingLView);
19642
- return existingLView;
19643
- }
19644
- function getExistingLViewFromLContainer(lContainer, index) {
19645
- const existingLView = getLViewFromLContainer(lContainer, index);
19646
- ngDevMode && assertLView(existingLView);
19647
- return existingLView;
19648
- }
19649
- function getExistingTNode(tView, index) {
19650
- const tNode = getTNode(tView, index + HEADER_OFFSET);
19651
- ngDevMode && assertTNode(tNode);
19652
- return tNode;
19653
- }
19654
-
19655
- /**
19656
- * Removes all dehydrated views from a given LContainer:
19657
- * both in internal data structure, as well as removing
19658
- * corresponding DOM nodes that belong to that dehydrated view.
19659
- */
19660
- function removeDehydratedViews(lContainer) {
19661
- const views = lContainer[DEHYDRATED_VIEWS] ?? [];
19662
- const parentLView = lContainer[PARENT];
19663
- const renderer = parentLView[RENDERER];
19664
- for (const view of views) {
19665
- removeDehydratedView(view, renderer);
19666
- ngDevMode && ngDevMode.dehydratedViewsRemoved++;
19667
- }
19668
- // Reset the value to an empty array to indicate that no
19669
- // further processing of dehydrated views is needed for
19670
- // this view container (i.e. do not trigger the lookup process
19671
- // once again in case a `ViewContainerRef` is created later).
19672
- lContainer[DEHYDRATED_VIEWS] = EMPTY_ARRAY;
19673
- }
19674
- /**
19675
- * Helper function to remove all nodes from a dehydrated view.
19676
- */
19677
- function removeDehydratedView(dehydratedView, renderer) {
19678
- let nodesRemoved = 0;
19679
- let currentRNode = dehydratedView.firstChild;
19680
- if (currentRNode) {
19681
- const numNodes = dehydratedView.data[NUM_ROOT_NODES];
19682
- while (nodesRemoved < numNodes) {
19683
- ngDevMode && validateSiblingNodeExists(currentRNode);
19684
- const nextSibling = currentRNode.nextSibling;
19685
- nativeRemoveNode(renderer, currentRNode, false);
19686
- currentRNode = nextSibling;
19687
- nodesRemoved++;
19688
- }
19689
- }
19690
- }
19691
- /**
19692
- * Walks over all views within this LContainer invokes dehydrated views
19693
- * cleanup function for each one.
19694
- */
19695
- function cleanupLContainer(lContainer) {
19696
- removeDehydratedViews(lContainer);
19697
- for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
19698
- cleanupLView(lContainer[i]);
19699
- }
19700
- }
19701
- /**
19702
- * Walks over `LContainer`s and components registered within
19703
- * this LView and invokes dehydrated views cleanup function for each one.
19704
- */
19705
- function cleanupLView(lView) {
19706
- const tView = lView[TVIEW];
19707
- for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
19708
- if (isLContainer(lView[i])) {
19709
- const lContainer = lView[i];
19710
- cleanupLContainer(lContainer);
19711
- }
19712
- else if (isLView(lView[i])) {
19713
- // This is a component, enter the `cleanupLView` recursively.
19714
- cleanupLView(lView[i]);
19715
- }
19716
- }
19717
- }
19718
- /**
19719
- * Walks over all views registered within the ApplicationRef and removes
19720
- * all dehydrated views from all `LContainer`s along the way.
19721
- */
19722
- function cleanupDehydratedViews(appRef) {
19723
- const viewRefs = appRef._views;
19724
- for (const viewRef of viewRefs) {
19725
- const lNode = getLNodeForHydration(viewRef);
19726
- // An `lView` might be `null` if a `ViewRef` represents
19727
- // an embedded view (not a component view).
19728
- if (lNode !== null && lNode[HOST] !== null) {
19729
- if (isLView(lNode)) {
19730
- cleanupLView(lNode);
19731
- }
19732
- else {
19733
- // Cleanup in the root component view
19734
- const componentLView = lNode[HOST];
19735
- cleanupLView(componentLView);
19736
- // Cleanup in all views within this view container
19737
- cleanupLContainer(lNode);
19738
- }
19739
- ngDevMode && ngDevMode.dehydratedViewsCleanupRuns++;
19740
- }
19741
- }
19742
- }
19743
-
19744
- /**
19745
- * Given a current DOM node and a serialized information about the views
19746
- * in a container, walks over the DOM structure, collecting the list of
19747
- * dehydrated views.
19748
- */
19749
- function locateDehydratedViewsInContainer(currentRNode, serializedViews) {
19750
- const dehydratedViews = [];
19751
- for (const serializedView of serializedViews) {
19752
- // Repeats a view multiple times as needed, based on the serialized information
19753
- // (for example, for *ngFor-produced views).
19754
- for (let i = 0; i < (serializedView[MULTIPLIER] ?? 1); i++) {
19755
- const view = {
19756
- data: serializedView,
19757
- firstChild: null,
19758
- };
19759
- if (serializedView[NUM_ROOT_NODES] > 0) {
19760
- // Keep reference to the first node in this view,
19761
- // so it can be accessed while invoking template instructions.
19762
- view.firstChild = currentRNode;
19763
- // Move over to the next node after this view, which can
19764
- // either be a first node of the next view or an anchor comment
19765
- // node after the last view in a container.
19766
- currentRNode = siblingAfter(serializedView[NUM_ROOT_NODES], currentRNode);
19767
- }
19768
- dehydratedViews.push(view);
19769
- }
19770
- }
19771
- return [currentRNode, dehydratedViews];
19772
- }
19773
- /**
19774
- * Reference to a function that searches for a matching dehydrated views
19775
- * stored on a given lContainer.
19776
- * Returns `null` by default, when hydration is not enabled.
19777
- */
19778
- let _findMatchingDehydratedViewImpl = (lContainer, template) => null;
19779
- /**
19780
- * Retrieves the next dehydrated view from the LContainer and verifies that
19781
- * it matches a given template id (from the TView that was used to create this
19782
- * instance of a view). If the id doesn't match, that means that we are in an
19783
- * unexpected state and can not complete the reconciliation process. Thus,
19784
- * all dehydrated views from this LContainer are removed (including corresponding
19785
- * DOM nodes) and the rendering is performed as if there were no dehydrated views
19786
- * in this container.
19787
- */
19788
- function findMatchingDehydratedViewImpl(lContainer, template) {
19789
- const views = lContainer[DEHYDRATED_VIEWS] ?? [];
19790
- if (!template || views.length === 0) {
19791
- return null;
19792
- }
19793
- const view = views[0];
19794
- // Verify whether the first dehydrated view in the container matches
19795
- // the template id passed to this function (that originated from a TView
19796
- // that was used to create an instance of an embedded or component views.
19797
- if (view.data[TEMPLATE_ID] === template) {
19798
- // If the template id matches - extract the first view and return it.
19799
- return views.shift();
19800
- }
19801
- else {
19802
- // Otherwise, we are at the state when reconciliation can not be completed,
19803
- // thus we remove all dehydrated views within this container (remove them
19804
- // from internal data structures as well as delete associated elements from
19805
- // the DOM tree).
19806
- removeDehydratedViews(lContainer);
19807
- return null;
19808
- }
19809
- }
19810
- function enableFindMatchingDehydratedViewImpl() {
19811
- _findMatchingDehydratedViewImpl = findMatchingDehydratedViewImpl;
19812
- }
19813
- function findMatchingDehydratedView(lContainer, template) {
19814
- return _findMatchingDehydratedViewImpl(lContainer, template);
19815
- }
19816
-
19817
- /**
19818
- * Represents a container where one or more views can be attached to a component.
19819
- *
19820
- * Can contain *host views* (created by instantiating a
19821
- * component with the `createComponent()` method), and *embedded views*
19822
- * (created by instantiating a `TemplateRef` with the `createEmbeddedView()` method).
19823
- *
19824
- * A view container instance can contain other view containers,
19825
- * creating a [view hierarchy](guide/glossary#view-hierarchy).
19826
- *
19827
- * @usageNotes
19828
- *
19829
- * The example below demonstrates how the `createComponent` function can be used
19830
- * to create an instance of a ComponentRef dynamically and attach it to an ApplicationRef,
19831
- * so that it gets included into change detection cycles.
19832
- *
19833
- * Note: the example uses standalone components, but the function can also be used for
19834
- * non-standalone components (declared in an NgModule) as well.
19835
- *
19836
- * ```typescript
19837
- * @Component({
19838
- * standalone: true,
19839
- * selector: 'dynamic',
19840
- * template: `<span>This is a content of a dynamic component.</span>`,
19841
- * })
19842
- * class DynamicComponent {
19843
- * vcr = inject(ViewContainerRef);
19844
- * }
19845
- *
19846
- * @Component({
19847
- * standalone: true,
19848
- * selector: 'app',
19849
- * template: `<main>Hi! This is the main content.</main>`,
19850
- * })
19851
- * class AppComponent {
19852
- * vcr = inject(ViewContainerRef);
19853
- *
19854
- * ngAfterViewInit() {
19855
- * const compRef = this.vcr.createComponent(DynamicComponent);
19856
- * compRef.changeDetectorRef.detectChanges();
19857
- * }
19858
- * }
19859
- * ```
19860
- *
19861
- * @see {@link ComponentRef}
19862
- * @see {@link EmbeddedViewRef}
19863
- *
19864
- * @publicApi
19865
- */
19866
- class ViewContainerRef {
19867
- /**
19868
- * @internal
19869
- * @nocollapse
19870
- */
19871
- static { this.__NG_ELEMENT_ID__ = injectViewContainerRef; }
19872
- }
19873
- /**
19874
- * Creates a ViewContainerRef and stores it on the injector. Or, if the ViewContainerRef
19875
- * already exists, retrieves the existing ViewContainerRef.
19876
- *
19877
- * @returns The ViewContainerRef instance to use
19878
- */
19879
- function injectViewContainerRef() {
19880
- const previousTNode = getCurrentTNode();
19881
- return createContainerRef(previousTNode, getLView());
19882
- }
19883
- const VE_ViewContainerRef = ViewContainerRef;
19884
- // TODO(alxhub): cleaning up this indirection triggers a subtle bug in Closure in g3. Once the fix
19885
- // for that lands, this can be cleaned up.
19886
- const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
19887
- constructor(_lContainer, _hostTNode, _hostLView) {
19888
- super();
19889
- this._lContainer = _lContainer;
19890
- this._hostTNode = _hostTNode;
19891
- this._hostLView = _hostLView;
19892
- }
19893
- get element() {
19894
- return createElementRef(this._hostTNode, this._hostLView);
19895
- }
19896
- get injector() {
19897
- return new NodeInjector(this._hostTNode, this._hostLView);
19898
- }
19899
- /** @deprecated No replacement */
19900
- get parentInjector() {
19901
- const parentLocation = getParentInjectorLocation(this._hostTNode, this._hostLView);
19902
- if (hasParentInjector(parentLocation)) {
19903
- const parentView = getParentInjectorView(parentLocation, this._hostLView);
19904
- const injectorIndex = getParentInjectorIndex(parentLocation);
19905
- ngDevMode && assertNodeInjector(parentView, injectorIndex);
19906
- const parentTNode = parentView[TVIEW].data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */];
19907
- return new NodeInjector(parentTNode, parentView);
19908
- }
19909
- else {
19910
- return new NodeInjector(null, this._hostLView);
19911
- }
19912
- }
19913
- clear() {
19914
- while (this.length > 0) {
19915
- this.remove(this.length - 1);
19916
- }
19917
- }
19918
- get(index) {
19919
- const viewRefs = getViewRefs(this._lContainer);
19920
- return viewRefs !== null && viewRefs[index] || null;
19921
- }
19922
- get length() {
19923
- return this._lContainer.length - CONTAINER_HEADER_OFFSET;
19924
- }
19925
- createEmbeddedView(templateRef, context, indexOrOptions) {
19926
- let index;
19927
- let injector;
19928
- if (typeof indexOrOptions === 'number') {
19929
- index = indexOrOptions;
19930
- }
19931
- else if (indexOrOptions != null) {
19932
- index = indexOrOptions.index;
19933
- injector = indexOrOptions.injector;
19934
- }
19935
- const dehydratedView = findMatchingDehydratedView(this._lContainer, templateRef.ssrId);
19936
- const viewRef = templateRef.createEmbeddedViewImpl(context || {}, injector, dehydratedView);
19937
- this.insertImpl(viewRef, index, shouldAddViewToDom(this._hostTNode, dehydratedView));
19938
- return viewRef;
19939
- }
19940
- createComponent(componentFactoryOrType, indexOrOptions, injector, projectableNodes, environmentInjector) {
19941
- const isComponentFactory = componentFactoryOrType && !isType(componentFactoryOrType);
19942
- let index;
19943
- // This function supports 2 signatures and we need to handle options correctly for both:
19944
- // 1. When first argument is a Component type. This signature also requires extra
19945
- // options to be provided as object (more ergonomic option).
19946
- // 2. First argument is a Component factory. In this case extra options are represented as
19947
- // positional arguments. This signature is less ergonomic and will be deprecated.
19948
- if (isComponentFactory) {
19949
- if (ngDevMode) {
19950
- assertEqual(typeof indexOrOptions !== 'object', true, 'It looks like Component factory was provided as the first argument ' +
19951
- 'and an options object as the second argument. This combination of arguments ' +
19952
- 'is incompatible. You can either change the first argument to provide Component ' +
19953
- 'type or change the second argument to be a number (representing an index at ' +
19954
- 'which to insert the new component\'s host view into this container)');
19955
- }
19956
- index = indexOrOptions;
19957
- }
19958
- else {
19959
- if (ngDevMode) {
19960
- assertDefined(getComponentDef(componentFactoryOrType), `Provided Component class doesn't contain Component definition. ` +
19961
- `Please check whether provided class has @Component decorator.`);
19962
- assertEqual(typeof indexOrOptions !== 'number', true, 'It looks like Component type was provided as the first argument ' +
19963
- 'and a number (representing an index at which to insert the new component\'s ' +
19964
- 'host view into this container as the second argument. This combination of arguments ' +
19965
- 'is incompatible. Please use an object as the second argument instead.');
20321
+ adjustedPreviousIndex = adjustToLastLContainerIndex(lContainer, adjustedPreviousIndex);
20322
+ removeLViewFromLContainer(lContainer, adjustedPreviousIndex);
20323
+ needsIndexUpdate = true;
19966
20324
  }
19967
- const options = (indexOrOptions || {});
19968
- if (ngDevMode && options.environmentInjector && options.ngModuleRef) {
19969
- throwError(`Cannot pass both environmentInjector and ngModuleRef options to createComponent().`);
20325
+ else if (adjustedPreviousIndex !== null) {
20326
+ // move
20327
+ const existingLView = detachExistingView(lContainer, adjustedPreviousIndex);
20328
+ addLViewToLContainer(lContainer, existingLView, currentIndex);
20329
+ needsIndexUpdate = true;
19970
20330
  }
19971
- index = options.index;
19972
- injector = options.injector;
19973
- projectableNodes = options.projectableNodes;
19974
- environmentInjector = options.environmentInjector || options.ngModuleRef;
19975
- }
19976
- const componentFactory = isComponentFactory ?
19977
- componentFactoryOrType :
19978
- new ComponentFactory(getComponentDef(componentFactoryOrType));
19979
- const contextInjector = injector || this.parentInjector;
19980
- // If an `NgModuleRef` is not provided explicitly, try retrieving it from the DI tree.
19981
- if (!environmentInjector && componentFactory.ngModule == null) {
19982
- // For the `ComponentFactory` case, entering this logic is very unlikely, since we expect that
19983
- // an instance of a `ComponentFactory`, resolved via `ComponentFactoryResolver` would have an
19984
- // `ngModule` field. This is possible in some test scenarios and potentially in some JIT-based
19985
- // use-cases. For the `ComponentFactory` case we preserve backwards-compatibility and try
19986
- // using a provided injector first, then fall back to the parent injector of this
19987
- // `ViewContainerRef` instance.
19988
- //
19989
- // For the factory-less case, it's critical to establish a connection with the module
19990
- // injector tree (by retrieving an instance of an `NgModuleRef` and accessing its injector),
19991
- // so that a component can use DI tokens provided in MgModules. For this reason, we can not
19992
- // rely on the provided injector, since it might be detached from the DI tree (for example, if
19993
- // it was created via `Injector.create` without specifying a parent injector, or if an
19994
- // injector is retrieved from an `NgModuleRef` created via `createNgModule` using an
19995
- // NgModule outside of a module tree). Instead, we always use `ViewContainerRef`'s parent
19996
- // injector, which is normally connected to the DI tree, which includes module injector
19997
- // subtree.
19998
- const _injector = isComponentFactory ? contextInjector : this.parentInjector;
19999
- // DO NOT REFACTOR. The code here used to have a `injector.get(NgModuleRef, null) ||
20000
- // undefined` expression which seems to cause internal google apps to fail. This is documented
20001
- // in the following internal bug issue: go/b/142967802
20002
- const result = _injector.get(EnvironmentInjector, null);
20003
- if (result) {
20004
- environmentInjector = result;
20331
+ });
20332
+ // A trackBy function might return the same value even if the underlying item changed - re-bind
20333
+ // it in the context.
20334
+ changes.forEachIdentityChange((record) => {
20335
+ const viewIdx = adjustToLastLContainerIndex(lContainer, record.currentIndex);
20336
+ const lView = getExistingLViewFromLContainer(lContainer, viewIdx);
20337
+ lView[CONTEXT].$implicit = record.item;
20338
+ });
20339
+ // moves in the container might caused context's index to get out of order, re-adjust
20340
+ if (needsIndexUpdate) {
20341
+ for (let i = 0; i < lContainer.length - CONTAINER_HEADER_OFFSET; i++) {
20342
+ const lView = getExistingLViewFromLContainer(lContainer, i);
20343
+ lView[CONTEXT].$index = i;
20005
20344
  }
20006
20345
  }
20007
- const componentDef = getComponentDef(componentFactory.componentType ?? {});
20008
- const dehydratedView = findMatchingDehydratedView(this._lContainer, componentDef?.id ?? null);
20009
- const rNode = dehydratedView?.firstChild ?? null;
20010
- const componentRef = componentFactory.create(contextInjector, projectableNodes, rNode, environmentInjector);
20011
- this.insertImpl(componentRef.hostView, index, shouldAddViewToDom(this._hostTNode, dehydratedView));
20012
- return componentRef;
20013
20346
  }
20014
- insert(viewRef, index) {
20015
- return this.insertImpl(viewRef, index, true);
20016
- }
20017
- insertImpl(viewRef, index, addToDOM) {
20018
- const lView = viewRef._lView;
20019
- if (ngDevMode && viewRef.destroyed) {
20020
- throw new Error('Cannot insert a destroyed View in a ViewContainer!');
20021
- }
20022
- if (viewAttachedToContainer(lView)) {
20023
- // If view is already attached, detach it first so we clean up references appropriately.
20024
- const prevIdx = this.indexOf(viewRef);
20025
- // A view might be attached either to this or a different container. The `prevIdx` for
20026
- // those cases will be:
20027
- // equal to -1 for views attached to this ViewContainerRef
20028
- // >= 0 for views attached to a different ViewContainerRef
20029
- if (prevIdx !== -1) {
20030
- this.detach(prevIdx);
20347
+ // handle empty blocks
20348
+ const bindingIndex = nextBindingIndex();
20349
+ if (metadata.hasEmptyBlock) {
20350
+ const hasItemsInCollection = differ.length > 0;
20351
+ if (bindingUpdated(hostLView, bindingIndex, hasItemsInCollection)) {
20352
+ const emptyTemplateIndex = metadataSlotIdx + 2;
20353
+ const lContainer = getLContainer(hostLView, HEADER_OFFSET + emptyTemplateIndex);
20354
+ if (hasItemsInCollection) {
20355
+ removeLViewFromLContainer(lContainer, 0);
20031
20356
  }
20032
20357
  else {
20033
- const prevLContainer = lView[PARENT];
20034
- ngDevMode &&
20035
- assertEqual(isLContainer(prevLContainer), true, 'An attached view should have its PARENT point to a container.');
20036
- // We need to re-create a R3ViewContainerRef instance since those are not stored on
20037
- // LView (nor anywhere else).
20038
- const prevVCRef = new R3ViewContainerRef(prevLContainer, prevLContainer[T_HOST], prevLContainer[PARENT]);
20039
- prevVCRef.detach(prevVCRef.indexOf(viewRef));
20358
+ const emptyTemplateTNode = getExistingTNode(hostTView, emptyTemplateIndex);
20359
+ const embeddedLView = createAndRenderEmbeddedLView(hostLView, emptyTemplateTNode, undefined);
20360
+ addLViewToLContainer(lContainer, embeddedLView, 0);
20040
20361
  }
20041
20362
  }
20042
- // Logical operation of adding `LView` to `LContainer`
20043
- const adjustedIdx = this._adjustIndex(index);
20044
- const lContainer = this._lContainer;
20045
- addLViewToLContainer(lContainer, lView, adjustedIdx, addToDOM);
20046
- viewRef.attachToViewContainerRef();
20047
- addToArray(getOrCreateViewRefs(lContainer), adjustedIdx, viewRef);
20048
- return viewRef;
20049
- }
20050
- move(viewRef, newIndex) {
20051
- if (ngDevMode && viewRef.destroyed) {
20052
- throw new Error('Cannot move a destroyed View in a ViewContainer!');
20053
- }
20054
- return this.insert(viewRef, newIndex);
20055
- }
20056
- indexOf(viewRef) {
20057
- const viewRefsArr = getViewRefs(this._lContainer);
20058
- return viewRefsArr !== null ? viewRefsArr.indexOf(viewRef) : -1;
20059
- }
20060
- remove(index) {
20061
- const adjustedIdx = this._adjustIndex(index, -1);
20062
- const detachedView = detachView(this._lContainer, adjustedIdx);
20063
- if (detachedView) {
20064
- // Before destroying the view, remove it from the container's array of `ViewRef`s.
20065
- // This ensures the view container length is updated before calling
20066
- // `destroyLView`, which could recursively call view container methods that
20067
- // rely on an accurate container length.
20068
- // (e.g. a method on this view container being called by a child directive's OnDestroy
20069
- // lifecycle hook)
20070
- removeFromArray(getOrCreateViewRefs(this._lContainer), adjustedIdx);
20071
- destroyLView(detachedView[TVIEW], detachedView);
20072
- }
20073
- }
20074
- detach(index) {
20075
- const adjustedIdx = this._adjustIndex(index, -1);
20076
- const view = detachView(this._lContainer, adjustedIdx);
20077
- const wasDetached = view && removeFromArray(getOrCreateViewRefs(this._lContainer), adjustedIdx) != null;
20078
- return wasDetached ? new ViewRef$1(view) : null;
20079
- }
20080
- _adjustIndex(index, shift = 0) {
20081
- if (index == null) {
20082
- return this.length + shift;
20083
- }
20084
- if (ngDevMode) {
20085
- assertGreaterThan(index, -1, `ViewRef index must be positive, got ${index}`);
20086
- // +1 because it's legal to insert at the end.
20087
- assertLessThan(index, this.length + 1 + shift, 'index');
20088
- }
20089
- return index;
20090
20363
  }
20091
- };
20092
- function getViewRefs(lContainer) {
20093
- return lContainer[VIEW_REFS];
20094
20364
  }
20095
- function getOrCreateViewRefs(lContainer) {
20096
- return (lContainer[VIEW_REFS] || (lContainer[VIEW_REFS] = []));
20365
+ function getLContainer(lView, index) {
20366
+ const lContainer = lView[index];
20367
+ ngDevMode && assertLContainer(lContainer);
20368
+ return lContainer;
20369
+ }
20370
+ function adjustToLastLContainerIndex(lContainer, index) {
20371
+ return index !== null ? index : lContainer.length - CONTAINER_HEADER_OFFSET;
20097
20372
  }
20373
+ function detachExistingView(lContainer, index) {
20374
+ const existingLView = detachView(lContainer, index);
20375
+ ngDevMode && assertLView(existingLView);
20376
+ return existingLView;
20377
+ }
20378
+ function getExistingLViewFromLContainer(lContainer, index) {
20379
+ const existingLView = getLViewFromLContainer(lContainer, index);
20380
+ ngDevMode && assertLView(existingLView);
20381
+ return existingLView;
20382
+ }
20383
+ function getExistingTNode(tView, index) {
20384
+ const tNode = getTNode(tView, index + HEADER_OFFSET);
20385
+ ngDevMode && assertTNode(tNode);
20386
+ return tNode;
20387
+ }
20388
+
20098
20389
  /**
20099
- * Creates a ViewContainerRef and stores it on the injector.
20390
+ * Describes the state of defer block dependency loading.
20391
+ */
20392
+ var DeferDependenciesLoadingState;
20393
+ (function (DeferDependenciesLoadingState) {
20394
+ /** Initial state, dependency loading is not yet triggered */
20395
+ DeferDependenciesLoadingState[DeferDependenciesLoadingState["NOT_STARTED"] = 0] = "NOT_STARTED";
20396
+ /** Dependency loading is in progress */
20397
+ DeferDependenciesLoadingState[DeferDependenciesLoadingState["IN_PROGRESS"] = 1] = "IN_PROGRESS";
20398
+ /** Dependency loading has completed successfully */
20399
+ DeferDependenciesLoadingState[DeferDependenciesLoadingState["COMPLETE"] = 2] = "COMPLETE";
20400
+ /** Dependency loading has failed */
20401
+ DeferDependenciesLoadingState[DeferDependenciesLoadingState["FAILED"] = 3] = "FAILED";
20402
+ })(DeferDependenciesLoadingState || (DeferDependenciesLoadingState = {}));
20403
+ /**
20404
+ * Describes the current state of this defer block instance.
20100
20405
  *
20101
- * @param hostTNode The node that is requesting a ViewContainerRef
20102
- * @param hostLView The view to which the node belongs
20103
- * @returns The ViewContainerRef instance to use
20406
+ * @publicApi
20407
+ * @developerPreview
20104
20408
  */
20105
- function createContainerRef(hostTNode, hostLView) {
20106
- ngDevMode && assertTNodeType(hostTNode, 12 /* TNodeType.AnyContainer */ | 3 /* TNodeType.AnyRNode */);
20107
- let lContainer;
20108
- const slotValue = hostLView[hostTNode.index];
20109
- if (isLContainer(slotValue)) {
20110
- // If the host is a container, we don't need to create a new LContainer
20111
- lContainer = slotValue;
20112
- }
20113
- else {
20114
- // An LContainer anchor can not be `null`, but we set it here temporarily
20115
- // and update to the actual value later in this function (see
20116
- // `_locateOrCreateAnchorNode`).
20117
- lContainer = createLContainer(slotValue, hostLView, null, hostTNode);
20118
- hostLView[hostTNode.index] = lContainer;
20119
- addToViewTree(hostLView, lContainer);
20120
- }
20121
- _locateOrCreateAnchorNode(lContainer, hostLView, hostTNode, slotValue);
20122
- return new R3ViewContainerRef(lContainer, hostTNode, hostLView);
20123
- }
20409
+ var DeferBlockState;
20410
+ (function (DeferBlockState) {
20411
+ /** The placeholder block content is rendered */
20412
+ DeferBlockState[DeferBlockState["Placeholder"] = 0] = "Placeholder";
20413
+ /** The loading block content is rendered */
20414
+ DeferBlockState[DeferBlockState["Loading"] = 1] = "Loading";
20415
+ /** The main content block content is rendered */
20416
+ DeferBlockState[DeferBlockState["Complete"] = 2] = "Complete";
20417
+ /** The error block content is rendered */
20418
+ DeferBlockState[DeferBlockState["Error"] = 3] = "Error";
20419
+ })(DeferBlockState || (DeferBlockState = {}));
20124
20420
  /**
20125
- * Creates and inserts a comment node that acts as an anchor for a view container.
20421
+ * Describes the initial state of this defer block instance.
20126
20422
  *
20127
- * If the host is a regular element, we have to insert a comment node manually which will
20128
- * be used as an anchor when inserting elements. In this specific case we use low-level DOM
20129
- * manipulation to insert it.
20423
+ * Note: this state is internal only and *must* be represented
20424
+ * with a number lower than any value in the `DeferBlockState` enum.
20130
20425
  */
20131
- function insertAnchorNode(hostLView, hostTNode) {
20132
- const renderer = hostLView[RENDERER];
20133
- ngDevMode && ngDevMode.rendererCreateComment++;
20134
- const commentNode = renderer.createComment(ngDevMode ? 'container' : '');
20135
- const hostNative = getNativeByTNode(hostTNode, hostLView);
20136
- const parentOfHostNative = nativeParentNode(renderer, hostNative);
20137
- nativeInsertBefore(renderer, parentOfHostNative, commentNode, nativeNextSibling(renderer, hostNative), false);
20138
- return commentNode;
20139
- }
20140
- let _locateOrCreateAnchorNode = createAnchorNode;
20141
- let _populateDehydratedViewsInContainer = (lContainer, lView, tNode) => false; // noop by default
20426
+ var DeferBlockInternalState;
20427
+ (function (DeferBlockInternalState) {
20428
+ /** Initial state. Nothing is rendered yet. */
20429
+ DeferBlockInternalState[DeferBlockInternalState["Initial"] = -1] = "Initial";
20430
+ })(DeferBlockInternalState || (DeferBlockInternalState = {}));
20142
20431
  /**
20143
- * Looks up dehydrated views that belong to a given LContainer and populates
20144
- * this information into the `LContainer[DEHYDRATED_VIEWS]` slot. When running
20145
- * in client-only mode, this function is a noop.
20432
+ * A slot in the `LDeferBlockDetails` array that contains a number
20433
+ * that represent a current block state that is being rendered.
20434
+ */
20435
+ const DEFER_BLOCK_STATE = 0;
20436
+ /**
20437
+ * Options for configuring defer blocks behavior.
20438
+ * @publicApi
20439
+ * @developerPreview
20440
+ */
20441
+ var DeferBlockBehavior;
20442
+ (function (DeferBlockBehavior) {
20443
+ /**
20444
+ * Manual triggering mode for defer blocks. Provides control over when defer blocks render
20445
+ * and which state they render. This is the default behavior in test environments.
20446
+ */
20447
+ DeferBlockBehavior[DeferBlockBehavior["Manual"] = 0] = "Manual";
20448
+ /**
20449
+ * Playthrough mode for defer blocks. This mode behaves like defer blocks would in a browser.
20450
+ */
20451
+ DeferBlockBehavior[DeferBlockBehavior["Playthrough"] = 1] = "Playthrough";
20452
+ })(DeferBlockBehavior || (DeferBlockBehavior = {}));
20453
+
20454
+ /*!
20455
+ * @license
20456
+ * Copyright Google LLC All Rights Reserved.
20146
20457
  *
20147
- * @param lContainer LContainer that should be populated.
20148
- * @returns a boolean flag that indicates whether a populating operation
20149
- * was successful. The operation might be unsuccessful in case is has completed
20150
- * previously, we are rendering in client-only mode or this content is located
20151
- * in a skip hydration section.
20458
+ * Use of this source code is governed by an MIT-style license that can be
20459
+ * found in the LICENSE file at https://angular.io/license
20152
20460
  */
20153
- function populateDehydratedViewsInContainer(lContainer) {
20154
- return _populateDehydratedViewsInContainer(lContainer, getLView(), getCurrentTNode());
20461
+ /** Configuration object used to register passive and capturing events. */
20462
+ const eventListenerOptions = {
20463
+ passive: true,
20464
+ capture: true
20465
+ };
20466
+ /** Keeps track of the currently-registered `on hover` triggers. */
20467
+ const hoverTriggers = new WeakMap();
20468
+ /** Keeps track of the currently-registered `on interaction` triggers. */
20469
+ const interactionTriggers = new WeakMap();
20470
+ /** Names of the events considered as interaction events. */
20471
+ const interactionEventNames = ['click', 'keydown'];
20472
+ /** Object keeping track of registered callbacks for a deferred block trigger. */
20473
+ class DeferEventEntry {
20474
+ constructor() {
20475
+ this.callbacks = new Set();
20476
+ this.listener = () => {
20477
+ for (const callback of this.callbacks) {
20478
+ callback();
20479
+ }
20480
+ };
20481
+ }
20155
20482
  }
20156
20483
  /**
20157
- * Regular creation mode: an anchor is created and
20158
- * assigned to the `lContainer[NATIVE]` slot.
20484
+ * Registers an interaction trigger.
20485
+ * @param trigger Element that is the trigger.
20486
+ * @param callback Callback to be invoked when the trigger is interacted with.
20487
+ */
20488
+ function onInteraction(trigger, callback) {
20489
+ let entry = interactionTriggers.get(trigger);
20490
+ // If this is the first entry for this element, add the listeners.
20491
+ if (!entry) {
20492
+ // Note that using managing events centrally like this lends itself well to using global
20493
+ // event delegation. It currently does delegation at the element level, rather than the
20494
+ // document level, because:
20495
+ // 1. Global delegation is the most effective when there are a lot of events being registered
20496
+ // at the same time. Deferred blocks are unlikely to be used in such a way.
20497
+ // 2. Matching events to their target isn't free. For each `click` and `keydown` event we
20498
+ // would have look through all the triggers and check if the target either is the element
20499
+ // itself or it's contained within the element. Given that `click` and `keydown` are some
20500
+ // of the most common events, this may end up introducing a lot of runtime overhead.
20501
+ // 3. We're still registering only two events per element, no matter how many deferred blocks
20502
+ // are referencing it.
20503
+ entry = new DeferEventEntry();
20504
+ interactionTriggers.set(trigger, entry);
20505
+ for (const name of interactionEventNames) {
20506
+ trigger.addEventListener(name, entry.listener, eventListenerOptions);
20507
+ }
20508
+ }
20509
+ entry.callbacks.add(callback);
20510
+ return () => {
20511
+ const { callbacks, listener } = entry;
20512
+ callbacks.delete(callback);
20513
+ if (callbacks.size === 0) {
20514
+ interactionTriggers.delete(trigger);
20515
+ for (const name of interactionEventNames) {
20516
+ trigger.removeEventListener(name, listener, eventListenerOptions);
20517
+ }
20518
+ }
20519
+ };
20520
+ }
20521
+ /**
20522
+ * Registers a hover trigger.
20523
+ * @param trigger Element that is the trigger.
20524
+ * @param callback Callback to be invoked when the trigger is hovered over.
20159
20525
  */
20160
- function createAnchorNode(lContainer, hostLView, hostTNode, slotValue) {
20161
- // We already have a native element (anchor) set, return.
20162
- if (lContainer[NATIVE])
20163
- return;
20164
- let commentNode;
20165
- // If the host is an element container, the native host element is guaranteed to be a
20166
- // comment and we can reuse that comment as anchor element for the new LContainer.
20167
- // The comment node in question is already part of the DOM structure so we don't need to append
20168
- // it again.
20169
- if (hostTNode.type & 8 /* TNodeType.ElementContainer */) {
20170
- commentNode = unwrapRNode(slotValue);
20171
- }
20172
- else {
20173
- commentNode = insertAnchorNode(hostLView, hostTNode);
20526
+ function onHover(trigger, callback) {
20527
+ let entry = hoverTriggers.get(trigger);
20528
+ // If this is the first entry for this element, add the listener.
20529
+ if (!entry) {
20530
+ entry = new DeferEventEntry();
20531
+ trigger.addEventListener('mouseenter', entry.listener, eventListenerOptions);
20532
+ hoverTriggers.set(trigger, entry);
20174
20533
  }
20175
- lContainer[NATIVE] = commentNode;
20534
+ entry.callbacks.add(callback);
20535
+ return () => {
20536
+ const { callbacks, listener } = entry;
20537
+ callbacks.delete(callback);
20538
+ if (callbacks.size === 0) {
20539
+ trigger.removeEventListener('mouseenter', listener, eventListenerOptions);
20540
+ hoverTriggers.delete(trigger);
20541
+ }
20542
+ };
20176
20543
  }
20177
20544
  /**
20178
- * Hydration logic that looks up all dehydrated views in this container
20179
- * and puts them into `lContainer[DEHYDRATED_VIEWS]` slot.
20180
- *
20181
- * @returns a boolean flag that indicates whether a populating operation
20182
- * was successful. The operation might be unsuccessful in case is has completed
20183
- * previously, we are rendering in client-only mode or this content is located
20184
- * in a skip hydration section.
20545
+ * Registers a viewport trigger.
20546
+ * @param trigger Element that is the trigger.
20547
+ * @param callback Callback to be invoked when the trigger comes into the viewport.
20548
+ * @param injector Injector that can be used by the trigger to resolve DI tokens.
20185
20549
  */
20186
- function populateDehydratedViewsInContainerImpl(lContainer, hostLView, hostTNode) {
20187
- // We already have a native element (anchor) set and the process
20188
- // of finding dehydrated views happened (so the `lContainer[DEHYDRATED_VIEWS]`
20189
- // is not null), exit early.
20190
- if (lContainer[NATIVE] && lContainer[DEHYDRATED_VIEWS]) {
20191
- return true;
20192
- }
20193
- const hydrationInfo = hostLView[HYDRATION];
20194
- const noOffsetIndex = hostTNode.index - HEADER_OFFSET;
20195
- // TODO(akushnir): this should really be a single condition, refactor the code
20196
- // to use `hasInSkipHydrationBlockFlag` logic inside `isInSkipHydrationBlock`.
20197
- const skipHydration = isInSkipHydrationBlock(hostTNode) || hasInSkipHydrationBlockFlag(hostTNode);
20198
- const isNodeCreationMode = !hydrationInfo || skipHydration || isDisconnectedNode$1(hydrationInfo, noOffsetIndex);
20199
- // Regular creation mode.
20200
- if (isNodeCreationMode) {
20201
- return false;
20202
- }
20203
- // Hydration mode, looking up an anchor node and dehydrated views in DOM.
20204
- const currentRNode = getSegmentHead(hydrationInfo, noOffsetIndex);
20205
- const serializedViews = hydrationInfo.data[CONTAINERS]?.[noOffsetIndex];
20206
- ngDevMode &&
20207
- assertDefined(serializedViews, 'Unexpected state: no hydration info available for a given TNode, ' +
20208
- 'which represents a view container.');
20209
- const [commentNode, dehydratedViews] = locateDehydratedViewsInContainer(currentRNode, serializedViews);
20210
- if (ngDevMode) {
20211
- validateMatchingNode(commentNode, Node.COMMENT_NODE, null, hostLView, hostTNode, true);
20212
- // Do not throw in case this node is already claimed (thus `false` as a second
20213
- // argument). If this container is created based on an `<ng-template>`, the comment
20214
- // node would be already claimed from the `template` instruction. If an element acts
20215
- // as an anchor (e.g. <div #vcRef>), a separate comment node would be created/located,
20216
- // so we need to claim it here.
20217
- markRNodeAsClaimedByHydration(commentNode, false);
20218
- }
20219
- lContainer[NATIVE] = commentNode;
20220
- lContainer[DEHYDRATED_VIEWS] = dehydratedViews;
20221
- return true;
20550
+ function onViewport(trigger, callback, injector) {
20551
+ return injector.get(DeferIntersectionManager).register(trigger, callback);
20222
20552
  }
20223
- function locateOrCreateAnchorNode(lContainer, hostLView, hostTNode, slotValue) {
20224
- if (!_populateDehydratedViewsInContainer(lContainer, hostLView, hostTNode)) {
20225
- // Populating dehydrated views operation returned `false`, which indicates
20226
- // that the logic was running in client-only mode, this an anchor comment
20227
- // node should be created for this container.
20228
- createAnchorNode(lContainer, hostLView, hostTNode, slotValue);
20553
+ /** Keeps track of the registered `viewport` triggers. */
20554
+ class DeferIntersectionManager {
20555
+ /** @nocollapse */
20556
+ static { this.ɵprov = ɵɵdefineInjectable({
20557
+ token: DeferIntersectionManager,
20558
+ providedIn: 'root',
20559
+ factory: () => new DeferIntersectionManager(inject(NgZone)),
20560
+ }); }
20561
+ constructor(ngZone) {
20562
+ this.ngZone = ngZone;
20563
+ /** `IntersectionObserver` used to observe `viewport` triggers. */
20564
+ this.intersectionObserver = null;
20565
+ /** Number of elements currently observed with `viewport` triggers. */
20566
+ this.observedViewportElements = 0;
20567
+ /** Currently-registered `viewport` triggers. */
20568
+ this.viewportTriggers = new WeakMap();
20569
+ this.intersectionCallback = entries => {
20570
+ for (const current of entries) {
20571
+ // Only invoke the callbacks if the specific element is intersecting.
20572
+ if (current.isIntersecting && this.viewportTriggers.has(current.target)) {
20573
+ this.ngZone.run(this.viewportTriggers.get(current.target).listener);
20574
+ }
20575
+ }
20576
+ };
20577
+ }
20578
+ register(trigger, callback) {
20579
+ let entry = this.viewportTriggers.get(trigger);
20580
+ if (!this.intersectionObserver) {
20581
+ this.intersectionObserver =
20582
+ this.ngZone.runOutsideAngular(() => new IntersectionObserver(this.intersectionCallback));
20583
+ }
20584
+ if (!entry) {
20585
+ entry = new DeferEventEntry();
20586
+ this.ngZone.runOutsideAngular(() => this.intersectionObserver.observe(trigger));
20587
+ this.viewportTriggers.set(trigger, entry);
20588
+ this.observedViewportElements++;
20589
+ }
20590
+ entry.callbacks.add(callback);
20591
+ return () => {
20592
+ entry.callbacks.delete(callback);
20593
+ if (entry.callbacks.size === 0) {
20594
+ this.intersectionObserver?.unobserve(trigger);
20595
+ this.viewportTriggers.delete(trigger);
20596
+ this.observedViewportElements--;
20597
+ }
20598
+ if (this.observedViewportElements === 0) {
20599
+ this.intersectionObserver?.disconnect();
20600
+ this.intersectionObserver = null;
20601
+ }
20602
+ };
20229
20603
  }
20230
20604
  }
20231
- function enableLocateOrCreateContainerRefImpl() {
20232
- _locateOrCreateAnchorNode = locateOrCreateAnchorNode;
20233
- _populateDehydratedViewsInContainer = populateDehydratedViewsInContainerImpl;
20234
- }
20235
-
20236
- const DEFER_BLOCK_STATE = 0;
20237
20605
 
20238
20606
  /**
20239
20607
  * Returns whether defer blocks should be triggered.
@@ -20242,27 +20610,25 @@ const DEFER_BLOCK_STATE = 0;
20242
20610
  * only placeholder content is rendered (if provided).
20243
20611
  */
20244
20612
  function shouldTriggerDeferBlock(injector) {
20613
+ const config = injector.get(DEFER_BLOCK_CONFIG, null, { optional: true });
20614
+ if (config?.behavior === DeferBlockBehavior.Manual) {
20615
+ return false;
20616
+ }
20245
20617
  return isPlatformBrowser(injector);
20246
20618
  }
20247
20619
  /**
20248
- * Shims for the `requestIdleCallback` and `cancelIdleCallback` functions for environments
20249
- * where those functions are not available (e.g. Node.js).
20250
- */
20251
- const _requestIdleCallback = typeof requestIdleCallback !== 'undefined' ? requestIdleCallback : setTimeout;
20252
- const _cancelIdleCallback = typeof requestIdleCallback !== 'undefined' ? cancelIdleCallback : clearTimeout;
20253
- /**
20254
- * Creates runtime data structures for `{#defer}` blocks.
20620
+ * Creates runtime data structures for defer blocks.
20255
20621
  *
20256
20622
  * @param index Index of the `defer` instruction.
20257
20623
  * @param primaryTmplIndex Index of the template with the primary block content.
20258
20624
  * @param dependencyResolverFn Function that contains dependencies for this defer block.
20259
- * @param loadingTmplIndex Index of the template with the `{:loading}` block content.
20260
- * @param placeholderTmplIndex Index of the template with the `{:placeholder}` block content.
20261
- * @param errorTmplIndex Index of the template with the `{:error}` block content.
20262
- * @param loadingConfigIndex Index in the constants array of the configuration of the `{:loading}`.
20625
+ * @param loadingTmplIndex Index of the template with the loading block content.
20626
+ * @param placeholderTmplIndex Index of the template with the placeholder block content.
20627
+ * @param errorTmplIndex Index of the template with the error block content.
20628
+ * @param loadingConfigIndex Index in the constants array of the configuration of the loading.
20263
20629
  * block.
20264
20630
  * @param placeholderConfigIndexIndex in the constants array of the configuration of the
20265
- * `{:placeholder}` block.
20631
+ * placeholder block.
20266
20632
  *
20267
20633
  * @codeGenApi
20268
20634
  */
@@ -20285,18 +20651,20 @@ function ɵɵdefer(index, primaryTmplIndex, dependencyResolverFn, loadingTmplInd
20285
20651
  getConstant(tViewConsts, loadingConfigIndex) :
20286
20652
  null,
20287
20653
  dependencyResolverFn: dependencyResolverFn ?? null,
20288
- loadingState: 0 /* DeferDependenciesLoadingState.NOT_STARTED */,
20654
+ loadingState: DeferDependenciesLoadingState.NOT_STARTED,
20289
20655
  loadingPromise: null,
20290
20656
  };
20291
20657
  setTDeferBlockDetails(tView, adjustedIndex, deferBlockConfig);
20292
20658
  }
20293
- // Lookup dehydrated views that belong to this LContainer.
20294
- // In client-only mode, this operation is noop.
20659
+ const tNode = getCurrentTNode();
20295
20660
  const lContainer = lView[adjustedIndex];
20296
- populateDehydratedViewsInContainer(lContainer);
20661
+ // If hydration is enabled, looks up dehydrated views in the DOM
20662
+ // using hydration annotation info and stores those views on LContainer.
20663
+ // In client-only mode, this function is a noop.
20664
+ populateDehydratedViewsInLContainer(lContainer, tNode, lView);
20297
20665
  // Init instance-specific defer details and store it.
20298
20666
  const lDetails = [];
20299
- lDetails[DEFER_BLOCK_STATE] = 0 /* DeferBlockInstanceState.INITIAL */;
20667
+ lDetails[DEFER_BLOCK_STATE] = DeferBlockInternalState.Initial;
20300
20668
  setLDeferBlockDetails(lView, adjustedIndex, lDetails);
20301
20669
  }
20302
20670
  /**
@@ -20311,13 +20679,13 @@ function ɵɵdeferWhen(rawValue) {
20311
20679
  const tNode = getSelectedTNode();
20312
20680
  const lDetails = getLDeferBlockDetails(lView, tNode);
20313
20681
  const renderedState = lDetails[DEFER_BLOCK_STATE];
20314
- if (value === false && renderedState === 0 /* DeferBlockInstanceState.INITIAL */) {
20682
+ if (value === false && renderedState === DeferBlockInternalState.Initial) {
20315
20683
  // If nothing is rendered yet, render a placeholder (if defined).
20316
20684
  renderPlaceholder(lView, tNode);
20317
20685
  }
20318
20686
  else if (value === true &&
20319
- (renderedState === 0 /* DeferBlockInstanceState.INITIAL */ ||
20320
- renderedState === 1 /* DeferBlockInstanceState.PLACEHOLDER */)) {
20687
+ (renderedState === DeferBlockInternalState.Initial ||
20688
+ renderedState === DeferBlockState.Placeholder)) {
20321
20689
  // The `when` condition has changed to `true`, trigger defer block loading
20322
20690
  // if the block is either in initial (nothing is rendered) or a placeholder
20323
20691
  // state.
@@ -20337,27 +20705,24 @@ function ɵɵdeferPrefetchWhen(rawValue) {
20337
20705
  const tView = lView[TVIEW];
20338
20706
  const tNode = getSelectedTNode();
20339
20707
  const tDetails = getTDeferBlockDetails(tView, tNode);
20340
- if (value === true && tDetails.loadingState === 0 /* DeferDependenciesLoadingState.NOT_STARTED */) {
20708
+ if (value === true && tDetails.loadingState === DeferDependenciesLoadingState.NOT_STARTED) {
20341
20709
  // If loading has not been started yet, trigger it now.
20342
- triggerResourceLoading(tDetails, tView, lView);
20710
+ triggerPrefetching(tDetails, lView);
20343
20711
  }
20344
20712
  }
20345
20713
  }
20346
20714
  /**
20347
- * Sets up handlers that represent `on idle` deferred trigger.
20715
+ * Sets up logic to handle the `on idle` deferred trigger.
20348
20716
  * @codeGenApi
20349
20717
  */
20350
20718
  function ɵɵdeferOnIdle() {
20351
20719
  const lView = getLView();
20352
20720
  const tNode = getCurrentTNode();
20353
20721
  renderPlaceholder(lView, tNode);
20354
- // Note: we pass an `lView` as a second argument to cancel an `idle`
20355
- // callback in case an LView got destroyed before an `idle` callback
20356
- // is invoked.
20357
- onIdle(() => triggerDeferBlock(lView, tNode), lView);
20722
+ onIdle(() => triggerDeferBlock(lView, tNode), lView, true /* withLViewCleanup */);
20358
20723
  }
20359
20724
  /**
20360
- * Creates runtime data structures for the `prefetch on idle` deferred trigger.
20725
+ * Sets up logic to handle the `prefetch on idle` deferred trigger.
20361
20726
  * @codeGenApi
20362
20727
  */
20363
20728
  function ɵɵdeferPrefetchOnIdle() {
@@ -20365,26 +20730,54 @@ function ɵɵdeferPrefetchOnIdle() {
20365
20730
  const tNode = getCurrentTNode();
20366
20731
  const tView = lView[TVIEW];
20367
20732
  const tDetails = getTDeferBlockDetails(tView, tNode);
20368
- if (tDetails.loadingState === 0 /* DeferDependenciesLoadingState.NOT_STARTED */) {
20369
- // Set loading to the scheduled state, so that we don't register it again.
20370
- tDetails.loadingState = 1 /* DeferDependenciesLoadingState.SCHEDULED */;
20371
- // In case of prefetching, we intentionally avoid cancelling prefetching if
20372
- // an underlying LView get destroyed (thus passing `null` as a second argument),
20373
- // because there might be other LViews (that represent embedded views) that
20374
- // depend on resource loading.
20375
- onIdle(() => triggerResourceLoading(tDetails, tView, lView), null /* LView */);
20376
- }
20377
- }
20378
- /**
20379
- * Creates runtime data structures for the `on immediate` deferred trigger.
20733
+ if (tDetails.loadingState === DeferDependenciesLoadingState.NOT_STARTED) {
20734
+ // Prevent scheduling more than one `requestIdleCallback` call
20735
+ // for each defer block. For this reason we use only a trigger
20736
+ // identifier in a key, so all instances would use the same key.
20737
+ const key = String(0 /* DeferBlockTriggers.OnIdle */);
20738
+ const injector = lView[INJECTOR$1];
20739
+ const manager = injector.get(DeferBlockCleanupManager);
20740
+ if (!manager.has(tDetails, key)) {
20741
+ // In case of prefetching, we intentionally avoid cancelling resource loading if
20742
+ // an underlying LView get destroyed (thus passing `null` as a second argument),
20743
+ // because there might be other LViews (that represent embedded views) that
20744
+ // depend on resource loading.
20745
+ const prefetch = () => triggerPrefetching(tDetails, lView);
20746
+ const cleanupFn = onIdle(prefetch, lView, false /* withLViewCleanup */);
20747
+ registerTDetailsCleanup(injector, tDetails, key, cleanupFn);
20748
+ }
20749
+ }
20750
+ }
20751
+ /**
20752
+ * Sets up logic to handle the `on immediate` deferred trigger.
20380
20753
  * @codeGenApi
20381
20754
  */
20382
- function ɵɵdeferOnImmediate() { } // TODO: implement runtime logic.
20755
+ function ɵɵdeferOnImmediate() {
20756
+ const lView = getLView();
20757
+ const tNode = getCurrentTNode();
20758
+ const tView = lView[TVIEW];
20759
+ const tDetails = getTDeferBlockDetails(tView, tNode);
20760
+ // Render placeholder block only if loading template is not present
20761
+ // to avoid content flickering, since it would be immediately replaced
20762
+ // by the loading block.
20763
+ if (tDetails.loadingTmplIndex === null) {
20764
+ renderPlaceholder(lView, tNode);
20765
+ }
20766
+ triggerDeferBlock(lView, tNode);
20767
+ }
20383
20768
  /**
20384
- * Creates runtime data structures for the `prefetch on immediate` deferred trigger.
20769
+ * Sets up logic to handle the `prefetch on immediate` deferred trigger.
20385
20770
  * @codeGenApi
20386
20771
  */
20387
- function ɵɵdeferPrefetchOnImmediate() { } // TODO: implement runtime logic.
20772
+ function ɵɵdeferPrefetchOnImmediate() {
20773
+ const lView = getLView();
20774
+ const tNode = getCurrentTNode();
20775
+ const tView = lView[TVIEW];
20776
+ const tDetails = getTDeferBlockDetails(tView, tNode);
20777
+ if (tDetails.loadingState === DeferDependenciesLoadingState.NOT_STARTED) {
20778
+ triggerResourceLoading(tDetails, lView);
20779
+ }
20780
+ }
20388
20781
  /**
20389
20782
  * Creates runtime data structures for the `on timer` deferred trigger.
20390
20783
  * @param delay Amount of time to wait before loading the content.
@@ -20399,67 +20792,207 @@ function ɵɵdeferOnTimer(delay) { } // TODO: implement runtime logic.
20399
20792
  function ɵɵdeferPrefetchOnTimer(delay) { } // TODO: implement runtime logic.
20400
20793
  /**
20401
20794
  * Creates runtime data structures for the `on hover` deferred trigger.
20795
+ * @param triggerIndex Index at which to find the trigger element.
20796
+ * @param walkUpTimes Number of times to walk up/down the tree hierarchy to find the trigger.
20402
20797
  * @codeGenApi
20403
20798
  */
20404
- function ɵɵdeferOnHover() { } // TODO: implement runtime logic.
20799
+ function ɵɵdeferOnHover(triggerIndex, walkUpTimes) {
20800
+ const lView = getLView();
20801
+ const tNode = getCurrentTNode();
20802
+ renderPlaceholder(lView, tNode);
20803
+ registerDomTrigger(lView, tNode, triggerIndex, walkUpTimes, onHover, () => triggerDeferBlock(lView, tNode));
20804
+ }
20405
20805
  /**
20406
20806
  * Creates runtime data structures for the `prefetch on hover` deferred trigger.
20807
+ * @param triggerIndex Index at which to find the trigger element.
20808
+ * @param walkUpTimes Number of times to walk up/down the tree hierarchy to find the trigger.
20407
20809
  * @codeGenApi
20408
20810
  */
20409
- function ɵɵdeferPrefetchOnHover() { } // TODO: implement runtime logic.
20811
+ function ɵɵdeferPrefetchOnHover(triggerIndex, walkUpTimes) {
20812
+ const lView = getLView();
20813
+ const tNode = getCurrentTNode();
20814
+ const tView = lView[TVIEW];
20815
+ const tDetails = getTDeferBlockDetails(tView, tNode);
20816
+ if (tDetails.loadingState === DeferDependenciesLoadingState.NOT_STARTED) {
20817
+ registerDomTrigger(lView, tNode, triggerIndex, walkUpTimes, onHover, () => triggerPrefetching(tDetails, lView));
20818
+ }
20819
+ }
20410
20820
  /**
20411
20821
  * Creates runtime data structures for the `on interaction` deferred trigger.
20412
- * @param target Optional element on which to listen for hover events.
20822
+ * @param triggerIndex Index at which to find the trigger element.
20823
+ * @param walkUpTimes Number of times to walk up/down the tree hierarchy to find the trigger.
20413
20824
  * @codeGenApi
20414
20825
  */
20415
- function ɵɵdeferOnInteraction(target) { } // TODO: implement runtime logic.
20826
+ function ɵɵdeferOnInteraction(triggerIndex, walkUpTimes) {
20827
+ const lView = getLView();
20828
+ const tNode = getCurrentTNode();
20829
+ renderPlaceholder(lView, tNode);
20830
+ registerDomTrigger(lView, tNode, triggerIndex, walkUpTimes, onInteraction, () => triggerDeferBlock(lView, tNode));
20831
+ }
20416
20832
  /**
20417
20833
  * Creates runtime data structures for the `prefetch on interaction` deferred trigger.
20418
- * @param target Optional element on which to listen for hover events.
20834
+ * @param triggerIndex Index at which to find the trigger element.
20835
+ * @param walkUpTimes Number of times to walk up/down the tree hierarchy to find the trigger.
20419
20836
  * @codeGenApi
20420
20837
  */
20421
- function ɵɵdeferPrefetchOnInteraction(target) { } // TODO: implement runtime logic.
20838
+ function ɵɵdeferPrefetchOnInteraction(triggerIndex, walkUpTimes) {
20839
+ const lView = getLView();
20840
+ const tNode = getCurrentTNode();
20841
+ const tView = lView[TVIEW];
20842
+ const tDetails = getTDeferBlockDetails(tView, tNode);
20843
+ if (tDetails.loadingState === DeferDependenciesLoadingState.NOT_STARTED) {
20844
+ registerDomTrigger(lView, tNode, triggerIndex, walkUpTimes, onInteraction, () => triggerPrefetching(tDetails, lView));
20845
+ }
20846
+ }
20422
20847
  /**
20423
20848
  * Creates runtime data structures for the `on viewport` deferred trigger.
20424
- * @param target Optional element on which to listen for hover events.
20849
+ * @param triggerIndex Index at which to find the trigger element.
20850
+ * @param walkUpTimes Number of times to walk up/down the tree hierarchy to find the trigger.
20425
20851
  * @codeGenApi
20426
20852
  */
20427
- function ɵɵdeferOnViewport(target) { } // TODO: implement runtime logic.
20853
+ function ɵɵdeferOnViewport(triggerIndex, walkUpTimes) {
20854
+ const lView = getLView();
20855
+ const tNode = getCurrentTNode();
20856
+ renderPlaceholder(lView, tNode);
20857
+ registerDomTrigger(lView, tNode, triggerIndex, walkUpTimes, onViewport, () => triggerDeferBlock(lView, tNode));
20858
+ }
20428
20859
  /**
20429
20860
  * Creates runtime data structures for the `prefetch on viewport` deferred trigger.
20430
- * @param target Optional element on which to listen for hover events.
20861
+ * @param triggerIndex Index at which to find the trigger element.
20862
+ * @param walkUpTimes Number of times to walk up/down the tree hierarchy to find the trigger.
20431
20863
  * @codeGenApi
20432
20864
  */
20433
- function ɵɵdeferPrefetchOnViewport(target) { } // TODO: implement runtime logic.
20865
+ function ɵɵdeferPrefetchOnViewport(triggerIndex, walkUpTimes) {
20866
+ const lView = getLView();
20867
+ const tNode = getCurrentTNode();
20868
+ const tView = lView[TVIEW];
20869
+ const tDetails = getTDeferBlockDetails(tView, tNode);
20870
+ if (tDetails.loadingState === DeferDependenciesLoadingState.NOT_STARTED) {
20871
+ registerDomTrigger(lView, tNode, triggerIndex, walkUpTimes, onViewport, () => triggerPrefetching(tDetails, lView));
20872
+ }
20873
+ }
20434
20874
  /********** Helper functions **********/
20875
+ /**
20876
+ * Helper function to get the LView in which a deferred block's trigger is rendered.
20877
+ * @param deferredHostLView LView in which the deferred block is defined.
20878
+ * @param deferredTNode TNode defining the deferred block.
20879
+ * @param walkUpTimes Number of times to go up in the view hierarchy to find the trigger's view.
20880
+ * A negative value means that the trigger is inside the block's placeholder, while an undefined
20881
+ * value means that the trigger is in the same LView as the deferred block.
20882
+ */
20883
+ function getTriggerLView(deferredHostLView, deferredTNode, walkUpTimes) {
20884
+ // The trigger is in the same view, we don't need to traverse.
20885
+ if (walkUpTimes == null) {
20886
+ return deferredHostLView;
20887
+ }
20888
+ // A positive value or zero means that the trigger is in a parent view.
20889
+ if (walkUpTimes >= 0) {
20890
+ return walkUpViews(walkUpTimes, deferredHostLView);
20891
+ }
20892
+ // If the value is negative, it means that the trigger is inside the placeholder.
20893
+ const deferredContainer = deferredHostLView[deferredTNode.index];
20894
+ ngDevMode && assertLContainer(deferredContainer);
20895
+ const triggerLView = deferredContainer[CONTAINER_HEADER_OFFSET] ?? null;
20896
+ // We need to null check, because the placeholder might not have been rendered yet.
20897
+ if (ngDevMode && triggerLView !== null) {
20898
+ const lDetails = getLDeferBlockDetails(deferredHostLView, deferredTNode);
20899
+ const renderedState = lDetails[DEFER_BLOCK_STATE];
20900
+ assertEqual(renderedState, DeferBlockState.Placeholder, 'Expected a placeholder to be rendered in this defer block.');
20901
+ assertLView(triggerLView);
20902
+ }
20903
+ return triggerLView;
20904
+ }
20905
+ /**
20906
+ * Gets the element that a deferred block's trigger is pointing to.
20907
+ * @param triggerLView LView in which the trigger is defined.
20908
+ * @param triggerIndex Index at which the trigger element should've been rendered.
20909
+ */
20910
+ function getTriggerElement(triggerLView, triggerIndex) {
20911
+ const element = getNativeByIndex(HEADER_OFFSET + triggerIndex, triggerLView);
20912
+ ngDevMode && assertElement(element);
20913
+ return element;
20914
+ }
20915
+ /**
20916
+ * Registers a DOM-node based trigger.
20917
+ * @param initialLView LView in which the defer block is rendered.
20918
+ * @param tNode TNode representing the defer block.
20919
+ * @param triggerIndex Index at which to find the trigger element.
20920
+ * @param walkUpTimes Number of times to go up/down in the view hierarchy to find the trigger.
20921
+ * @param registerFn Function that will register the DOM events.
20922
+ * @param callback Callback to be invoked when the trigger receives the event that should render
20923
+ * the deferred block.
20924
+ */
20925
+ function registerDomTrigger(initialLView, tNode, triggerIndex, walkUpTimes, registerFn, callback) {
20926
+ const injector = initialLView[INJECTOR$1];
20927
+ // Assumption: the `afterRender` reference should be destroyed
20928
+ // automatically so we don't need to keep track of it.
20929
+ const afterRenderRef = afterRender(() => {
20930
+ const lDetails = getLDeferBlockDetails(initialLView, tNode);
20931
+ const renderedState = lDetails[DEFER_BLOCK_STATE];
20932
+ // If the block was loaded before the trigger was resolved, we don't need to do anything.
20933
+ if (renderedState !== DeferBlockInternalState.Initial &&
20934
+ renderedState !== DeferBlockState.Placeholder) {
20935
+ afterRenderRef.destroy();
20936
+ return;
20937
+ }
20938
+ const triggerLView = getTriggerLView(initialLView, tNode, walkUpTimes);
20939
+ // Keep polling until we resolve the trigger's LView.
20940
+ // `afterRender` should stop automatically if the view is destroyed.
20941
+ if (!triggerLView) {
20942
+ return;
20943
+ }
20944
+ // It's possible that the trigger's view was destroyed before we resolved the trigger element.
20945
+ if (triggerLView[FLAGS] & 256 /* LViewFlags.Destroyed */) {
20946
+ afterRenderRef.destroy();
20947
+ return;
20948
+ }
20949
+ // TODO: add integration with `DeferBlockCleanupManager`.
20950
+ const element = getTriggerElement(triggerLView, triggerIndex);
20951
+ const cleanup = registerFn(element, () => {
20952
+ callback();
20953
+ removeLViewOnDestroy(triggerLView, cleanup);
20954
+ if (initialLView !== triggerLView) {
20955
+ removeLViewOnDestroy(initialLView, cleanup);
20956
+ }
20957
+ cleanup();
20958
+ }, injector);
20959
+ afterRenderRef.destroy();
20960
+ storeLViewOnDestroy(triggerLView, cleanup);
20961
+ // Since the trigger and deferred block might be in different
20962
+ // views, we have to register the callback in both locations.
20963
+ if (initialLView !== triggerLView) {
20964
+ storeLViewOnDestroy(initialLView, cleanup);
20965
+ }
20966
+ }, { injector });
20967
+ }
20435
20968
  /**
20436
20969
  * Helper function to schedule a callback to be invoked when a browser becomes idle.
20437
20970
  *
20438
20971
  * @param callback A function to be invoked when a browser becomes idle.
20439
- * @param lView An optional LView that hosts an instance of a defer block. LView is
20440
- * used to register a cleanup callback in case that LView got destroyed before
20441
- * callback was invoked. In this case, an `idle` callback is never invoked. This is
20442
- * helpful for cases when a defer block has scheduled rendering, but an underlying
20443
- * LView got destroyed prior to th block rendering.
20972
+ * @param lView LView that hosts an instance of a defer block.
20973
+ * @param withLViewCleanup A flag that indicates whether a scheduled callback
20974
+ * should be cancelled in case an LView is destroyed before a callback
20975
+ * was invoked.
20444
20976
  */
20445
- function onIdle(callback, lView) {
20446
- let id;
20447
- const removeIdleCallback = () => _cancelIdleCallback(id);
20448
- id = _requestIdleCallback(() => {
20449
- removeIdleCallback();
20450
- if (lView !== null) {
20451
- // The idle callback is invoked, we no longer need
20452
- // to retain a cleanup callback in an LView.
20453
- removeLViewOnDestroy(lView, removeIdleCallback);
20454
- }
20977
+ function onIdle(callback, lView, withLViewCleanup) {
20978
+ const injector = lView[INJECTOR$1];
20979
+ const scheduler = injector.get(OnIdleScheduler);
20980
+ const cleanupFn = () => scheduler.remove(callback);
20981
+ const wrappedCallback = withLViewCleanup ? wrapWithLViewCleanup(callback, lView, cleanupFn) : callback;
20982
+ scheduler.add(wrappedCallback);
20983
+ return cleanupFn;
20984
+ }
20985
+ /**
20986
+ * Wraps a given callback into a logic that registers a cleanup function
20987
+ * in the LView cleanup slot, to be invoked when an LView is destroyed.
20988
+ */
20989
+ function wrapWithLViewCleanup(callback, lView, cleanup) {
20990
+ const wrappedCallback = () => {
20455
20991
  callback();
20456
- });
20457
- if (lView !== null) {
20458
- // Store a cleanup function on LView, so that we cancel idle
20459
- // callback in case this LView is destroyed before a callback
20460
- // is invoked.
20461
- storeLViewOnDestroy(lView, removeIdleCallback);
20462
- }
20992
+ removeLViewOnDestroy(lView, cleanup);
20993
+ };
20994
+ storeLViewOnDestroy(lView, cleanup);
20995
+ return wrappedCallback;
20463
20996
  }
20464
20997
  /**
20465
20998
  * Calculates a data slot index for defer block info (either static or
@@ -20496,6 +21029,23 @@ function setTDeferBlockDetails(tView, deferBlockIndex, deferBlockConfig) {
20496
21029
  ngDevMode && assertIndexInDeclRange(tView, slotIndex);
20497
21030
  tView.data[slotIndex] = deferBlockConfig;
20498
21031
  }
21032
+ function getTemplateIndexForState(newState, hostLView, tNode) {
21033
+ const tView = hostLView[TVIEW];
21034
+ const tDetails = getTDeferBlockDetails(tView, tNode);
21035
+ switch (newState) {
21036
+ case DeferBlockState.Complete:
21037
+ return tDetails.primaryTmplIndex;
21038
+ case DeferBlockState.Loading:
21039
+ return tDetails.loadingTmplIndex;
21040
+ case DeferBlockState.Error:
21041
+ return tDetails.errorTmplIndex;
21042
+ case DeferBlockState.Placeholder:
21043
+ return tDetails.placeholderTmplIndex;
21044
+ default:
21045
+ ngDevMode && throwError(`Unexpected defer block state: ${newState}`);
21046
+ return null;
21047
+ }
21048
+ }
20499
21049
  /**
20500
21050
  * Transitions a defer block to the new state. Updates the necessary
20501
21051
  * data structures and renders corresponding block.
@@ -20503,9 +21053,8 @@ function setTDeferBlockDetails(tView, deferBlockIndex, deferBlockConfig) {
20503
21053
  * @param newState New state that should be applied to the defer block.
20504
21054
  * @param tNode TNode that represents a defer block.
20505
21055
  * @param lContainer Represents an instance of a defer block.
20506
- * @param stateTmplIndex Index of a template that should be rendered.
20507
21056
  */
20508
- function renderDeferBlockState(newState, tNode, lContainer, stateTmplIndex) {
21057
+ function renderDeferBlockState(newState, tNode, lContainer) {
20509
21058
  const hostLView = lContainer[PARENT];
20510
21059
  // Check if this view is not destroyed. Since the loading process was async,
20511
21060
  // the view might end up being destroyed by the time rendering happens.
@@ -20515,6 +21064,7 @@ function renderDeferBlockState(newState, tNode, lContainer, stateTmplIndex) {
20515
21064
  ngDevMode && assertTNodeForLView(tNode, hostLView);
20516
21065
  const lDetails = getLDeferBlockDetails(hostLView, tNode);
20517
21066
  ngDevMode && assertDefined(lDetails, 'Expected a defer block state defined');
21067
+ const stateTmplIndex = getTemplateIndexForState(newState, hostLView, tNode);
20518
21068
  // Note: we transition to the next state if the previous state was represented
20519
21069
  // with a number that is less than the next state. For example, if the current
20520
21070
  // state is "loading" (represented as `2`), we should not show a placeholder
@@ -20525,7 +21075,7 @@ function renderDeferBlockState(newState, tNode, lContainer, stateTmplIndex) {
20525
21075
  const adjustedIndex = stateTmplIndex + HEADER_OFFSET;
20526
21076
  const tNode = getTNode(hostTView, adjustedIndex);
20527
21077
  // There is only 1 view that can be present in an LContainer that
20528
- // represents a `{#defer}` block, so always refer to the first one.
21078
+ // represents a defer block, so always refer to the first one.
20529
21079
  const viewIndex = 0;
20530
21080
  removeLViewFromLContainer(lContainer, viewIndex);
20531
21081
  const dehydratedView = findMatchingDehydratedView(lContainer, tNode.tView.ssrId);
@@ -20533,18 +21083,27 @@ function renderDeferBlockState(newState, tNode, lContainer, stateTmplIndex) {
20533
21083
  addLViewToLContainer(lContainer, embeddedLView, viewIndex, shouldAddViewToDom(tNode, dehydratedView));
20534
21084
  }
20535
21085
  }
21086
+ /**
21087
+ * Trigger prefetching of dependencies for a defer block.
21088
+ *
21089
+ * @param tDetails Static information about this defer block.
21090
+ * @param lView LView of a host view.
21091
+ */
21092
+ function triggerPrefetching(tDetails, lView) {
21093
+ if (lView[INJECTOR$1] && shouldTriggerDeferBlock(lView[INJECTOR$1])) {
21094
+ triggerResourceLoading(tDetails, lView);
21095
+ }
21096
+ }
20536
21097
  /**
20537
21098
  * Trigger loading of defer block dependencies if the process hasn't started yet.
20538
21099
  *
20539
21100
  * @param tDetails Static information about this defer block.
20540
- * @param tView TView of a host view.
20541
21101
  * @param lView LView of a host view.
20542
21102
  */
20543
- function triggerResourceLoading(tDetails, tView, lView) {
21103
+ function triggerResourceLoading(tDetails, lView) {
20544
21104
  const injector = lView[INJECTOR$1];
20545
- if (!shouldTriggerDeferBlock(injector) ||
20546
- (tDetails.loadingState !== 0 /* DeferDependenciesLoadingState.NOT_STARTED */ &&
20547
- tDetails.loadingState !== 1 /* DeferDependenciesLoadingState.SCHEDULED */)) {
21105
+ const tView = lView[TVIEW];
21106
+ if (tDetails.loadingState !== DeferDependenciesLoadingState.NOT_STARTED) {
20548
21107
  // If the loading status is different from initial one, it means that
20549
21108
  // the loading of dependencies is in progress and there is nothing to do
20550
21109
  // in this function. All details can be obtained from the `tDetails` object.
@@ -20552,21 +21111,24 @@ function triggerResourceLoading(tDetails, tView, lView) {
20552
21111
  }
20553
21112
  const primaryBlockTNode = getPrimaryBlockTNode(tView, tDetails);
20554
21113
  // Switch from NOT_STARTED -> IN_PROGRESS state.
20555
- tDetails.loadingState = 2 /* DeferDependenciesLoadingState.IN_PROGRESS */;
21114
+ tDetails.loadingState = DeferDependenciesLoadingState.IN_PROGRESS;
20556
21115
  // Check if dependency function interceptor is configured.
20557
21116
  const deferDependencyInterceptor = injector.get(DEFER_BLOCK_DEPENDENCY_INTERCEPTOR, null, { optional: true });
20558
21117
  const dependenciesFn = deferDependencyInterceptor ?
20559
21118
  deferDependencyInterceptor.intercept(tDetails.dependencyResolverFn) :
20560
21119
  tDetails.dependencyResolverFn;
20561
21120
  // The `dependenciesFn` might be `null` when all dependencies within
20562
- // a given `{#defer}` block were eagerly references elsewhere in a file,
21121
+ // a given defer block were eagerly references elsewhere in a file,
20563
21122
  // thus no dynamic `import()`s were produced.
20564
21123
  if (!dependenciesFn) {
20565
21124
  tDetails.loadingPromise = Promise.resolve().then(() => {
20566
- tDetails.loadingState = 3 /* DeferDependenciesLoadingState.COMPLETE */;
21125
+ tDetails.loadingState = DeferDependenciesLoadingState.COMPLETE;
20567
21126
  });
20568
21127
  return;
20569
21128
  }
21129
+ // Defer block may have multiple prefetch triggers. Once the loading
21130
+ // starts, invoke all clean functions, since they are no longer needed.
21131
+ invokeTDetailsCleanup(injector, tDetails);
20570
21132
  // Start downloading of defer block dependencies.
20571
21133
  tDetails.loadingPromise = Promise.allSettled(dependenciesFn()).then(results => {
20572
21134
  let failed = false;
@@ -20594,10 +21156,10 @@ function triggerResourceLoading(tDetails, tView, lView) {
20594
21156
  // Loading is completed, we no longer need this Promise.
20595
21157
  tDetails.loadingPromise = null;
20596
21158
  if (failed) {
20597
- tDetails.loadingState = 4 /* DeferDependenciesLoadingState.FAILED */;
21159
+ tDetails.loadingState = DeferDependenciesLoadingState.FAILED;
20598
21160
  }
20599
21161
  else {
20600
- tDetails.loadingState = 3 /* DeferDependenciesLoadingState.COMPLETE */;
21162
+ tDetails.loadingState = DeferDependenciesLoadingState.COMPLETE;
20601
21163
  // Update directive and pipe registries to add newly downloaded dependencies.
20602
21164
  const primaryBlockTView = primaryBlockTNode.tView;
20603
21165
  if (directiveDefs.length > 0) {
@@ -20613,13 +21175,13 @@ function triggerResourceLoading(tDetails, tView, lView) {
20613
21175
  }
20614
21176
  });
20615
21177
  }
20616
- /** Utility function to render `{:placeholder}` content (if present) */
21178
+ /** Utility function to render placeholder content (if present) */
20617
21179
  function renderPlaceholder(lView, tNode) {
20618
21180
  const tView = lView[TVIEW];
20619
21181
  const lContainer = lView[tNode.index];
20620
21182
  ngDevMode && assertLContainer(lContainer);
20621
21183
  const tDetails = getTDeferBlockDetails(tView, tNode);
20622
- renderDeferBlockState(1 /* DeferBlockInstanceState.PLACEHOLDER */, tNode, lContainer, tDetails.placeholderTmplIndex);
21184
+ renderDeferBlockState(DeferBlockState.Placeholder, tNode, lContainer);
20623
21185
  }
20624
21186
  /**
20625
21187
  * Subscribes to the "loading" Promise and renders corresponding defer sub-block,
@@ -20632,13 +21194,13 @@ function renderDeferStateAfterResourceLoading(tDetails, tNode, lContainer) {
20632
21194
  ngDevMode &&
20633
21195
  assertDefined(tDetails.loadingPromise, 'Expected loading Promise to exist on this defer block');
20634
21196
  tDetails.loadingPromise.then(() => {
20635
- if (tDetails.loadingState === 3 /* DeferDependenciesLoadingState.COMPLETE */) {
21197
+ if (tDetails.loadingState === DeferDependenciesLoadingState.COMPLETE) {
20636
21198
  ngDevMode && assertDeferredDependenciesLoaded(tDetails);
20637
21199
  // Everything is loaded, show the primary block content
20638
- renderDeferBlockState(3 /* DeferBlockInstanceState.COMPLETE */, tNode, lContainer, tDetails.primaryTmplIndex);
21200
+ renderDeferBlockState(DeferBlockState.Complete, tNode, lContainer);
20639
21201
  }
20640
- else if (tDetails.loadingState === 4 /* DeferDependenciesLoadingState.FAILED */) {
20641
- renderDeferBlockState(4 /* DeferBlockInstanceState.ERROR */, tNode, lContainer, tDetails.errorTmplIndex);
21202
+ else if (tDetails.loadingState === DeferDependenciesLoadingState.FAILED) {
21203
+ renderDeferBlockState(DeferBlockState.Error, tNode, lContainer);
20642
21204
  }
20643
21205
  });
20644
21206
  }
@@ -20662,26 +21224,25 @@ function triggerDeferBlock(lView, tNode) {
20662
21224
  const tDetails = getTDeferBlockDetails(tView, tNode);
20663
21225
  // Condition is triggered, try to render loading state and start downloading.
20664
21226
  // Note: if a block is in a loading, completed or an error state, this call would be a noop.
20665
- renderDeferBlockState(2 /* DeferBlockInstanceState.LOADING */, tNode, lContainer, tDetails.loadingTmplIndex);
21227
+ renderDeferBlockState(DeferBlockState.Loading, tNode, lContainer);
20666
21228
  switch (tDetails.loadingState) {
20667
- case 0 /* DeferDependenciesLoadingState.NOT_STARTED */:
20668
- case 1 /* DeferDependenciesLoadingState.SCHEDULED */:
20669
- triggerResourceLoading(tDetails, lView[TVIEW], lView);
21229
+ case DeferDependenciesLoadingState.NOT_STARTED:
21230
+ triggerResourceLoading(tDetails, lView);
20670
21231
  // The `loadingState` might have changed to "loading".
20671
21232
  if (tDetails.loadingState ===
20672
- 2 /* DeferDependenciesLoadingState.IN_PROGRESS */) {
21233
+ DeferDependenciesLoadingState.IN_PROGRESS) {
20673
21234
  renderDeferStateAfterResourceLoading(tDetails, tNode, lContainer);
20674
21235
  }
20675
21236
  break;
20676
- case 2 /* DeferDependenciesLoadingState.IN_PROGRESS */:
21237
+ case DeferDependenciesLoadingState.IN_PROGRESS:
20677
21238
  renderDeferStateAfterResourceLoading(tDetails, tNode, lContainer);
20678
21239
  break;
20679
- case 3 /* DeferDependenciesLoadingState.COMPLETE */:
21240
+ case DeferDependenciesLoadingState.COMPLETE:
20680
21241
  ngDevMode && assertDeferredDependenciesLoaded(tDetails);
20681
- renderDeferBlockState(3 /* DeferBlockInstanceState.COMPLETE */, tNode, lContainer, tDetails.primaryTmplIndex);
21242
+ renderDeferBlockState(DeferBlockState.Complete, tNode, lContainer);
20682
21243
  break;
20683
- case 4 /* DeferDependenciesLoadingState.FAILED */:
20684
- renderDeferBlockState(4 /* DeferBlockInstanceState.ERROR */, tNode, lContainer, tDetails.errorTmplIndex);
21244
+ case DeferDependenciesLoadingState.FAILED:
21245
+ renderDeferBlockState(DeferBlockState.Error, tNode, lContainer);
20685
21246
  break;
20686
21247
  default:
20687
21248
  if (ngDevMode) {
@@ -20695,7 +21256,7 @@ function triggerDeferBlock(lView, tNode) {
20695
21256
  * block in completed state.
20696
21257
  */
20697
21258
  function assertDeferredDependenciesLoaded(tDetails) {
20698
- assertEqual(tDetails.loadingState, 3 /* DeferDependenciesLoadingState.COMPLETE */, 'Expecting all deferred dependencies to be loaded.');
21259
+ assertEqual(tDetails.loadingState, DeferDependenciesLoadingState.COMPLETE, 'Expecting all deferred dependencies to be loaded.');
20699
21260
  }
20700
21261
  /**
20701
21262
  * **INTERNAL**, avoid referencing it in application code.
@@ -20704,6 +21265,193 @@ function assertDeferredDependenciesLoaded(tDetails) {
20704
21265
  * implementation.
20705
21266
  */
20706
21267
  const DEFER_BLOCK_DEPENDENCY_INTERCEPTOR = new InjectionToken(ngDevMode ? 'DEFER_BLOCK_DEPENDENCY_INTERCEPTOR' : '');
21268
+ /**
21269
+ * Determines if a given value matches the expected structure of a defer block
21270
+ *
21271
+ * We can safely rely on the primaryTmplIndex because every defer block requires
21272
+ * that a primary template exists. All the other template options are optional.
21273
+ */
21274
+ function isTDeferBlockDetails(value) {
21275
+ return (typeof value === 'object') &&
21276
+ (typeof value.primaryTmplIndex === 'number');
21277
+ }
21278
+ /**
21279
+ * Internal token used for configuring defer block behavior.
21280
+ */
21281
+ const DEFER_BLOCK_CONFIG = new InjectionToken(ngDevMode ? 'DEFER_BLOCK_CONFIG' : '');
21282
+ /**
21283
+ * Retrieves all defer blocks in a given LView.
21284
+ *
21285
+ * @param lView lView with defer blocks
21286
+ * @param deferBlocks defer block aggregator array
21287
+ */
21288
+ function getDeferBlocks(lView, deferBlocks) {
21289
+ const tView = lView[TVIEW];
21290
+ for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
21291
+ if (isLContainer(lView[i])) {
21292
+ const lContainer = lView[i];
21293
+ // An LContainer may represent an instance of a defer block, in which case
21294
+ // we store it as a result. Otherwise, keep iterating over LContainer views and
21295
+ // look for defer blocks.
21296
+ const isLast = i === tView.bindingStartIndex - 1;
21297
+ if (!isLast) {
21298
+ const tNode = tView.data[i];
21299
+ const tDetails = getTDeferBlockDetails(tView, tNode);
21300
+ if (isTDeferBlockDetails(tDetails)) {
21301
+ deferBlocks.push({ lContainer, lView, tNode, tDetails });
21302
+ // This LContainer represents a defer block, so we exit
21303
+ // this iteration and don't inspect views in this LContainer.
21304
+ continue;
21305
+ }
21306
+ }
21307
+ for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
21308
+ getDeferBlocks(lContainer[i], deferBlocks);
21309
+ }
21310
+ }
21311
+ else if (isLView(lView[i])) {
21312
+ // This is a component, enter the `getDeferBlocks` recursively.
21313
+ getDeferBlocks(lView[i], deferBlocks);
21314
+ }
21315
+ }
21316
+ }
21317
+ /**
21318
+ * Registers a cleanup function associated with a prefetching trigger
21319
+ * of a given defer block.
21320
+ */
21321
+ function registerTDetailsCleanup(injector, tDetails, key, cleanupFn) {
21322
+ injector.get(DeferBlockCleanupManager).add(tDetails, key, cleanupFn);
21323
+ }
21324
+ /**
21325
+ * Invokes all registered prefetch cleanup triggers
21326
+ * and removes all cleanup functions afterwards.
21327
+ */
21328
+ function invokeTDetailsCleanup(injector, tDetails) {
21329
+ injector.get(DeferBlockCleanupManager).cleanup(tDetails);
21330
+ }
21331
+ /**
21332
+ * Internal service to keep track of cleanup functions associated
21333
+ * with defer blocks. This class is used to manage cleanup functions
21334
+ * created for prefetching triggers.
21335
+ */
21336
+ class DeferBlockCleanupManager {
21337
+ constructor() {
21338
+ this.blocks = new Map();
21339
+ }
21340
+ add(tDetails, key, callback) {
21341
+ if (!this.blocks.has(tDetails)) {
21342
+ this.blocks.set(tDetails, new Map());
21343
+ }
21344
+ const block = this.blocks.get(tDetails);
21345
+ if (!block.has(key)) {
21346
+ block.set(key, []);
21347
+ }
21348
+ const callbacks = block.get(key);
21349
+ callbacks.push(callback);
21350
+ }
21351
+ has(tDetails, key) {
21352
+ return !!this.blocks.get(tDetails)?.has(key);
21353
+ }
21354
+ cleanup(tDetails) {
21355
+ const block = this.blocks.get(tDetails);
21356
+ if (block) {
21357
+ for (const callbacks of Object.values(block)) {
21358
+ for (const callback of callbacks) {
21359
+ callback();
21360
+ }
21361
+ }
21362
+ this.blocks.delete(tDetails);
21363
+ }
21364
+ }
21365
+ ngOnDestroy() {
21366
+ for (const [block] of this.blocks) {
21367
+ this.cleanup(block);
21368
+ }
21369
+ this.blocks.clear();
21370
+ }
21371
+ /** @nocollapse */
21372
+ static { this.ɵprov = ɵɵdefineInjectable({
21373
+ token: DeferBlockCleanupManager,
21374
+ providedIn: 'root',
21375
+ factory: () => new DeferBlockCleanupManager(),
21376
+ }); }
21377
+ }
21378
+ /**
21379
+ * Use shims for the `requestIdleCallback` and `cancelIdleCallback` functions for
21380
+ * environments where those functions are not available (e.g. Node.js and Safari).
21381
+ *
21382
+ * Note: we wrap the `requestIdleCallback` call into a function, so that it can be
21383
+ * overridden/mocked in test environment and picked up by the runtime code.
21384
+ */
21385
+ const _requestIdleCallback = () => typeof requestIdleCallback !== 'undefined' ? requestIdleCallback : setTimeout;
21386
+ const _cancelIdleCallback = () => typeof requestIdleCallback !== 'undefined' ? cancelIdleCallback : clearTimeout;
21387
+ /**
21388
+ * Helper service to schedule `requestIdleCallback`s for batches of defer blocks,
21389
+ * to avoid calling `requestIdleCallback` for each defer block (e.g. if
21390
+ * defer blocks are defined inside a for loop).
21391
+ */
21392
+ class OnIdleScheduler {
21393
+ constructor() {
21394
+ // Indicates whether current callbacks are being invoked.
21395
+ this.executingCallbacks = false;
21396
+ // Currently scheduled idle callback id.
21397
+ this.idleId = null;
21398
+ // Set of callbacks to be invoked next.
21399
+ this.current = new Set();
21400
+ // Set of callbacks collected while invoking current set of callbacks.
21401
+ // Those callbacks are scheduled for the next idle period.
21402
+ this.deferred = new Set();
21403
+ this.requestIdleCallback = _requestIdleCallback().bind(globalThis);
21404
+ this.cancelIdleCallback = _cancelIdleCallback().bind(globalThis);
21405
+ }
21406
+ add(callback) {
21407
+ const target = this.executingCallbacks ? this.deferred : this.current;
21408
+ target.add(callback);
21409
+ if (this.idleId === null) {
21410
+ this.scheduleIdleCallback();
21411
+ }
21412
+ }
21413
+ remove(callback) {
21414
+ this.current.delete(callback);
21415
+ this.deferred.delete(callback);
21416
+ }
21417
+ scheduleIdleCallback() {
21418
+ const callback = () => {
21419
+ this.cancelIdleCallback(this.idleId);
21420
+ this.idleId = null;
21421
+ this.executingCallbacks = true;
21422
+ for (const callback of this.current) {
21423
+ callback();
21424
+ }
21425
+ this.current.clear();
21426
+ this.executingCallbacks = false;
21427
+ // If there are any callbacks added during an invocation
21428
+ // of the current ones - make them "current" and schedule
21429
+ // a new idle callback.
21430
+ if (this.deferred.size > 0) {
21431
+ for (const callback of this.deferred) {
21432
+ this.current.add(callback);
21433
+ }
21434
+ this.deferred.clear();
21435
+ this.scheduleIdleCallback();
21436
+ }
21437
+ };
21438
+ this.idleId = this.requestIdleCallback(callback);
21439
+ }
21440
+ ngOnDestroy() {
21441
+ if (this.idleId !== null) {
21442
+ this.cancelIdleCallback(this.idleId);
21443
+ this.idleId = null;
21444
+ }
21445
+ this.current.clear();
21446
+ this.deferred.clear();
21447
+ }
21448
+ /** @nocollapse */
21449
+ static { this.ɵprov = ɵɵdefineInjectable({
21450
+ token: OnIdleScheduler,
21451
+ providedIn: 'root',
21452
+ factory: () => new OnIdleScheduler(),
21453
+ }); }
21454
+ }
20707
21455
 
20708
21456
  function elementStartFirstCreatePass(index, tView, lView, name, attrsIndex, localRefsIndex) {
20709
21457
  ngDevMode && assertFirstCreatePass(tView);
@@ -25507,6 +26255,10 @@ function ɵɵsetNgModuleScope(type, scope) {
25507
26255
  ngModuleDef.declarations = convertToTypeArray(scope.declarations || EMPTY_ARRAY);
25508
26256
  ngModuleDef.imports = convertToTypeArray(scope.imports || EMPTY_ARRAY);
25509
26257
  ngModuleDef.exports = convertToTypeArray(scope.exports || EMPTY_ARRAY);
26258
+ if (scope.bootstrap) {
26259
+ // This only happens in local compilation mode.
26260
+ ngModuleDef.bootstrap = convertToTypeArray(scope.bootstrap);
26261
+ }
25510
26262
  depsTracker.registerNgModule(type, scope);
25511
26263
  });
25512
26264
  }
@@ -25514,11 +26266,12 @@ function convertToTypeArray(values) {
25514
26266
  if (typeof values === 'function') {
25515
26267
  return values;
25516
26268
  }
25517
- if (values.some(isForwardRef)) {
25518
- return () => values.map(resolveForwardRef).map(maybeUnwrapModuleWithProviders);
26269
+ const flattenValues = flatten(values);
26270
+ if (flattenValues.some(isForwardRef)) {
26271
+ return () => flattenValues.map(resolveForwardRef).map(maybeUnwrapModuleWithProviders);
25519
26272
  }
25520
26273
  else {
25521
- return values.map(maybeUnwrapModuleWithProviders);
26274
+ return flattenValues.map(maybeUnwrapModuleWithProviders);
25522
26275
  }
25523
26276
  }
25524
26277
  function maybeUnwrapModuleWithProviders(value) {
@@ -25919,7 +26672,7 @@ function getAsyncClassMetadata(type) {
25919
26672
  }
25920
26673
  /**
25921
26674
  * Handles the process of applying metadata info to a component class in case
25922
- * component template had `{#defer}` blocks (thus some dependencies became deferrable).
26675
+ * component template had defer blocks (thus some dependencies became deferrable).
25923
26676
  *
25924
26677
  * @param type Component class where metadata should be added
25925
26678
  * @param dependencyLoaderFn Function that loads dependencies
@@ -26667,16 +27420,13 @@ class QueryList {
26667
27420
  * are compared as is (without any pre-processing).
26668
27421
  */
26669
27422
  reset(resultsTree, identityAccessor) {
26670
- // Cast to `QueryListInternal` so that we can mutate fields which are readonly for the usage of
26671
- // QueryList (but not for QueryList itself.)
26672
- const self = this;
26673
- self.dirty = false;
27423
+ this.dirty = false;
26674
27424
  const newResultFlat = flatten(resultsTree);
26675
- if (this._changesDetected = !arrayEquals(self._results, newResultFlat, identityAccessor)) {
26676
- self._results = newResultFlat;
26677
- self.length = newResultFlat.length;
26678
- self.last = newResultFlat[this.length - 1];
26679
- self.first = newResultFlat[0];
27425
+ if (this._changesDetected = !arrayEquals(this._results, newResultFlat, identityAccessor)) {
27426
+ this._results = newResultFlat;
27427
+ this.length = newResultFlat.length;
27428
+ this.last = newResultFlat[this.length - 1];
27429
+ this.first = newResultFlat[0];
26680
27430
  }
26681
27431
  }
26682
27432
  /**
@@ -27244,7 +27994,13 @@ function ɵɵtemplateRefExtractor(tNode, lView) {
27244
27994
 
27245
27995
  function ɵɵgetComponentDepsFactory(type, rawImports) {
27246
27996
  return () => {
27247
- return depsTracker.getComponentDependencies(type, rawImports).dependencies;
27997
+ try {
27998
+ return depsTracker.getComponentDependencies(type, rawImports).dependencies;
27999
+ }
28000
+ catch (e) {
28001
+ console.error(`Computing dependencies in local compilation mode for the component "${type.name}" failed with the exception:`, e);
28002
+ throw e;
28003
+ }
27248
28004
  };
27249
28005
  }
27250
28006
 
@@ -28743,10 +29499,10 @@ class ApplicationInitStatus {
28743
29499
  static { this.ɵfac = function ApplicationInitStatus_Factory(t) { return new (t || ApplicationInitStatus)(); }; }
28744
29500
  static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: ApplicationInitStatus, factory: ApplicationInitStatus.ɵfac, providedIn: 'root' }); }
28745
29501
  }
28746
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ApplicationInitStatus, [{
29502
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ApplicationInitStatus, [{
28747
29503
  type: Injectable,
28748
29504
  args: [{ providedIn: 'root' }]
28749
- }], function () { return []; }, null); })();
29505
+ }], () => [], null); })();
28750
29506
 
28751
29507
  class Console {
28752
29508
  log(message) {
@@ -28761,7 +29517,7 @@ class Console {
28761
29517
  static { this.ɵfac = function Console_Factory(t) { return new (t || Console)(); }; }
28762
29518
  static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: Console, factory: Console.ɵfac, providedIn: 'platform' }); }
28763
29519
  }
28764
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(Console, [{
29520
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(Console, [{
28765
29521
  type: Injectable,
28766
29522
  args: [{ providedIn: 'platform' }]
28767
29523
  }], null, null); })();
@@ -28972,7 +29728,7 @@ class InitialRenderPendingTasks {
28972
29728
  static { this.ɵfac = function InitialRenderPendingTasks_Factory(t) { return new (t || InitialRenderPendingTasks)(); }; }
28973
29729
  static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: InitialRenderPendingTasks, factory: InitialRenderPendingTasks.ɵfac, providedIn: 'root' }); }
28974
29730
  }
28975
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(InitialRenderPendingTasks, [{
29731
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(InitialRenderPendingTasks, [{
28976
29732
  type: Injectable,
28977
29733
  args: [{ providedIn: 'root' }]
28978
29734
  }], null, null); })();
@@ -29060,7 +29816,7 @@ class Compiler {
29060
29816
  static { this.ɵfac = function Compiler_Factory(t) { return new (t || Compiler)(); }; }
29061
29817
  static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: Compiler, factory: Compiler.ɵfac, providedIn: 'root' }); }
29062
29818
  }
29063
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(Compiler, [{
29819
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(Compiler, [{
29064
29820
  type: Injectable,
29065
29821
  args: [{ providedIn: 'root' }]
29066
29822
  }], null, null); })();
@@ -30024,12 +30780,12 @@ class Testability {
30024
30780
  static { this.ɵfac = function Testability_Factory(t) { return new (t || Testability)(ɵɵinject(NgZone), ɵɵinject(TestabilityRegistry), ɵɵinject(TESTABILITY_GETTER)); }; }
30025
30781
  static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: Testability, factory: Testability.ɵfac }); }
30026
30782
  }
30027
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(Testability, [{
30783
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(Testability, [{
30028
30784
  type: Injectable
30029
- }], function () { return [{ type: NgZone }, { type: TestabilityRegistry }, { type: undefined, decorators: [{
30785
+ }], () => [{ type: NgZone }, { type: TestabilityRegistry }, { type: undefined, decorators: [{
30030
30786
  type: Inject,
30031
30787
  args: [TESTABILITY_GETTER]
30032
- }] }]; }, null); })();
30788
+ }] }], null); })();
30033
30789
  /**
30034
30790
  * A global registry of {@link Testability} instances for specific elements.
30035
30791
  * @publicApi
@@ -30091,7 +30847,7 @@ class TestabilityRegistry {
30091
30847
  static { this.ɵfac = function TestabilityRegistry_Factory(t) { return new (t || TestabilityRegistry)(); }; }
30092
30848
  static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: TestabilityRegistry, factory: TestabilityRegistry.ɵfac, providedIn: 'platform' }); }
30093
30849
  }
30094
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(TestabilityRegistry, [{
30850
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(TestabilityRegistry, [{
30095
30851
  type: Injectable,
30096
30852
  args: [{ providedIn: 'platform' }]
30097
30853
  }], null, null); })();
@@ -30534,10 +31290,10 @@ class PlatformRef {
30534
31290
  static { this.ɵfac = function PlatformRef_Factory(t) { return new (t || PlatformRef)(ɵɵinject(Injector)); }; }
30535
31291
  static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: PlatformRef, factory: PlatformRef.ɵfac, providedIn: 'platform' }); }
30536
31292
  }
30537
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(PlatformRef, [{
31293
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(PlatformRef, [{
30538
31294
  type: Injectable,
30539
31295
  args: [{ providedIn: 'platform' }]
30540
- }], function () { return [{ type: Injector }]; }, null); })();
31296
+ }], () => [{ type: Injector }], null); })();
30541
31297
  // Transforms a set of `BootstrapOptions` (supported by the NgModule-based bootstrap APIs) ->
30542
31298
  // `NgZoneOptions` that are recognized by the NgZone constructor. Passing no options will result in
30543
31299
  // a set of default options returned.
@@ -30917,7 +31673,7 @@ class ApplicationRef {
30917
31673
  static { this.ɵfac = function ApplicationRef_Factory(t) { return new (t || ApplicationRef)(); }; }
30918
31674
  static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: ApplicationRef, factory: ApplicationRef.ɵfac, providedIn: 'root' }); }
30919
31675
  }
30920
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ApplicationRef, [{
31676
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ApplicationRef, [{
30921
31677
  type: Injectable,
30922
31678
  args: [{ providedIn: 'root' }]
30923
31679
  }], null, null); })();
@@ -30976,7 +31732,7 @@ class NgZoneChangeDetectionScheduler {
30976
31732
  static { this.ɵfac = function NgZoneChangeDetectionScheduler_Factory(t) { return new (t || NgZoneChangeDetectionScheduler)(); }; }
30977
31733
  static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: NgZoneChangeDetectionScheduler, factory: NgZoneChangeDetectionScheduler.ɵfac, providedIn: 'root' }); }
30978
31734
  }
30979
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NgZoneChangeDetectionScheduler, [{
31735
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NgZoneChangeDetectionScheduler, [{
30980
31736
  type: Injectable,
30981
31737
  args: [{ providedIn: 'root' }]
30982
31738
  }], null, null); })();
@@ -31033,6 +31789,23 @@ function provideZoneChangeDetection(options) {
31033
31789
  zoneProviders,
31034
31790
  ]);
31035
31791
  }
31792
+ let whenStableStore;
31793
+ /**
31794
+ * Returns a Promise that resolves when the application becomes stable after this method is called
31795
+ * the first time.
31796
+ */
31797
+ function whenStable(applicationRef) {
31798
+ whenStableStore ??= new WeakMap();
31799
+ const cachedWhenStable = whenStableStore.get(applicationRef);
31800
+ if (cachedWhenStable) {
31801
+ return cachedWhenStable;
31802
+ }
31803
+ const whenStablePromise = applicationRef.isStable.pipe(first((isStable) => isStable)).toPromise().then(() => void 0);
31804
+ whenStableStore.set(applicationRef, whenStablePromise);
31805
+ // Be a good citizen and clean the store `onDestroy` even though we are using `WeakMap`.
31806
+ applicationRef.onDestroy(() => whenStableStore?.delete(applicationRef));
31807
+ return whenStablePromise;
31808
+ }
31036
31809
 
31037
31810
  /**
31038
31811
  * Returns whether Angular is in development mode.
@@ -31752,9 +32525,9 @@ class ApplicationModule {
31752
32525
  static { this.ɵmod = /*@__PURE__*/ ɵɵdefineNgModule({ type: ApplicationModule }); }
31753
32526
  static { this.ɵinj = /*@__PURE__*/ ɵɵdefineInjector({}); }
31754
32527
  }
31755
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ApplicationModule, [{
32528
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ApplicationModule, [{
31756
32529
  type: NgModule
31757
- }], function () { return [{ type: ApplicationRef }]; }, null); })();
32530
+ }], () => [{ type: ApplicationRef }], null); })();
31758
32531
 
31759
32532
  /**
31760
32533
  * A collection that tracks all serialized views (`ngh` DOM annotations)
@@ -32300,8 +33073,8 @@ function printHydrationStats(injector) {
32300
33073
  /**
32301
33074
  * Returns a Promise that is resolved when an application becomes stable.
32302
33075
  */
32303
- function whenStable(appRef, injector) {
32304
- const isStablePromise = appRef.isStable.pipe(first((isStable) => isStable)).toPromise();
33076
+ function whenStableWithTimeout(appRef, injector) {
33077
+ const whenStablePromise = whenStable(appRef);
32305
33078
  if (typeof ngDevMode !== 'undefined' && ngDevMode) {
32306
33079
  const timeoutTime = APPLICATION_IS_STABLE_TIMEOUT;
32307
33080
  const console = injector.get(Console);
@@ -32312,9 +33085,9 @@ function whenStable(appRef, injector) {
32312
33085
  const timeoutId = ngZone.runOutsideAngular(() => {
32313
33086
  return setTimeout(() => logWarningOnStableTimedout(timeoutTime, console), timeoutTime);
32314
33087
  });
32315
- isStablePromise.finally(() => clearTimeout(timeoutId));
33088
+ whenStablePromise.finally(() => clearTimeout(timeoutId));
32316
33089
  }
32317
- return isStablePromise.then(() => { });
33090
+ return whenStablePromise;
32318
33091
  }
32319
33092
  /**
32320
33093
  * Returns a set of providers required to setup hydration support
@@ -32393,7 +33166,7 @@ function withDomHydration() {
32393
33166
  //
32394
33167
  // Note: the cleanup task *MUST* be scheduled within the Angular zone
32395
33168
  // to ensure that change detection is properly run afterward.
32396
- whenStable(appRef, injector).then(() => {
33169
+ whenStableWithTimeout(appRef, injector).then(() => {
32397
33170
  NgZone.assertInAngularZone();
32398
33171
  cleanupDehydratedViews(appRef);
32399
33172
  if (typeof ngDevMode !== 'undefined' && ngDevMode) {
@@ -32689,19 +33462,11 @@ class EffectHandle {
32689
33462
  this.effectFn = effectFn;
32690
33463
  this.creationZone = creationZone;
32691
33464
  this.errorHandler = errorHandler;
32692
- this.alive = true;
32693
33465
  this.watcher =
32694
33466
  watch((onCleanup) => this.runEffect(onCleanup), () => this.schedule(), allowSignalWrites);
32695
33467
  this.unregisterOnDestroy = destroyRef?.onDestroy(() => this.destroy());
32696
33468
  }
32697
33469
  runEffect(onCleanup) {
32698
- if (!this.alive) {
32699
- // Running a destroyed effect is a no-op.
32700
- return;
32701
- }
32702
- if (ngDevMode && isInNotificationPhase()) {
32703
- throw new Error(`Schedulers cannot synchronously execute effects while scheduling.`);
32704
- }
32705
33470
  try {
32706
33471
  this.effectFn(onCleanup);
32707
33472
  }
@@ -32713,17 +33478,13 @@ class EffectHandle {
32713
33478
  this.watcher.run();
32714
33479
  }
32715
33480
  schedule() {
32716
- if (!this.alive) {
32717
- return;
32718
- }
32719
33481
  this.scheduler.scheduleEffect(this);
32720
33482
  }
32721
33483
  notify() {
32722
33484
  this.watcher.notify();
32723
33485
  }
32724
33486
  destroy() {
32725
- this.alive = false;
32726
- this.watcher.cleanup();
33487
+ this.watcher.destroy();
32727
33488
  this.unregisterOnDestroy?.();
32728
33489
  // Note: if the effect is currently scheduled, it's not un-scheduled, and so the scheduler will
32729
33490
  // retain a reference to it. Attempting to execute it will be a no-op.
@@ -32935,5 +33696,5 @@ if (typeof ngDevMode !== 'undefined' && ngDevMode) {
32935
33696
  * Generated bundle index. Do not edit.
32936
33697
  */
32937
33698
 
32938
- export { ANIMATION_MODULE_TYPE, APP_BOOTSTRAP_LISTENER, APP_ID, APP_INITIALIZER, ApplicationInitStatus, ApplicationModule, ApplicationRef, Attribute, COMPILER_OPTIONS, CSP_NONCE, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, ChangeDetectorRef, Compiler, CompilerFactory, Component, ComponentFactory$1 as ComponentFactory, ComponentFactoryResolver$1 as ComponentFactoryResolver, ComponentRef$1 as ComponentRef, ContentChild, ContentChildren, DEFAULT_CURRENCY_CODE, DebugElement, DebugEventListener, DebugNode, DefaultIterableDiffer, DestroyRef, Directive, ENVIRONMENT_INITIALIZER, ElementRef, EmbeddedViewRef, EnvironmentInjector, ErrorHandler, EventEmitter, Host, HostBinding, HostListener, INJECTOR, Inject, InjectFlags, Injectable, InjectionToken, Injector, Input, IterableDiffers, KeyValueDiffers, LOCALE_ID, MissingTranslationStrategy, ModuleWithComponentFactories, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory$1 as NgModuleFactory, NgModuleRef$1 as NgModuleRef, NgProbeToken, NgZone, Optional, Output, PACKAGE_ROOT_URL, PLATFORM_ID, PLATFORM_INITIALIZER, Pipe, PlatformRef, Query, QueryList, Renderer2, RendererFactory2, RendererStyleFlags2, Sanitizer, SecurityContext, Self, SimpleChange, SkipSelf, TRANSLATIONS, TRANSLATIONS_FORMAT, TemplateRef, Testability, TestabilityRegistry, TransferState, Type, VERSION, Version, ViewChild, ViewChildren, ViewContainerRef, ViewEncapsulation$1 as ViewEncapsulation, ViewRef, afterNextRender, afterRender, asNativeElements, assertInInjectionContext, assertPlatform, booleanAttribute, computed, createComponent, createEnvironmentInjector, createNgModule, createNgModuleRef, createPlatform, createPlatformFactory, defineInjectable, destroyPlatform, effect, enableProdMode, forwardRef, getDebugNode, getModuleFactory, getNgModuleById, getPlatform, importProvidersFrom, inject, isDevMode, isSignal, isStandalone, makeEnvironmentProviders, makeStateKey, mergeApplicationConfig, numberAttribute, platformCore, provideZoneChangeDetection, reflectComponentType, resolveForwardRef, runInInjectionContext, setTestabilityGetter, signal, untracked, ALLOW_MULTIPLE_PLATFORMS as ɵALLOW_MULTIPLE_PLATFORMS, AfterRenderEventManager as ɵAfterRenderEventManager, ComponentFactory$1 as ɵComponentFactory, Console as ɵConsole, DEFAULT_LOCALE_ID as ɵDEFAULT_LOCALE_ID, DEFER_BLOCK_DEPENDENCY_INTERCEPTOR as ɵDEFER_BLOCK_DEPENDENCY_INTERCEPTOR, ENABLED_SSR_FEATURES as ɵENABLED_SSR_FEATURES, EffectScheduler as ɵEffectScheduler, INJECTOR_SCOPE as ɵINJECTOR_SCOPE, IS_HYDRATION_DOM_REUSE_ENABLED as ɵIS_HYDRATION_DOM_REUSE_ENABLED, InitialRenderPendingTasks as ɵInitialRenderPendingTasks, LContext as ɵLContext, LifecycleHooksFeature as ɵLifecycleHooksFeature, LocaleDataIndex as ɵLocaleDataIndex, NG_COMP_DEF as ɵNG_COMP_DEF, NG_DIR_DEF as ɵNG_DIR_DEF, NG_ELEMENT_ID as ɵNG_ELEMENT_ID, NG_INJ_DEF as ɵNG_INJ_DEF, NG_MOD_DEF as ɵNG_MOD_DEF, NG_PIPE_DEF as ɵNG_PIPE_DEF, NG_PROV_DEF as ɵNG_PROV_DEF, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR, NO_CHANGE as ɵNO_CHANGE, NgModuleFactory as ɵNgModuleFactory, NoopNgZone as ɵNoopNgZone, ReflectionCapabilities as ɵReflectionCapabilities, ComponentFactory as ɵRender3ComponentFactory, ComponentRef as ɵRender3ComponentRef, NgModuleRef as ɵRender3NgModuleRef, RuntimeError as ɵRuntimeError, SSR_CONTENT_INTEGRITY_MARKER as ɵSSR_CONTENT_INTEGRITY_MARKER, TESTABILITY as ɵTESTABILITY, TESTABILITY_GETTER as ɵTESTABILITY_GETTER, USE_RUNTIME_DEPS_TRACKER_FOR_JIT as ɵUSE_RUNTIME_DEPS_TRACKER_FOR_JIT, ViewRef$1 as ɵViewRef, XSS_SECURITY_URL as ɵXSS_SECURITY_URL, ZoneAwareQueueingScheduler as ɵZoneAwareQueueingScheduler, _sanitizeHtml as ɵ_sanitizeHtml, _sanitizeUrl as ɵ_sanitizeUrl, allowSanitizationBypassAndThrow as ɵallowSanitizationBypassAndThrow, annotateForHydration as ɵannotateForHydration, bypassSanitizationTrustHtml as ɵbypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl as ɵbypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript as ɵbypassSanitizationTrustScript, bypassSanitizationTrustStyle as ɵbypassSanitizationTrustStyle, bypassSanitizationTrustUrl as ɵbypassSanitizationTrustUrl, clearResolutionOfComponentResourcesQueue as ɵclearResolutionOfComponentResourcesQueue, compileComponent as ɵcompileComponent, compileDirective as ɵcompileDirective, compileNgModule as ɵcompileNgModule, compileNgModuleDefs as ɵcompileNgModuleDefs, compileNgModuleFactory as ɵcompileNgModuleFactory, compilePipe as ɵcompilePipe, convertToBitFlags as ɵconvertToBitFlags, createInjector as ɵcreateInjector, defaultIterableDiffers as ɵdefaultIterableDiffers, defaultKeyValueDiffers as ɵdefaultKeyValueDiffers, depsTracker as ɵdepsTracker, detectChanges as ɵdetectChanges, devModeEqual as ɵdevModeEqual, findLocaleData as ɵfindLocaleData, flushModuleScopingQueueAsMuchAsPossible as ɵflushModuleScopingQueueAsMuchAsPossible, formatRuntimeError as ɵformatRuntimeError, generateStandaloneInDeclarationsError as ɵgenerateStandaloneInDeclarationsError, getAsyncClassMetadata as ɵgetAsyncClassMetadata, getDebugNode as ɵgetDebugNode, getDirectives as ɵgetDirectives, getHostElement as ɵgetHostElement, getInjectableDef as ɵgetInjectableDef, getLContext as ɵgetLContext, getLocaleCurrencyCode as ɵgetLocaleCurrencyCode, getLocalePluralCase as ɵgetLocalePluralCase, getSanitizationBypassType as ɵgetSanitizationBypassType, ɵgetUnknownElementStrictMode, ɵgetUnknownPropertyStrictMode, _global as ɵglobal, injectChangeDetectorRef as ɵinjectChangeDetectorRef, internalCreateApplication as ɵinternalCreateApplication, isBoundToModule as ɵisBoundToModule, isComponentDefPendingResolution as ɵisComponentDefPendingResolution, isEnvironmentProviders as ɵisEnvironmentProviders, isInjectable as ɵisInjectable, isNgModule as ɵisNgModule, isPromise as ɵisPromise, isSubscribable as ɵisSubscribable, noSideEffects as ɵnoSideEffects, patchComponentDefWithScope as ɵpatchComponentDefWithScope, publishDefaultGlobalUtils$1 as ɵpublishDefaultGlobalUtils, publishGlobalUtil as ɵpublishGlobalUtil, registerLocaleData as ɵregisterLocaleData, resetCompiledComponents as ɵresetCompiledComponents, resetJitOptions as ɵresetJitOptions, resolveComponentResources as ɵresolveComponentResources, restoreComponentResolutionQueue as ɵrestoreComponentResolutionQueue, setAllowDuplicateNgModuleIdsForTest as ɵsetAllowDuplicateNgModuleIdsForTest, setAlternateWeakRefImpl as ɵsetAlternateWeakRefImpl, setClassMetadata as ɵsetClassMetadata, setClassMetadataAsync as ɵsetClassMetadataAsync, setCurrentInjector as ɵsetCurrentInjector, setDocument as ɵsetDocument, setInjectorProfilerContext as ɵsetInjectorProfilerContext, setLocaleId as ɵsetLocaleId, ɵsetUnknownElementStrictMode, ɵsetUnknownPropertyStrictMode, store as ɵstore, stringify as ɵstringify, transitiveScopesFor as ɵtransitiveScopesFor, unregisterAllLocaleData as ɵunregisterLocaleData, unwrapSafeValue as ɵunwrapSafeValue, withDomHydration as ɵwithDomHydration, ɵɵCopyDefinitionFeature, FactoryTarget as ɵɵFactoryTarget, ɵɵHostDirectivesFeature, ɵɵInheritDefinitionFeature, ɵɵInputTransformsFeature, ɵɵNgOnChangesFeature, ɵɵProvidersFeature, ɵɵStandaloneFeature, ɵɵadvance, ɵɵattribute, ɵɵattributeInterpolate1, ɵɵattributeInterpolate2, ɵɵattributeInterpolate3, ɵɵattributeInterpolate4, ɵɵattributeInterpolate5, ɵɵattributeInterpolate6, ɵɵattributeInterpolate7, ɵɵattributeInterpolate8, ɵɵattributeInterpolateV, ɵɵclassMap, ɵɵclassMapInterpolate1, ɵɵclassMapInterpolate2, ɵɵclassMapInterpolate3, ɵɵclassMapInterpolate4, ɵɵclassMapInterpolate5, ɵɵclassMapInterpolate6, ɵɵclassMapInterpolate7, ɵɵclassMapInterpolate8, ɵɵclassMapInterpolateV, ɵɵclassProp, ɵɵcomponentInstance, ɵɵconditional, ɵɵcontentQuery, ɵɵdefer, ɵɵdeferOnHover, ɵɵdeferOnIdle, ɵɵdeferOnImmediate, ɵɵdeferOnInteraction, ɵɵdeferOnTimer, ɵɵdeferOnViewport, ɵɵdeferPrefetchOnHover, ɵɵdeferPrefetchOnIdle, ɵɵdeferPrefetchOnImmediate, ɵɵdeferPrefetchOnInteraction, ɵɵdeferPrefetchOnTimer, ɵɵdeferPrefetchOnViewport, ɵɵdeferPrefetchWhen, ɵɵdeferWhen, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdefineInjectable, ɵɵdefineInjector, ɵɵdefineNgModule, ɵɵdefinePipe, ɵɵdirectiveInject, ɵɵdisableBindings, ɵɵelement, ɵɵelementContainer, ɵɵelementContainerEnd, ɵɵelementContainerStart, ɵɵelementEnd, ɵɵelementStart, ɵɵenableBindings, ɵɵgetComponentDepsFactory, ɵɵgetCurrentView, ɵɵgetInheritedFactory, ɵɵhostProperty, ɵɵi18n, ɵɵi18nApply, ɵɵi18nAttributes, ɵɵi18nEnd, ɵɵi18nExp, ɵɵi18nPostprocess, ɵɵi18nStart, ɵɵinject, ɵɵinjectAttribute, ɵɵinvalidFactory, ɵɵinvalidFactoryDep, ɵɵlistener, ɵɵloadQuery, ɵɵnamespaceHTML, ɵɵnamespaceMathML, ɵɵnamespaceSVG, ɵɵnextContext, ɵɵngDeclareClassMetadata, ɵɵngDeclareComponent, ɵɵngDeclareDirective, ɵɵngDeclareFactory, ɵɵngDeclareInjectable, ɵɵngDeclareInjector, ɵɵngDeclareNgModule, ɵɵngDeclarePipe, ɵɵpipe, ɵɵpipeBind1, ɵɵpipeBind2, ɵɵpipeBind3, ɵɵpipeBind4, ɵɵpipeBindV, ɵɵprojection, ɵɵprojectionDef, ɵɵproperty, ɵɵpropertyInterpolate, ɵɵpropertyInterpolate1, ɵɵpropertyInterpolate2, ɵɵpropertyInterpolate3, ɵɵpropertyInterpolate4, ɵɵpropertyInterpolate5, ɵɵpropertyInterpolate6, ɵɵpropertyInterpolate7, ɵɵpropertyInterpolate8, ɵɵpropertyInterpolateV, ɵɵpureFunction0, ɵɵpureFunction1, ɵɵpureFunction2, ɵɵpureFunction3, ɵɵpureFunction4, ɵɵpureFunction5, ɵɵpureFunction6, ɵɵpureFunction7, ɵɵpureFunction8, ɵɵpureFunctionV, ɵɵqueryRefresh, ɵɵreference, registerNgModuleType as ɵɵregisterNgModuleType, ɵɵrepeater, ɵɵrepeaterCreate, ɵɵrepeaterTrackByIdentity, ɵɵrepeaterTrackByIndex, ɵɵresetView, ɵɵresolveBody, ɵɵresolveDocument, ɵɵresolveWindow, ɵɵrestoreView, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl, ɵɵsanitizeUrlOrResourceUrl, ɵɵsetComponentScope, ɵɵsetNgModuleScope, ɵɵstyleMap, ɵɵstyleMapInterpolate1, ɵɵstyleMapInterpolate2, ɵɵstyleMapInterpolate3, ɵɵstyleMapInterpolate4, ɵɵstyleMapInterpolate5, ɵɵstyleMapInterpolate6, ɵɵstyleMapInterpolate7, ɵɵstyleMapInterpolate8, ɵɵstyleMapInterpolateV, ɵɵstyleProp, ɵɵstylePropInterpolate1, ɵɵstylePropInterpolate2, ɵɵstylePropInterpolate3, ɵɵstylePropInterpolate4, ɵɵstylePropInterpolate5, ɵɵstylePropInterpolate6, ɵɵstylePropInterpolate7, ɵɵstylePropInterpolate8, ɵɵstylePropInterpolateV, ɵɵsyntheticHostListener, ɵɵsyntheticHostProperty, ɵɵtemplate, ɵɵtemplateRefExtractor, ɵɵtext, ɵɵtextInterpolate, ɵɵtextInterpolate1, ɵɵtextInterpolate2, ɵɵtextInterpolate3, ɵɵtextInterpolate4, ɵɵtextInterpolate5, ɵɵtextInterpolate6, ɵɵtextInterpolate7, ɵɵtextInterpolate8, ɵɵtextInterpolateV, ɵɵtrustConstantHtml, ɵɵtrustConstantResourceUrl, ɵɵvalidateIframeAttribute, ɵɵviewQuery };
33699
+ export { ANIMATION_MODULE_TYPE, APP_BOOTSTRAP_LISTENER, APP_ID, APP_INITIALIZER, AfterRenderPhase, ApplicationInitStatus, ApplicationModule, ApplicationRef, Attribute, COMPILER_OPTIONS, CSP_NONCE, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, ChangeDetectorRef, Compiler, CompilerFactory, Component, ComponentFactory$1 as ComponentFactory, ComponentFactoryResolver$1 as ComponentFactoryResolver, ComponentRef$1 as ComponentRef, ContentChild, ContentChildren, DEFAULT_CURRENCY_CODE, DebugElement, DebugEventListener, DebugNode, DefaultIterableDiffer, DestroyRef, Directive, ENVIRONMENT_INITIALIZER, ElementRef, EmbeddedViewRef, EnvironmentInjector, ErrorHandler, EventEmitter, Host, HostBinding, HostListener, INJECTOR, Inject, InjectFlags, Injectable, InjectionToken, Injector, Input, IterableDiffers, KeyValueDiffers, LOCALE_ID, MissingTranslationStrategy, ModuleWithComponentFactories, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory$1 as NgModuleFactory, NgModuleRef$1 as NgModuleRef, NgProbeToken, NgZone, Optional, Output, PACKAGE_ROOT_URL, PLATFORM_ID, PLATFORM_INITIALIZER, Pipe, PlatformRef, Query, QueryList, Renderer2, RendererFactory2, RendererStyleFlags2, Sanitizer, SecurityContext, Self, SimpleChange, SkipSelf, TRANSLATIONS, TRANSLATIONS_FORMAT, TemplateRef, Testability, TestabilityRegistry, TransferState, Type, VERSION, Version, ViewChild, ViewChildren, ViewContainerRef, ViewEncapsulation$1 as ViewEncapsulation, ViewRef, afterNextRender, afterRender, asNativeElements, assertInInjectionContext, assertPlatform, booleanAttribute, computed, createComponent, createEnvironmentInjector, createNgModule, createNgModuleRef, createPlatform, createPlatformFactory, defineInjectable, destroyPlatform, effect, enableProdMode, forwardRef, getDebugNode, getModuleFactory, getNgModuleById, getPlatform, importProvidersFrom, inject, isDevMode, isSignal, isStandalone, makeEnvironmentProviders, makeStateKey, mergeApplicationConfig, numberAttribute, platformCore, provideZoneChangeDetection, reflectComponentType, resolveForwardRef, runInInjectionContext, setTestabilityGetter, signal, untracked, ALLOW_MULTIPLE_PLATFORMS as ɵALLOW_MULTIPLE_PLATFORMS, AfterRenderEventManager as ɵAfterRenderEventManager, CONTAINER_HEADER_OFFSET as ɵCONTAINER_HEADER_OFFSET, ComponentFactory$1 as ɵComponentFactory, Console as ɵConsole, DEFAULT_LOCALE_ID as ɵDEFAULT_LOCALE_ID, DEFER_BLOCK_CONFIG as ɵDEFER_BLOCK_CONFIG, DEFER_BLOCK_DEPENDENCY_INTERCEPTOR as ɵDEFER_BLOCK_DEPENDENCY_INTERCEPTOR, DeferBlockBehavior as ɵDeferBlockBehavior, DeferBlockState as ɵDeferBlockState, ENABLED_SSR_FEATURES as ɵENABLED_SSR_FEATURES, EffectScheduler as ɵEffectScheduler, INJECTOR_SCOPE as ɵINJECTOR_SCOPE, IS_HYDRATION_DOM_REUSE_ENABLED as ɵIS_HYDRATION_DOM_REUSE_ENABLED, InitialRenderPendingTasks as ɵInitialRenderPendingTasks, LContext as ɵLContext, LifecycleHooksFeature as ɵLifecycleHooksFeature, LocaleDataIndex as ɵLocaleDataIndex, NG_COMP_DEF as ɵNG_COMP_DEF, NG_DIR_DEF as ɵNG_DIR_DEF, NG_ELEMENT_ID as ɵNG_ELEMENT_ID, NG_INJ_DEF as ɵNG_INJ_DEF, NG_MOD_DEF as ɵNG_MOD_DEF, NG_PIPE_DEF as ɵNG_PIPE_DEF, NG_PROV_DEF as ɵNG_PROV_DEF, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR, NO_CHANGE as ɵNO_CHANGE, NgModuleFactory as ɵNgModuleFactory, NoopNgZone as ɵNoopNgZone, ReflectionCapabilities as ɵReflectionCapabilities, ComponentFactory as ɵRender3ComponentFactory, ComponentRef as ɵRender3ComponentRef, NgModuleRef as ɵRender3NgModuleRef, RuntimeError as ɵRuntimeError, SSR_CONTENT_INTEGRITY_MARKER as ɵSSR_CONTENT_INTEGRITY_MARKER, TESTABILITY as ɵTESTABILITY, TESTABILITY_GETTER as ɵTESTABILITY_GETTER, USE_RUNTIME_DEPS_TRACKER_FOR_JIT as ɵUSE_RUNTIME_DEPS_TRACKER_FOR_JIT, ViewRef$1 as ɵViewRef, XSS_SECURITY_URL as ɵXSS_SECURITY_URL, ZoneAwareQueueingScheduler as ɵZoneAwareQueueingScheduler, _sanitizeHtml as ɵ_sanitizeHtml, _sanitizeUrl as ɵ_sanitizeUrl, allowSanitizationBypassAndThrow as ɵallowSanitizationBypassAndThrow, annotateForHydration as ɵannotateForHydration, bypassSanitizationTrustHtml as ɵbypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl as ɵbypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript as ɵbypassSanitizationTrustScript, bypassSanitizationTrustStyle as ɵbypassSanitizationTrustStyle, bypassSanitizationTrustUrl as ɵbypassSanitizationTrustUrl, clearResolutionOfComponentResourcesQueue as ɵclearResolutionOfComponentResourcesQueue, compileComponent as ɵcompileComponent, compileDirective as ɵcompileDirective, compileNgModule as ɵcompileNgModule, compileNgModuleDefs as ɵcompileNgModuleDefs, compileNgModuleFactory as ɵcompileNgModuleFactory, compilePipe as ɵcompilePipe, convertToBitFlags as ɵconvertToBitFlags, createInjector as ɵcreateInjector, defaultIterableDiffers as ɵdefaultIterableDiffers, defaultKeyValueDiffers as ɵdefaultKeyValueDiffers, depsTracker as ɵdepsTracker, detectChanges as ɵdetectChanges, devModeEqual as ɵdevModeEqual, findLocaleData as ɵfindLocaleData, flushModuleScopingQueueAsMuchAsPossible as ɵflushModuleScopingQueueAsMuchAsPossible, formatRuntimeError as ɵformatRuntimeError, generateStandaloneInDeclarationsError as ɵgenerateStandaloneInDeclarationsError, getAsyncClassMetadata as ɵgetAsyncClassMetadata, getDebugNode as ɵgetDebugNode, getDeferBlocks as ɵgetDeferBlocks, getDirectives as ɵgetDirectives, getHostElement as ɵgetHostElement, getInjectableDef as ɵgetInjectableDef, getLContext as ɵgetLContext, getLocaleCurrencyCode as ɵgetLocaleCurrencyCode, getLocalePluralCase as ɵgetLocalePluralCase, getSanitizationBypassType as ɵgetSanitizationBypassType, ɵgetUnknownElementStrictMode, ɵgetUnknownPropertyStrictMode, _global as ɵglobal, injectChangeDetectorRef as ɵinjectChangeDetectorRef, internalCreateApplication as ɵinternalCreateApplication, isBoundToModule as ɵisBoundToModule, isComponentDefPendingResolution as ɵisComponentDefPendingResolution, isEnvironmentProviders as ɵisEnvironmentProviders, isInjectable as ɵisInjectable, isNgModule as ɵisNgModule, isPromise as ɵisPromise, isSubscribable as ɵisSubscribable, noSideEffects as ɵnoSideEffects, patchComponentDefWithScope as ɵpatchComponentDefWithScope, publishDefaultGlobalUtils$1 as ɵpublishDefaultGlobalUtils, publishGlobalUtil as ɵpublishGlobalUtil, registerLocaleData as ɵregisterLocaleData, renderDeferBlockState as ɵrenderDeferBlockState, resetCompiledComponents as ɵresetCompiledComponents, resetJitOptions as ɵresetJitOptions, resolveComponentResources as ɵresolveComponentResources, restoreComponentResolutionQueue as ɵrestoreComponentResolutionQueue, setAllowDuplicateNgModuleIdsForTest as ɵsetAllowDuplicateNgModuleIdsForTest, setAlternateWeakRefImpl as ɵsetAlternateWeakRefImpl, setClassMetadata as ɵsetClassMetadata, setClassMetadataAsync as ɵsetClassMetadataAsync, setCurrentInjector as ɵsetCurrentInjector, setDocument as ɵsetDocument, setInjectorProfilerContext as ɵsetInjectorProfilerContext, setLocaleId as ɵsetLocaleId, ɵsetUnknownElementStrictMode, ɵsetUnknownPropertyStrictMode, store as ɵstore, stringify as ɵstringify, transitiveScopesFor as ɵtransitiveScopesFor, triggerResourceLoading as ɵtriggerResourceLoading, unregisterAllLocaleData as ɵunregisterLocaleData, unwrapSafeValue as ɵunwrapSafeValue, whenStable as ɵwhenStable, withDomHydration as ɵwithDomHydration, ɵɵCopyDefinitionFeature, FactoryTarget as ɵɵFactoryTarget, ɵɵHostDirectivesFeature, ɵɵInheritDefinitionFeature, ɵɵInputTransformsFeature, ɵɵNgOnChangesFeature, ɵɵProvidersFeature, ɵɵStandaloneFeature, ɵɵadvance, ɵɵattribute, ɵɵattributeInterpolate1, ɵɵattributeInterpolate2, ɵɵattributeInterpolate3, ɵɵattributeInterpolate4, ɵɵattributeInterpolate5, ɵɵattributeInterpolate6, ɵɵattributeInterpolate7, ɵɵattributeInterpolate8, ɵɵattributeInterpolateV, ɵɵclassMap, ɵɵclassMapInterpolate1, ɵɵclassMapInterpolate2, ɵɵclassMapInterpolate3, ɵɵclassMapInterpolate4, ɵɵclassMapInterpolate5, ɵɵclassMapInterpolate6, ɵɵclassMapInterpolate7, ɵɵclassMapInterpolate8, ɵɵclassMapInterpolateV, ɵɵclassProp, ɵɵcomponentInstance, ɵɵconditional, ɵɵcontentQuery, ɵɵdefer, ɵɵdeferOnHover, ɵɵdeferOnIdle, ɵɵdeferOnImmediate, ɵɵdeferOnInteraction, ɵɵdeferOnTimer, ɵɵdeferOnViewport, ɵɵdeferPrefetchOnHover, ɵɵdeferPrefetchOnIdle, ɵɵdeferPrefetchOnImmediate, ɵɵdeferPrefetchOnInteraction, ɵɵdeferPrefetchOnTimer, ɵɵdeferPrefetchOnViewport, ɵɵdeferPrefetchWhen, ɵɵdeferWhen, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdefineInjectable, ɵɵdefineInjector, ɵɵdefineNgModule, ɵɵdefinePipe, ɵɵdirectiveInject, ɵɵdisableBindings, ɵɵelement, ɵɵelementContainer, ɵɵelementContainerEnd, ɵɵelementContainerStart, ɵɵelementEnd, ɵɵelementStart, ɵɵenableBindings, ɵɵgetComponentDepsFactory, ɵɵgetCurrentView, ɵɵgetInheritedFactory, ɵɵhostProperty, ɵɵi18n, ɵɵi18nApply, ɵɵi18nAttributes, ɵɵi18nEnd, ɵɵi18nExp, ɵɵi18nPostprocess, ɵɵi18nStart, ɵɵinject, ɵɵinjectAttribute, ɵɵinvalidFactory, ɵɵinvalidFactoryDep, ɵɵlistener, ɵɵloadQuery, ɵɵnamespaceHTML, ɵɵnamespaceMathML, ɵɵnamespaceSVG, ɵɵnextContext, ɵɵngDeclareClassMetadata, ɵɵngDeclareComponent, ɵɵngDeclareDirective, ɵɵngDeclareFactory, ɵɵngDeclareInjectable, ɵɵngDeclareInjector, ɵɵngDeclareNgModule, ɵɵngDeclarePipe, ɵɵpipe, ɵɵpipeBind1, ɵɵpipeBind2, ɵɵpipeBind3, ɵɵpipeBind4, ɵɵpipeBindV, ɵɵprojection, ɵɵprojectionDef, ɵɵproperty, ɵɵpropertyInterpolate, ɵɵpropertyInterpolate1, ɵɵpropertyInterpolate2, ɵɵpropertyInterpolate3, ɵɵpropertyInterpolate4, ɵɵpropertyInterpolate5, ɵɵpropertyInterpolate6, ɵɵpropertyInterpolate7, ɵɵpropertyInterpolate8, ɵɵpropertyInterpolateV, ɵɵpureFunction0, ɵɵpureFunction1, ɵɵpureFunction2, ɵɵpureFunction3, ɵɵpureFunction4, ɵɵpureFunction5, ɵɵpureFunction6, ɵɵpureFunction7, ɵɵpureFunction8, ɵɵpureFunctionV, ɵɵqueryRefresh, ɵɵreference, registerNgModuleType as ɵɵregisterNgModuleType, ɵɵrepeater, ɵɵrepeaterCreate, ɵɵrepeaterTrackByIdentity, ɵɵrepeaterTrackByIndex, ɵɵresetView, ɵɵresolveBody, ɵɵresolveDocument, ɵɵresolveWindow, ɵɵrestoreView, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl, ɵɵsanitizeUrlOrResourceUrl, ɵɵsetComponentScope, ɵɵsetNgModuleScope, ɵɵstyleMap, ɵɵstyleMapInterpolate1, ɵɵstyleMapInterpolate2, ɵɵstyleMapInterpolate3, ɵɵstyleMapInterpolate4, ɵɵstyleMapInterpolate5, ɵɵstyleMapInterpolate6, ɵɵstyleMapInterpolate7, ɵɵstyleMapInterpolate8, ɵɵstyleMapInterpolateV, ɵɵstyleProp, ɵɵstylePropInterpolate1, ɵɵstylePropInterpolate2, ɵɵstylePropInterpolate3, ɵɵstylePropInterpolate4, ɵɵstylePropInterpolate5, ɵɵstylePropInterpolate6, ɵɵstylePropInterpolate7, ɵɵstylePropInterpolate8, ɵɵstylePropInterpolateV, ɵɵsyntheticHostListener, ɵɵsyntheticHostProperty, ɵɵtemplate, ɵɵtemplateRefExtractor, ɵɵtext, ɵɵtextInterpolate, ɵɵtextInterpolate1, ɵɵtextInterpolate2, ɵɵtextInterpolate3, ɵɵtextInterpolate4, ɵɵtextInterpolate5, ɵɵtextInterpolate6, ɵɵtextInterpolate7, ɵɵtextInterpolate8, ɵɵtextInterpolateV, ɵɵtrustConstantHtml, ɵɵtrustConstantResourceUrl, ɵɵvalidateIframeAttribute, ɵɵviewQuery };
32939
33700
  //# sourceMappingURL=core.mjs.map