@angular/core 16.0.0-rc.2 → 16.0.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/esm2022/rxjs-interop/src/index.mjs +1 -1
  2. package/esm2022/rxjs-interop/src/take_until_destroyed.mjs +3 -2
  3. package/esm2022/rxjs-interop/src/to_observable.mjs +20 -19
  4. package/esm2022/rxjs-interop/src/to_signal.mjs +6 -5
  5. package/esm2022/src/application_init.mjs +9 -3
  6. package/esm2022/src/application_ref.mjs +2 -1
  7. package/esm2022/src/errors.mjs +3 -3
  8. package/esm2022/src/hydration/error_handling.mjs +13 -5
  9. package/esm2022/src/linker/template_ref.mjs +3 -2
  10. package/esm2022/src/render3/component_ref.mjs +3 -2
  11. package/esm2022/src/render3/definition.mjs +4 -5
  12. package/esm2022/src/render3/instructions/change_detection.mjs +263 -3
  13. package/esm2022/src/render3/instructions/element.mjs +2 -2
  14. package/esm2022/src/render3/instructions/element_container.mjs +2 -2
  15. package/esm2022/src/render3/instructions/render.mjs +125 -0
  16. package/esm2022/src/render3/instructions/shared.mjs +23 -405
  17. package/esm2022/src/render3/instructions/template.mjs +22 -9
  18. package/esm2022/src/render3/interfaces/container.mjs +3 -3
  19. package/esm2022/src/render3/interfaces/view.mjs +2 -2
  20. package/esm2022/src/render3/node_manipulation.mjs +4 -8
  21. package/esm2022/src/render3/util/view_utils.mjs +35 -11
  22. package/esm2022/src/render3/view_ref.mjs +2 -2
  23. package/esm2022/src/version.mjs +1 -1
  24. package/esm2022/testing/src/logger.mjs +3 -3
  25. package/esm2022/testing/src/test_bed.mjs +1 -3
  26. package/esm2022/testing/src/test_bed_common.mjs +1 -1
  27. package/fesm2022/core.mjs +647 -618
  28. package/fesm2022/core.mjs.map +1 -1
  29. package/fesm2022/rxjs-interop.mjs +29 -26
  30. package/fesm2022/rxjs-interop.mjs.map +1 -1
  31. package/fesm2022/testing.mjs +639 -618
  32. package/fesm2022/testing.mjs.map +1 -1
  33. package/index.d.ts +24 -10
  34. package/package.json +1 -1
  35. package/rxjs-interop/index.d.ts +111 -20
  36. package/schematics/migrations/guard-and-resolve-interfaces/bundle.js +13 -13
  37. package/schematics/migrations/remove-module-id/bundle.js +14 -14
  38. package/schematics/ng-generate/standalone-migration/bundle.js +371 -350
  39. package/schematics/ng-generate/standalone-migration/bundle.js.map +2 -2
  40. package/testing/index.d.ts +3 -3
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v16.0.0-rc.2
2
+ * @license Angular v16.0.0-rc.4
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -840,9 +840,9 @@ function formatRuntimeError(code, message) {
840
840
  // generate a link to the error details page on angular.io.
841
841
  // We also prepend `0` to non-compile-time errors.
842
842
  const fullCode = `NG0${Math.abs(code)}`;
843
- let errorMessage = `${fullCode}${message ? ': ' + message.trim() : ''}`;
843
+ let errorMessage = `${fullCode}${message ? ': ' + message : ''}`;
844
844
  if (ngDevMode && code < 0) {
845
- const addPeriodSeparator = !errorMessage.match(/[.,;!?]$/);
845
+ const addPeriodSeparator = !errorMessage.match(/[.,;!?\n]$/);
846
846
  const separator = addPeriodSeparator ? '.' : '';
847
847
  errorMessage =
848
848
  `${errorMessage}${separator} Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/${fullCode}`;
@@ -3184,10 +3184,8 @@ function getComponentId(componentDef) {
3184
3184
  if (GENERATED_COMP_IDS.has(compId)) {
3185
3185
  const previousCompDefType = GENERATED_COMP_IDS.get(compId);
3186
3186
  if (previousCompDefType !== componentDef.type) {
3187
- // TODO: use `formatRuntimeError` to have an error code and we can later on create an error
3188
- // guide to explain this further.
3189
- console.warn(`Component ID generation collision detected. Components '${previousCompDefType.name}' and '${componentDef.type.name}' with selector '${stringifyCSSSelectorList(componentDef
3190
- .selectors)}' generated the same component ID. To fix this, you can change the selector of one of those components or add an extra host attribute to force a different ID.`);
3187
+ console.warn(formatRuntimeError(-912 /* RuntimeErrorCode.COMPONENT_ID_COLLISION */, `Component ID generation collision detected. Components '${previousCompDefType.name}' and '${componentDef.type.name}' with selector '${stringifyCSSSelectorList(componentDef
3188
+ .selectors)}' generated the same component ID. To fix this, you can change the selector of one of those components or add an extra host attribute to force a different ID.`));
3191
3189
  }
3192
3190
  }
3193
3191
  else {
@@ -3205,7 +3203,7 @@ const TVIEW = 1;
3205
3203
  const FLAGS = 2;
3206
3204
  const PARENT = 3;
3207
3205
  const NEXT = 4;
3208
- const TRANSPLANTED_VIEWS_TO_REFRESH = 5;
3206
+ const DESCENDANT_VIEWS_TO_REFRESH = 5;
3209
3207
  const T_HOST = 6;
3210
3208
  const CLEANUP = 7;
3211
3209
  const CONTEXT = 8;
@@ -3258,7 +3256,7 @@ const TYPE = 1;
3258
3256
  * that the `MOVED_VIEWS` are transplanted and on-push.
3259
3257
  */
3260
3258
  const HAS_TRANSPLANTED_VIEWS = 2;
3261
- // PARENT, NEXT, TRANSPLANTED_VIEWS_TO_REFRESH are indices 3, 4, and 5
3259
+ // PARENT, NEXT, DESCENDANT_VIEWS_TO_REFRESH are indices 3, 4, and 5
3262
3260
  // As we already have these constants in LView, we don't need to re-create them.
3263
3261
  // T_HOST is index 6
3264
3262
  // We already have this constants in LView, we don't need to re-create it.
@@ -4260,20 +4258,44 @@ function resetPreOrderHookFlags(lView) {
4260
4258
  lView[PREORDER_HOOK_FLAGS] = 0;
4261
4259
  }
4262
4260
  /**
4263
- * Updates the `TRANSPLANTED_VIEWS_TO_REFRESH` counter on the `LContainer` as well as the parents
4264
- * whose
4261
+ * Adds the `RefreshView` flag from the lView and updates DESCENDANT_VIEWS_TO_REFRESH counters of
4262
+ * parents.
4263
+ */
4264
+ function markViewForRefresh(lView) {
4265
+ if ((lView[FLAGS] & 1024 /* LViewFlags.RefreshView */) === 0) {
4266
+ lView[FLAGS] |= 1024 /* LViewFlags.RefreshView */;
4267
+ updateViewsToRefresh(lView, 1);
4268
+ }
4269
+ }
4270
+ /**
4271
+ * Removes the `RefreshView` flag from the lView and updates DESCENDANT_VIEWS_TO_REFRESH counters of
4272
+ * parents.
4273
+ */
4274
+ function clearViewRefreshFlag(lView) {
4275
+ if (lView[FLAGS] & 1024 /* LViewFlags.RefreshView */) {
4276
+ lView[FLAGS] &= ~1024 /* LViewFlags.RefreshView */;
4277
+ updateViewsToRefresh(lView, -1);
4278
+ }
4279
+ }
4280
+ /**
4281
+ * Updates the `DESCENDANT_VIEWS_TO_REFRESH` counter on the parents of the `LView` as well as the
4282
+ * parents above that whose
4265
4283
  * 1. counter goes from 0 to 1, indicating that there is a new child that has a view to refresh
4266
4284
  * or
4267
4285
  * 2. counter goes from 1 to 0, indicating there are no more descendant views to refresh
4268
4286
  */
4269
- function updateTransplantedViewCount(lContainer, amount) {
4270
- lContainer[TRANSPLANTED_VIEWS_TO_REFRESH] += amount;
4271
- let viewOrContainer = lContainer;
4272
- let parent = lContainer[PARENT];
4287
+ function updateViewsToRefresh(lView, amount) {
4288
+ let parent = lView[PARENT];
4289
+ if (parent === null) {
4290
+ return;
4291
+ }
4292
+ parent[DESCENDANT_VIEWS_TO_REFRESH] += amount;
4293
+ let viewOrContainer = parent;
4294
+ parent = parent[PARENT];
4273
4295
  while (parent !== null &&
4274
- ((amount === 1 && viewOrContainer[TRANSPLANTED_VIEWS_TO_REFRESH] === 1) ||
4275
- (amount === -1 && viewOrContainer[TRANSPLANTED_VIEWS_TO_REFRESH] === 0))) {
4276
- parent[TRANSPLANTED_VIEWS_TO_REFRESH] += amount;
4296
+ ((amount === 1 && viewOrContainer[DESCENDANT_VIEWS_TO_REFRESH] === 1) ||
4297
+ (amount === -1 && viewOrContainer[DESCENDANT_VIEWS_TO_REFRESH] === 0))) {
4298
+ parent[DESCENDANT_VIEWS_TO_REFRESH] += amount;
4277
4299
  viewOrContainer = parent;
4278
4300
  parent = parent[PARENT];
4279
4301
  }
@@ -7249,12 +7271,8 @@ function detachMovedView(declarationContainer, lView) {
7249
7271
  const insertionLContainer = lView[PARENT];
7250
7272
  ngDevMode && assertLContainer(insertionLContainer);
7251
7273
  // If the view was marked for refresh but then detached before it was checked (where the flag
7252
- // would be cleared and the counter decremented), we need to decrement the view counter here
7253
- // instead.
7254
- if (lView[FLAGS] & 1024 /* LViewFlags.RefreshTransplantedView */) {
7255
- lView[FLAGS] &= ~1024 /* LViewFlags.RefreshTransplantedView */;
7256
- updateTransplantedViewCount(insertionLContainer, -1);
7257
- }
7274
+ // would be cleared and the counter decremented), we need to update the status here.
7275
+ clearViewRefreshFlag(lView);
7258
7276
  movedViews.splice(declarationViewIndex, 1);
7259
7277
  }
7260
7278
  /**
@@ -10359,7 +10377,7 @@ class Version {
10359
10377
  /**
10360
10378
  * @publicApi
10361
10379
  */
10362
- const VERSION = new Version('16.0.0-rc.2');
10380
+ const VERSION = new Version('16.0.0-rc.4');
10363
10381
 
10364
10382
  // This default value is when checking the hierarchy for a token.
10365
10383
  //
@@ -11065,36 +11083,6 @@ function processHostBindingOpCodes(tView, lView) {
11065
11083
  setSelectedIndex(-1);
11066
11084
  }
11067
11085
  }
11068
- /** Refreshes all content queries declared by directives in a given view */
11069
- function refreshContentQueries(tView, lView) {
11070
- const contentQueries = tView.contentQueries;
11071
- if (contentQueries !== null) {
11072
- for (let i = 0; i < contentQueries.length; i += 2) {
11073
- const queryStartIdx = contentQueries[i];
11074
- const directiveDefIdx = contentQueries[i + 1];
11075
- if (directiveDefIdx !== -1) {
11076
- const directiveDef = tView.data[directiveDefIdx];
11077
- ngDevMode && assertDefined(directiveDef, 'DirectiveDef not found.');
11078
- ngDevMode &&
11079
- assertDefined(directiveDef.contentQueries, 'contentQueries function should be defined');
11080
- setCurrentQueryIndex(queryStartIdx);
11081
- directiveDef.contentQueries(2 /* RenderFlags.Update */, lView[directiveDefIdx], directiveDefIdx);
11082
- }
11083
- }
11084
- }
11085
- }
11086
- /** Refreshes child components in the current view (update mode). */
11087
- function refreshChildComponents(hostLView, components) {
11088
- for (let i = 0; i < components.length; i++) {
11089
- refreshComponent(hostLView, components[i]);
11090
- }
11091
- }
11092
- /** Renders child components in the current view (creation mode). */
11093
- function renderChildComponents(hostLView, components) {
11094
- for (let i = 0; i < components.length; i++) {
11095
- renderComponent(hostLView, components[i]);
11096
- }
11097
- }
11098
11086
  function createLView(parentLView, tView, context, flags, host, tHostNode, environment, renderer, injector, embeddedViewInjector, hydrationInfo) {
11099
11087
  const lView = tView.blueprint.slice();
11100
11088
  lView[HOST] = host;
@@ -11211,195 +11199,6 @@ function allocExpando(tView, lView, numSlotsToAlloc, initialValue) {
11211
11199
  }
11212
11200
  return allocIdx;
11213
11201
  }
11214
- //////////////////////////
11215
- //// Render
11216
- //////////////////////////
11217
- /**
11218
- * Processes a view in the creation mode. This includes a number of steps in a specific order:
11219
- * - creating view query functions (if any);
11220
- * - executing a template function in the creation mode;
11221
- * - updating static queries (if any);
11222
- * - creating child components defined in a given view.
11223
- */
11224
- function renderView(tView, lView, context) {
11225
- ngDevMode && assertEqual(isCreationMode(lView), true, 'Should be run in creation mode');
11226
- enterView(lView);
11227
- try {
11228
- const viewQuery = tView.viewQuery;
11229
- if (viewQuery !== null) {
11230
- executeViewQueryFn(1 /* RenderFlags.Create */, viewQuery, context);
11231
- }
11232
- // Execute a template associated with this view, if it exists. A template function might not be
11233
- // defined for the root component views.
11234
- const templateFn = tView.template;
11235
- if (templateFn !== null) {
11236
- executeTemplate(tView, lView, templateFn, 1 /* RenderFlags.Create */, context);
11237
- }
11238
- // This needs to be set before children are processed to support recursive components.
11239
- // This must be set to false immediately after the first creation run because in an
11240
- // ngFor loop, all the views will be created together before update mode runs and turns
11241
- // off firstCreatePass. If we don't set it here, instances will perform directive
11242
- // matching, etc again and again.
11243
- if (tView.firstCreatePass) {
11244
- tView.firstCreatePass = false;
11245
- }
11246
- // We resolve content queries specifically marked as `static` in creation mode. Dynamic
11247
- // content queries are resolved during change detection (i.e. update mode), after embedded
11248
- // views are refreshed (see block above).
11249
- if (tView.staticContentQueries) {
11250
- refreshContentQueries(tView, lView);
11251
- }
11252
- // We must materialize query results before child components are processed
11253
- // in case a child component has projected a container. The LContainer needs
11254
- // to exist so the embedded views are properly attached by the container.
11255
- if (tView.staticViewQueries) {
11256
- executeViewQueryFn(2 /* RenderFlags.Update */, tView.viewQuery, context);
11257
- }
11258
- // Render child component views.
11259
- const components = tView.components;
11260
- if (components !== null) {
11261
- renderChildComponents(lView, components);
11262
- }
11263
- }
11264
- catch (error) {
11265
- // If we didn't manage to get past the first template pass due to
11266
- // an error, mark the view as corrupted so we can try to recover.
11267
- if (tView.firstCreatePass) {
11268
- tView.incompleteFirstPass = true;
11269
- tView.firstCreatePass = false;
11270
- }
11271
- throw error;
11272
- }
11273
- finally {
11274
- lView[FLAGS] &= ~4 /* LViewFlags.CreationMode */;
11275
- leaveView();
11276
- }
11277
- }
11278
- /**
11279
- * Processes a view in update mode. This includes a number of steps in a specific order:
11280
- * - executing a template function in update mode;
11281
- * - executing hooks;
11282
- * - refreshing queries;
11283
- * - setting host bindings;
11284
- * - refreshing child (embedded and component) views.
11285
- */
11286
- function refreshView(tView, lView, templateFn, context) {
11287
- ngDevMode && assertEqual(isCreationMode(lView), false, 'Should be run in update mode');
11288
- const flags = lView[FLAGS];
11289
- if ((flags & 256 /* LViewFlags.Destroyed */) === 256 /* LViewFlags.Destroyed */)
11290
- return;
11291
- // Check no changes mode is a dev only mode used to verify that bindings have not changed
11292
- // since they were assigned. We do not want to execute lifecycle hooks in that mode.
11293
- const isInCheckNoChangesPass = ngDevMode && isInCheckNoChangesMode();
11294
- !isInCheckNoChangesPass && lView[ENVIRONMENT].effectManager?.flush();
11295
- enterView(lView);
11296
- try {
11297
- resetPreOrderHookFlags(lView);
11298
- setBindingIndex(tView.bindingStartIndex);
11299
- if (templateFn !== null) {
11300
- executeTemplate(tView, lView, templateFn, 2 /* RenderFlags.Update */, context);
11301
- }
11302
- const hooksInitPhaseCompleted = (flags & 3 /* LViewFlags.InitPhaseStateMask */) === 3 /* InitPhaseState.InitPhaseCompleted */;
11303
- // execute pre-order hooks (OnInit, OnChanges, DoCheck)
11304
- // PERF WARNING: do NOT extract this to a separate function without running benchmarks
11305
- if (!isInCheckNoChangesPass) {
11306
- if (hooksInitPhaseCompleted) {
11307
- const preOrderCheckHooks = tView.preOrderCheckHooks;
11308
- if (preOrderCheckHooks !== null) {
11309
- executeCheckHooks(lView, preOrderCheckHooks, null);
11310
- }
11311
- }
11312
- else {
11313
- const preOrderHooks = tView.preOrderHooks;
11314
- if (preOrderHooks !== null) {
11315
- executeInitAndCheckHooks(lView, preOrderHooks, 0 /* InitPhaseState.OnInitHooksToBeRun */, null);
11316
- }
11317
- incrementInitPhaseFlags(lView, 0 /* InitPhaseState.OnInitHooksToBeRun */);
11318
- }
11319
- }
11320
- // First mark transplanted views that are declared in this lView as needing a refresh at their
11321
- // insertion points. This is needed to avoid the situation where the template is defined in this
11322
- // `LView` but its declaration appears after the insertion component.
11323
- markTransplantedViewsForRefresh(lView);
11324
- refreshEmbeddedViews(lView);
11325
- // Content query results must be refreshed before content hooks are called.
11326
- if (tView.contentQueries !== null) {
11327
- refreshContentQueries(tView, lView);
11328
- }
11329
- // execute content hooks (AfterContentInit, AfterContentChecked)
11330
- // PERF WARNING: do NOT extract this to a separate function without running benchmarks
11331
- if (!isInCheckNoChangesPass) {
11332
- if (hooksInitPhaseCompleted) {
11333
- const contentCheckHooks = tView.contentCheckHooks;
11334
- if (contentCheckHooks !== null) {
11335
- executeCheckHooks(lView, contentCheckHooks);
11336
- }
11337
- }
11338
- else {
11339
- const contentHooks = tView.contentHooks;
11340
- if (contentHooks !== null) {
11341
- executeInitAndCheckHooks(lView, contentHooks, 1 /* InitPhaseState.AfterContentInitHooksToBeRun */);
11342
- }
11343
- incrementInitPhaseFlags(lView, 1 /* InitPhaseState.AfterContentInitHooksToBeRun */);
11344
- }
11345
- }
11346
- processHostBindingOpCodes(tView, lView);
11347
- // Refresh child component views.
11348
- const components = tView.components;
11349
- if (components !== null) {
11350
- refreshChildComponents(lView, components);
11351
- }
11352
- // View queries must execute after refreshing child components because a template in this view
11353
- // could be inserted in a child component. If the view query executes before child component
11354
- // refresh, the template might not yet be inserted.
11355
- const viewQuery = tView.viewQuery;
11356
- if (viewQuery !== null) {
11357
- executeViewQueryFn(2 /* RenderFlags.Update */, viewQuery, context);
11358
- }
11359
- // execute view hooks (AfterViewInit, AfterViewChecked)
11360
- // PERF WARNING: do NOT extract this to a separate function without running benchmarks
11361
- if (!isInCheckNoChangesPass) {
11362
- if (hooksInitPhaseCompleted) {
11363
- const viewCheckHooks = tView.viewCheckHooks;
11364
- if (viewCheckHooks !== null) {
11365
- executeCheckHooks(lView, viewCheckHooks);
11366
- }
11367
- }
11368
- else {
11369
- const viewHooks = tView.viewHooks;
11370
- if (viewHooks !== null) {
11371
- executeInitAndCheckHooks(lView, viewHooks, 2 /* InitPhaseState.AfterViewInitHooksToBeRun */);
11372
- }
11373
- incrementInitPhaseFlags(lView, 2 /* InitPhaseState.AfterViewInitHooksToBeRun */);
11374
- }
11375
- }
11376
- if (tView.firstUpdatePass === true) {
11377
- // We need to make sure that we only flip the flag on successful `refreshView` only
11378
- // Don't do this in `finally` block.
11379
- // If we did this in `finally` block then an exception could block the execution of styling
11380
- // instructions which in turn would be unable to insert themselves into the styling linked
11381
- // list. The result of this would be that if the exception would not be throw on subsequent CD
11382
- // the styling would be unable to process it data and reflect to the DOM.
11383
- tView.firstUpdatePass = false;
11384
- }
11385
- // Do not reset the dirty state when running in check no changes mode. We don't want components
11386
- // to behave differently depending on whether check no changes is enabled or not. For example:
11387
- // Marking an OnPush component as dirty from within the `ngAfterViewInit` hook in order to
11388
- // refresh a `NgClass` binding should work. If we would reset the dirty state in the check
11389
- // no changes cycle, the component would be not be dirty for the next update pass. This would
11390
- // be different in production mode where the component dirty state is not reset.
11391
- if (!isInCheckNoChangesPass) {
11392
- lView[FLAGS] &= ~(64 /* LViewFlags.Dirty */ | 8 /* LViewFlags.FirstLViewPass */);
11393
- }
11394
- if (lView[FLAGS] & 1024 /* LViewFlags.RefreshTransplantedView */) {
11395
- lView[FLAGS] &= ~1024 /* LViewFlags.RefreshTransplantedView */;
11396
- updateTransplantedViewCount(lView[PARENT], -1);
11397
- }
11398
- }
11399
- finally {
11400
- leaveView();
11401
- }
11402
- }
11403
11202
  function executeTemplate(tView, lView, templateFn, rf, context) {
11404
11203
  const consumer = getReactiveLViewConsumer(lView, REACTIVE_TEMPLATE_CONSUMER);
11405
11204
  const prevSelectedIndex = getSelectedIndex();
@@ -12382,153 +12181,26 @@ function createLContainer(hostNative, currentView, native, tNode) {
12382
12181
  assertEqual(lContainer.length, CONTAINER_HEADER_OFFSET, 'Should allocate correct number of slots for LContainer header.');
12383
12182
  return lContainer;
12384
12183
  }
12385
- /**
12386
- * Goes over embedded views (ones created through ViewContainerRef APIs) and refreshes
12387
- * them by executing an associated template function.
12388
- */
12389
- function refreshEmbeddedViews(lView) {
12390
- for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
12391
- for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
12392
- const embeddedLView = lContainer[i];
12393
- const embeddedTView = embeddedLView[TVIEW];
12394
- ngDevMode && assertDefined(embeddedTView, 'TView must be allocated');
12395
- if (viewAttachedToChangeDetector(embeddedLView)) {
12396
- refreshView(embeddedTView, embeddedLView, embeddedTView.template, embeddedLView[CONTEXT]);
12184
+ /** Refreshes all content queries declared by directives in a given view */
12185
+ function refreshContentQueries(tView, lView) {
12186
+ const contentQueries = tView.contentQueries;
12187
+ if (contentQueries !== null) {
12188
+ for (let i = 0; i < contentQueries.length; i += 2) {
12189
+ const queryStartIdx = contentQueries[i];
12190
+ const directiveDefIdx = contentQueries[i + 1];
12191
+ if (directiveDefIdx !== -1) {
12192
+ const directiveDef = tView.data[directiveDefIdx];
12193
+ ngDevMode && assertDefined(directiveDef, 'DirectiveDef not found.');
12194
+ ngDevMode &&
12195
+ assertDefined(directiveDef.contentQueries, 'contentQueries function should be defined');
12196
+ setCurrentQueryIndex(queryStartIdx);
12197
+ directiveDef.contentQueries(2 /* RenderFlags.Update */, lView[directiveDefIdx], directiveDefIdx);
12397
12198
  }
12398
12199
  }
12399
12200
  }
12400
12201
  }
12401
12202
  /**
12402
- * Mark transplanted views as needing to be refreshed at their insertion points.
12403
- *
12404
- * @param lView The `LView` that may have transplanted views.
12405
- */
12406
- function markTransplantedViewsForRefresh(lView) {
12407
- for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
12408
- if (!lContainer[HAS_TRANSPLANTED_VIEWS])
12409
- continue;
12410
- const movedViews = lContainer[MOVED_VIEWS];
12411
- ngDevMode && assertDefined(movedViews, 'Transplanted View flags set but missing MOVED_VIEWS');
12412
- for (let i = 0; i < movedViews.length; i++) {
12413
- const movedLView = movedViews[i];
12414
- const insertionLContainer = movedLView[PARENT];
12415
- ngDevMode && assertLContainer(insertionLContainer);
12416
- // We don't want to increment the counter if the moved LView was already marked for
12417
- // refresh.
12418
- if ((movedLView[FLAGS] & 1024 /* LViewFlags.RefreshTransplantedView */) === 0) {
12419
- updateTransplantedViewCount(insertionLContainer, 1);
12420
- }
12421
- // Note, it is possible that the `movedViews` is tracking views that are transplanted *and*
12422
- // those that aren't (declaration component === insertion component). In the latter case,
12423
- // it's fine to add the flag, as we will clear it immediately in
12424
- // `refreshEmbeddedViews` for the view currently being refreshed.
12425
- movedLView[FLAGS] |= 1024 /* LViewFlags.RefreshTransplantedView */;
12426
- }
12427
- }
12428
- }
12429
- /////////////
12430
- /**
12431
- * Refreshes components by entering the component view and processing its bindings, queries, etc.
12432
- *
12433
- * @param componentHostIdx Element index in LView[] (adjusted for HEADER_OFFSET)
12434
- */
12435
- function refreshComponent(hostLView, componentHostIdx) {
12436
- ngDevMode && assertEqual(isCreationMode(hostLView), false, 'Should be run in update mode');
12437
- const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
12438
- // Only attached components that are CheckAlways or OnPush and dirty should be refreshed
12439
- if (viewAttachedToChangeDetector(componentView)) {
12440
- const tView = componentView[TVIEW];
12441
- if (componentView[FLAGS] & (16 /* LViewFlags.CheckAlways */ | 64 /* LViewFlags.Dirty */)) {
12442
- refreshView(tView, componentView, tView.template, componentView[CONTEXT]);
12443
- }
12444
- else if (componentView[TRANSPLANTED_VIEWS_TO_REFRESH] > 0) {
12445
- // Only attached components that are CheckAlways or OnPush and dirty should be refreshed
12446
- refreshContainsDirtyView(componentView);
12447
- }
12448
- }
12449
- }
12450
- /**
12451
- * Refreshes all transplanted views marked with `LViewFlags.RefreshTransplantedView` that are
12452
- * children or descendants of the given lView.
12453
- *
12454
- * @param lView The lView which contains descendant transplanted views that need to be refreshed.
12455
- */
12456
- function refreshContainsDirtyView(lView) {
12457
- for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
12458
- for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
12459
- const embeddedLView = lContainer[i];
12460
- if (viewAttachedToChangeDetector(embeddedLView)) {
12461
- if (embeddedLView[FLAGS] & 1024 /* LViewFlags.RefreshTransplantedView */) {
12462
- const embeddedTView = embeddedLView[TVIEW];
12463
- ngDevMode && assertDefined(embeddedTView, 'TView must be allocated');
12464
- refreshView(embeddedTView, embeddedLView, embeddedTView.template, embeddedLView[CONTEXT]);
12465
- }
12466
- else if (embeddedLView[TRANSPLANTED_VIEWS_TO_REFRESH] > 0) {
12467
- refreshContainsDirtyView(embeddedLView);
12468
- }
12469
- }
12470
- }
12471
- }
12472
- const tView = lView[TVIEW];
12473
- // Refresh child component views.
12474
- const components = tView.components;
12475
- if (components !== null) {
12476
- for (let i = 0; i < components.length; i++) {
12477
- const componentView = getComponentLViewByIndex(components[i], lView);
12478
- // Only attached components that are CheckAlways or OnPush and dirty should be refreshed
12479
- if (viewAttachedToChangeDetector(componentView) &&
12480
- componentView[TRANSPLANTED_VIEWS_TO_REFRESH] > 0) {
12481
- refreshContainsDirtyView(componentView);
12482
- }
12483
- }
12484
- }
12485
- }
12486
- function renderComponent(hostLView, componentHostIdx) {
12487
- ngDevMode && assertEqual(isCreationMode(hostLView), true, 'Should be run in creation mode');
12488
- const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
12489
- const componentTView = componentView[TVIEW];
12490
- syncViewWithBlueprint(componentTView, componentView);
12491
- const hostRNode = componentView[HOST];
12492
- // Populate an LView with hydration info retrieved from the DOM via TransferState.
12493
- if (hostRNode !== null && componentView[HYDRATION] === null) {
12494
- componentView[HYDRATION] = retrieveHydrationInfo(hostRNode, componentView[INJECTOR$1]);
12495
- }
12496
- renderView(componentTView, componentView, componentView[CONTEXT]);
12497
- }
12498
- /**
12499
- * Syncs an LView instance with its blueprint if they have gotten out of sync.
12500
- *
12501
- * Typically, blueprints and their view instances should always be in sync, so the loop here
12502
- * will be skipped. However, consider this case of two components side-by-side:
12503
- *
12504
- * App template:
12505
- * ```
12506
- * <comp></comp>
12507
- * <comp></comp>
12508
- * ```
12509
- *
12510
- * The following will happen:
12511
- * 1. App template begins processing.
12512
- * 2. First <comp> is matched as a component and its LView is created.
12513
- * 3. Second <comp> is matched as a component and its LView is created.
12514
- * 4. App template completes processing, so it's time to check child templates.
12515
- * 5. First <comp> template is checked. It has a directive, so its def is pushed to blueprint.
12516
- * 6. Second <comp> template is checked. Its blueprint has been updated by the first
12517
- * <comp> template, but its LView was created before this update, so it is out of sync.
12518
- *
12519
- * Note that embedded views inside ngFor loops will never be out of sync because these views
12520
- * are processed as soon as they are created.
12521
- *
12522
- * @param tView The `TView` that contains the blueprint for syncing
12523
- * @param lView The view to sync
12524
- */
12525
- function syncViewWithBlueprint(tView, lView) {
12526
- for (let i = lView.length; i < tView.blueprint.length; i++) {
12527
- lView.push(tView.blueprint[i]);
12528
- }
12529
- }
12530
- /**
12531
- * Adds LView or LContainer to the end of the current view tree.
12203
+ * Adds LView or LContainer to the end of the current view tree.
12532
12204
  *
12533
12205
  * This structure will be used to traverse through nested views to remove listeners
12534
12206
  * and call onDestroy callbacks.
@@ -12555,40 +12227,6 @@ function addToViewTree(lView, lViewOrLContainer) {
12555
12227
  ///////////////////////////////
12556
12228
  //// Change detection
12557
12229
  ///////////////////////////////
12558
- function detectChangesInternal(tView, lView, context, notifyErrorHandler = true) {
12559
- const rendererFactory = lView[ENVIRONMENT].rendererFactory;
12560
- // Check no changes mode is a dev only mode used to verify that bindings have not changed
12561
- // since they were assigned. We do not want to invoke renderer factory functions in that mode
12562
- // to avoid any possible side-effects.
12563
- const checkNoChangesMode = !!ngDevMode && isInCheckNoChangesMode();
12564
- if (!checkNoChangesMode && rendererFactory.begin)
12565
- rendererFactory.begin();
12566
- try {
12567
- refreshView(tView, lView, tView.template, context);
12568
- }
12569
- catch (error) {
12570
- if (notifyErrorHandler) {
12571
- handleError(lView, error);
12572
- }
12573
- throw error;
12574
- }
12575
- finally {
12576
- if (!checkNoChangesMode && rendererFactory.end)
12577
- rendererFactory.end();
12578
- // One final flush of the effects queue to catch any effects created in `ngAfterViewInit` or
12579
- // other post-order hooks.
12580
- !checkNoChangesMode && lView[ENVIRONMENT].effectManager?.flush();
12581
- }
12582
- }
12583
- function checkNoChangesInternal(tView, lView, context, notifyErrorHandler = true) {
12584
- setIsInCheckNoChangesMode(true);
12585
- try {
12586
- detectChangesInternal(tView, lView, context, notifyErrorHandler);
12587
- }
12588
- finally {
12589
- setIsInCheckNoChangesMode(false);
12590
- }
12591
- }
12592
12230
  function executeViewQueryFn(flags, viewQueryFn, component) {
12593
12231
  ngDevMode && assertDefined(viewQueryFn, 'View queries function to execute must be defined.');
12594
12232
  setCurrentQueryIndex(0);
@@ -12701,205 +12339,582 @@ function textBindingInternal(lView, index, value) {
12701
12339
  updateTextNode(lView[RENDERER], element, value);
12702
12340
  }
12703
12341
 
12342
+ function renderComponent(hostLView, componentHostIdx) {
12343
+ ngDevMode && assertEqual(isCreationMode(hostLView), true, 'Should be run in creation mode');
12344
+ const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
12345
+ const componentTView = componentView[TVIEW];
12346
+ syncViewWithBlueprint(componentTView, componentView);
12347
+ const hostRNode = componentView[HOST];
12348
+ // Populate an LView with hydration info retrieved from the DOM via TransferState.
12349
+ if (hostRNode !== null && componentView[HYDRATION] === null) {
12350
+ componentView[HYDRATION] = retrieveHydrationInfo(hostRNode, componentView[INJECTOR$1]);
12351
+ }
12352
+ renderView(componentTView, componentView, componentView[CONTEXT]);
12353
+ }
12704
12354
  /**
12705
- * `DestroyRef` lets you set callbacks to run for any cleanup or destruction behavior.
12706
- * The scope of this destruction depends on where `DestroyRef` is injected. If `DestroyRef`
12707
- * is injected in a component or directive, the callbacks run when that component or
12708
- * directive is destroyed. Otherwise the callbacks run when a corresponding injector is destroyed.
12355
+ * Syncs an LView instance with its blueprint if they have gotten out of sync.
12709
12356
  *
12710
- * @publicApi
12357
+ * Typically, blueprints and their view instances should always be in sync, so the loop here
12358
+ * will be skipped. However, consider this case of two components side-by-side:
12359
+ *
12360
+ * App template:
12361
+ * ```
12362
+ * <comp></comp>
12363
+ * <comp></comp>
12364
+ * ```
12365
+ *
12366
+ * The following will happen:
12367
+ * 1. App template begins processing.
12368
+ * 2. First <comp> is matched as a component and its LView is created.
12369
+ * 3. Second <comp> is matched as a component and its LView is created.
12370
+ * 4. App template completes processing, so it's time to check child templates.
12371
+ * 5. First <comp> template is checked. It has a directive, so its def is pushed to blueprint.
12372
+ * 6. Second <comp> template is checked. Its blueprint has been updated by the first
12373
+ * <comp> template, but its LView was created before this update, so it is out of sync.
12374
+ *
12375
+ * Note that embedded views inside ngFor loops will never be out of sync because these views
12376
+ * are processed as soon as they are created.
12377
+ *
12378
+ * @param tView The `TView` that contains the blueprint for syncing
12379
+ * @param lView The view to sync
12711
12380
  */
12712
- class DestroyRef {
12713
- /**
12714
- * @internal
12715
- * @nocollapse
12716
- */
12717
- static { this.__NG_ELEMENT_ID__ = injectDestroyRef; }
12718
- /**
12719
- * @internal
12720
- * @nocollapse
12721
- */
12722
- static { this.__NG_ENV_ID__ = (injector) => injector; }
12381
+ function syncViewWithBlueprint(tView, lView) {
12382
+ for (let i = lView.length; i < tView.blueprint.length; i++) {
12383
+ lView.push(tView.blueprint[i]);
12384
+ }
12723
12385
  }
12724
- class NodeInjectorDestroyRef extends DestroyRef {
12725
- constructor(_lView) {
12726
- super();
12727
- this._lView = _lView;
12386
+ /**
12387
+ * Processes a view in the creation mode. This includes a number of steps in a specific order:
12388
+ * - creating view query functions (if any);
12389
+ * - executing a template function in the creation mode;
12390
+ * - updating static queries (if any);
12391
+ * - creating child components defined in a given view.
12392
+ */
12393
+ function renderView(tView, lView, context) {
12394
+ ngDevMode && assertEqual(isCreationMode(lView), true, 'Should be run in creation mode');
12395
+ enterView(lView);
12396
+ try {
12397
+ const viewQuery = tView.viewQuery;
12398
+ if (viewQuery !== null) {
12399
+ executeViewQueryFn(1 /* RenderFlags.Create */, viewQuery, context);
12400
+ }
12401
+ // Execute a template associated with this view, if it exists. A template function might not be
12402
+ // defined for the root component views.
12403
+ const templateFn = tView.template;
12404
+ if (templateFn !== null) {
12405
+ executeTemplate(tView, lView, templateFn, 1 /* RenderFlags.Create */, context);
12406
+ }
12407
+ // This needs to be set before children are processed to support recursive components.
12408
+ // This must be set to false immediately after the first creation run because in an
12409
+ // ngFor loop, all the views will be created together before update mode runs and turns
12410
+ // off firstCreatePass. If we don't set it here, instances will perform directive
12411
+ // matching, etc again and again.
12412
+ if (tView.firstCreatePass) {
12413
+ tView.firstCreatePass = false;
12414
+ }
12415
+ // We resolve content queries specifically marked as `static` in creation mode. Dynamic
12416
+ // content queries are resolved during change detection (i.e. update mode), after embedded
12417
+ // views are refreshed (see block above).
12418
+ if (tView.staticContentQueries) {
12419
+ refreshContentQueries(tView, lView);
12420
+ }
12421
+ // We must materialize query results before child components are processed
12422
+ // in case a child component has projected a container. The LContainer needs
12423
+ // to exist so the embedded views are properly attached by the container.
12424
+ if (tView.staticViewQueries) {
12425
+ executeViewQueryFn(2 /* RenderFlags.Update */, tView.viewQuery, context);
12426
+ }
12427
+ // Render child component views.
12428
+ const components = tView.components;
12429
+ if (components !== null) {
12430
+ renderChildComponents(lView, components);
12431
+ }
12432
+ }
12433
+ catch (error) {
12434
+ // If we didn't manage to get past the first template pass due to
12435
+ // an error, mark the view as corrupted so we can try to recover.
12436
+ if (tView.firstCreatePass) {
12437
+ tView.incompleteFirstPass = true;
12438
+ tView.firstCreatePass = false;
12439
+ }
12440
+ throw error;
12441
+ }
12442
+ finally {
12443
+ lView[FLAGS] &= ~4 /* LViewFlags.CreationMode */;
12444
+ leaveView();
12445
+ }
12446
+ }
12447
+ /** Renders child components in the current view (creation mode). */
12448
+ function renderChildComponents(hostLView, components) {
12449
+ for (let i = 0; i < components.length; i++) {
12450
+ renderComponent(hostLView, components[i]);
12451
+ }
12452
+ }
12453
+
12454
+ /**
12455
+ * `DestroyRef` lets you set callbacks to run for any cleanup or destruction behavior.
12456
+ * The scope of this destruction depends on where `DestroyRef` is injected. If `DestroyRef`
12457
+ * is injected in a component or directive, the callbacks run when that component or
12458
+ * directive is destroyed. Otherwise the callbacks run when a corresponding injector is destroyed.
12459
+ *
12460
+ * @publicApi
12461
+ */
12462
+ class DestroyRef {
12463
+ /**
12464
+ * @internal
12465
+ * @nocollapse
12466
+ */
12467
+ static { this.__NG_ELEMENT_ID__ = injectDestroyRef; }
12468
+ /**
12469
+ * @internal
12470
+ * @nocollapse
12471
+ */
12472
+ static { this.__NG_ENV_ID__ = (injector) => injector; }
12473
+ }
12474
+ class NodeInjectorDestroyRef extends DestroyRef {
12475
+ constructor(_lView) {
12476
+ super();
12477
+ this._lView = _lView;
12478
+ }
12479
+ onDestroy(callback) {
12480
+ storeLViewOnDestroy(this._lView, callback);
12481
+ return () => removeLViewOnDestroy(this._lView, callback);
12482
+ }
12483
+ }
12484
+ function injectDestroyRef() {
12485
+ return new NodeInjectorDestroyRef(getLView());
12486
+ }
12487
+
12488
+ /**
12489
+ * Tracks all effects registered within a given application and runs them via `flush`.
12490
+ */
12491
+ class EffectManager {
12492
+ constructor() {
12493
+ this.all = new Set();
12494
+ this.queue = new Map();
12495
+ }
12496
+ create(effectFn, destroyRef, allowSignalWrites) {
12497
+ const zone = (typeof Zone === 'undefined') ? null : Zone.current;
12498
+ const watch = new Watch(effectFn, (watch) => {
12499
+ if (!this.all.has(watch)) {
12500
+ return;
12501
+ }
12502
+ this.queue.set(watch, zone);
12503
+ }, allowSignalWrites);
12504
+ this.all.add(watch);
12505
+ // Effects start dirty.
12506
+ watch.notify();
12507
+ let unregisterOnDestroy;
12508
+ const destroy = () => {
12509
+ watch.cleanup();
12510
+ unregisterOnDestroy?.();
12511
+ this.all.delete(watch);
12512
+ this.queue.delete(watch);
12513
+ };
12514
+ unregisterOnDestroy = destroyRef?.onDestroy(destroy);
12515
+ return {
12516
+ destroy,
12517
+ };
12518
+ }
12519
+ flush() {
12520
+ if (this.queue.size === 0) {
12521
+ return;
12522
+ }
12523
+ for (const [watch, zone] of this.queue) {
12524
+ this.queue.delete(watch);
12525
+ if (zone) {
12526
+ zone.run(() => watch.run());
12527
+ }
12528
+ else {
12529
+ watch.run();
12530
+ }
12531
+ }
12532
+ }
12533
+ get isQueueEmpty() {
12534
+ return this.queue.size === 0;
12535
+ }
12536
+ /** @nocollapse */
12537
+ static { this.ɵprov = ɵɵdefineInjectable({
12538
+ token: EffectManager,
12539
+ providedIn: 'root',
12540
+ factory: () => new EffectManager(),
12541
+ }); }
12542
+ }
12543
+ /**
12544
+ * Create a global `Effect` for the given reactive function.
12545
+ *
12546
+ * @developerPreview
12547
+ */
12548
+ function effect(effectFn, options) {
12549
+ !options?.injector && assertInInjectionContext(effect);
12550
+ const injector = options?.injector ?? inject$1(Injector);
12551
+ const effectManager = injector.get(EffectManager);
12552
+ const destroyRef = options?.manualCleanup !== true ? injector.get(DestroyRef) : null;
12553
+ return effectManager.create(effectFn, destroyRef, !!options?.allowSignalWrites);
12554
+ }
12555
+
12556
+ /**
12557
+ * Compute the static styling (class/style) from `TAttributes`.
12558
+ *
12559
+ * This function should be called during `firstCreatePass` only.
12560
+ *
12561
+ * @param tNode The `TNode` into which the styling information should be loaded.
12562
+ * @param attrs `TAttributes` containing the styling information.
12563
+ * @param writeToHost Where should the resulting static styles be written?
12564
+ * - `false` Write to `TNode.stylesWithoutHost` / `TNode.classesWithoutHost`
12565
+ * - `true` Write to `TNode.styles` / `TNode.classes`
12566
+ */
12567
+ function computeStaticStyling(tNode, attrs, writeToHost) {
12568
+ ngDevMode &&
12569
+ assertFirstCreatePass(getTView(), 'Expecting to be called in first template pass only');
12570
+ let styles = writeToHost ? tNode.styles : null;
12571
+ let classes = writeToHost ? tNode.classes : null;
12572
+ let mode = 0;
12573
+ if (attrs !== null) {
12574
+ for (let i = 0; i < attrs.length; i++) {
12575
+ const value = attrs[i];
12576
+ if (typeof value === 'number') {
12577
+ mode = value;
12578
+ }
12579
+ else if (mode == 1 /* AttributeMarker.Classes */) {
12580
+ classes = concatStringsWithSpace(classes, value);
12581
+ }
12582
+ else if (mode == 2 /* AttributeMarker.Styles */) {
12583
+ const style = value;
12584
+ const styleValue = attrs[++i];
12585
+ styles = concatStringsWithSpace(styles, style + ': ' + styleValue + ';');
12586
+ }
12587
+ }
12588
+ }
12589
+ writeToHost ? tNode.styles = styles : tNode.stylesWithoutHost = styles;
12590
+ writeToHost ? tNode.classes = classes : tNode.classesWithoutHost = classes;
12591
+ }
12592
+
12593
+ function collectNativeNodes(tView, lView, tNode, result, isProjection = false) {
12594
+ while (tNode !== null) {
12595
+ ngDevMode &&
12596
+ assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 16 /* TNodeType.Projection */ | 32 /* TNodeType.Icu */);
12597
+ const lNode = lView[tNode.index];
12598
+ if (lNode !== null) {
12599
+ result.push(unwrapRNode(lNode));
12600
+ }
12601
+ // A given lNode can represent either a native node or a LContainer (when it is a host of a
12602
+ // ViewContainerRef). When we find a LContainer we need to descend into it to collect root nodes
12603
+ // from the views in this container.
12604
+ if (isLContainer(lNode)) {
12605
+ for (let i = CONTAINER_HEADER_OFFSET; i < lNode.length; i++) {
12606
+ const lViewInAContainer = lNode[i];
12607
+ const lViewFirstChildTNode = lViewInAContainer[TVIEW].firstChild;
12608
+ if (lViewFirstChildTNode !== null) {
12609
+ collectNativeNodes(lViewInAContainer[TVIEW], lViewInAContainer, lViewFirstChildTNode, result);
12610
+ }
12611
+ }
12612
+ // When an LContainer is created, the anchor (comment) node is:
12613
+ // - (1) either reused in case of an ElementContainer (<ng-container>)
12614
+ // - (2) or a new comment node is created
12615
+ // In the first case, the anchor comment node would be added to the final
12616
+ // list by the code above (`result.push(unwrapRNode(lNode))`), but the second
12617
+ // case requires extra handling: the anchor node needs to be added to the
12618
+ // final list manually. See additional information in the `createAnchorNode`
12619
+ // function in the `view_container_ref.ts`.
12620
+ //
12621
+ // In the first case, the same reference would be stored in the `NATIVE`
12622
+ // and `HOST` slots in an LContainer. Otherwise, this is the second case and
12623
+ // we should add an element to the final list.
12624
+ if (lNode[NATIVE] !== lNode[HOST]) {
12625
+ result.push(lNode[NATIVE]);
12626
+ }
12627
+ }
12628
+ const tNodeType = tNode.type;
12629
+ if (tNodeType & 8 /* TNodeType.ElementContainer */) {
12630
+ collectNativeNodes(tView, lView, tNode.child, result);
12631
+ }
12632
+ else if (tNodeType & 32 /* TNodeType.Icu */) {
12633
+ const nextRNode = icuContainerIterate(tNode, lView);
12634
+ let rNode;
12635
+ while (rNode = nextRNode()) {
12636
+ result.push(rNode);
12637
+ }
12638
+ }
12639
+ else if (tNodeType & 16 /* TNodeType.Projection */) {
12640
+ const nodesInSlot = getProjectionNodes(lView, tNode);
12641
+ if (Array.isArray(nodesInSlot)) {
12642
+ result.push(...nodesInSlot);
12643
+ }
12644
+ else {
12645
+ const parentView = getLViewParent(lView[DECLARATION_COMPONENT_VIEW]);
12646
+ ngDevMode && assertParentView(parentView);
12647
+ collectNativeNodes(parentView[TVIEW], parentView, nodesInSlot, result, true);
12648
+ }
12649
+ }
12650
+ tNode = isProjection ? tNode.projectionNext : tNode.next;
12651
+ }
12652
+ return result;
12653
+ }
12654
+
12655
+ function detectChangesInternal(tView, lView, context, notifyErrorHandler = true) {
12656
+ const rendererFactory = lView[ENVIRONMENT].rendererFactory;
12657
+ // Check no changes mode is a dev only mode used to verify that bindings have not changed
12658
+ // since they were assigned. We do not want to invoke renderer factory functions in that mode
12659
+ // to avoid any possible side-effects.
12660
+ const checkNoChangesMode = !!ngDevMode && isInCheckNoChangesMode();
12661
+ if (!checkNoChangesMode && rendererFactory.begin)
12662
+ rendererFactory.begin();
12663
+ try {
12664
+ refreshView(tView, lView, tView.template, context);
12665
+ }
12666
+ catch (error) {
12667
+ if (notifyErrorHandler) {
12668
+ handleError(lView, error);
12669
+ }
12670
+ throw error;
12671
+ }
12672
+ finally {
12673
+ if (!checkNoChangesMode && rendererFactory.end)
12674
+ rendererFactory.end();
12675
+ // One final flush of the effects queue to catch any effects created in `ngAfterViewInit` or
12676
+ // other post-order hooks.
12677
+ !checkNoChangesMode && lView[ENVIRONMENT].effectManager?.flush();
12678
+ }
12679
+ }
12680
+ function checkNoChangesInternal(tView, lView, context, notifyErrorHandler = true) {
12681
+ setIsInCheckNoChangesMode(true);
12682
+ try {
12683
+ detectChangesInternal(tView, lView, context, notifyErrorHandler);
12684
+ }
12685
+ finally {
12686
+ setIsInCheckNoChangesMode(false);
12687
+ }
12688
+ }
12689
+ /**
12690
+ * Synchronously perform change detection on a component (and possibly its sub-components).
12691
+ *
12692
+ * This function triggers change detection in a synchronous way on a component.
12693
+ *
12694
+ * @param component The component which the change detection should be performed on.
12695
+ */
12696
+ function detectChanges(component) {
12697
+ const view = getComponentViewByInstance(component);
12698
+ detectChangesInternal(view[TVIEW], view, component);
12699
+ }
12700
+ /**
12701
+ * Processes a view in update mode. This includes a number of steps in a specific order:
12702
+ * - executing a template function in update mode;
12703
+ * - executing hooks;
12704
+ * - refreshing queries;
12705
+ * - setting host bindings;
12706
+ * - refreshing child (embedded and component) views.
12707
+ */
12708
+ function refreshView(tView, lView, templateFn, context) {
12709
+ ngDevMode && assertEqual(isCreationMode(lView), false, 'Should be run in update mode');
12710
+ const flags = lView[FLAGS];
12711
+ if ((flags & 256 /* LViewFlags.Destroyed */) === 256 /* LViewFlags.Destroyed */)
12712
+ return;
12713
+ // Check no changes mode is a dev only mode used to verify that bindings have not changed
12714
+ // since they were assigned. We do not want to execute lifecycle hooks in that mode.
12715
+ const isInCheckNoChangesPass = ngDevMode && isInCheckNoChangesMode();
12716
+ !isInCheckNoChangesPass && lView[ENVIRONMENT].effectManager?.flush();
12717
+ enterView(lView);
12718
+ try {
12719
+ resetPreOrderHookFlags(lView);
12720
+ setBindingIndex(tView.bindingStartIndex);
12721
+ if (templateFn !== null) {
12722
+ executeTemplate(tView, lView, templateFn, 2 /* RenderFlags.Update */, context);
12723
+ }
12724
+ const hooksInitPhaseCompleted = (flags & 3 /* LViewFlags.InitPhaseStateMask */) === 3 /* InitPhaseState.InitPhaseCompleted */;
12725
+ // execute pre-order hooks (OnInit, OnChanges, DoCheck)
12726
+ // PERF WARNING: do NOT extract this to a separate function without running benchmarks
12727
+ if (!isInCheckNoChangesPass) {
12728
+ if (hooksInitPhaseCompleted) {
12729
+ const preOrderCheckHooks = tView.preOrderCheckHooks;
12730
+ if (preOrderCheckHooks !== null) {
12731
+ executeCheckHooks(lView, preOrderCheckHooks, null);
12732
+ }
12733
+ }
12734
+ else {
12735
+ const preOrderHooks = tView.preOrderHooks;
12736
+ if (preOrderHooks !== null) {
12737
+ executeInitAndCheckHooks(lView, preOrderHooks, 0 /* InitPhaseState.OnInitHooksToBeRun */, null);
12738
+ }
12739
+ incrementInitPhaseFlags(lView, 0 /* InitPhaseState.OnInitHooksToBeRun */);
12740
+ }
12741
+ }
12742
+ // First mark transplanted views that are declared in this lView as needing a refresh at their
12743
+ // insertion points. This is needed to avoid the situation where the template is defined in this
12744
+ // `LView` but its declaration appears after the insertion component.
12745
+ markTransplantedViewsForRefresh(lView);
12746
+ refreshEmbeddedViews(lView);
12747
+ // Content query results must be refreshed before content hooks are called.
12748
+ if (tView.contentQueries !== null) {
12749
+ refreshContentQueries(tView, lView);
12750
+ }
12751
+ // execute content hooks (AfterContentInit, AfterContentChecked)
12752
+ // PERF WARNING: do NOT extract this to a separate function without running benchmarks
12753
+ if (!isInCheckNoChangesPass) {
12754
+ if (hooksInitPhaseCompleted) {
12755
+ const contentCheckHooks = tView.contentCheckHooks;
12756
+ if (contentCheckHooks !== null) {
12757
+ executeCheckHooks(lView, contentCheckHooks);
12758
+ }
12759
+ }
12760
+ else {
12761
+ const contentHooks = tView.contentHooks;
12762
+ if (contentHooks !== null) {
12763
+ executeInitAndCheckHooks(lView, contentHooks, 1 /* InitPhaseState.AfterContentInitHooksToBeRun */);
12764
+ }
12765
+ incrementInitPhaseFlags(lView, 1 /* InitPhaseState.AfterContentInitHooksToBeRun */);
12766
+ }
12767
+ }
12768
+ processHostBindingOpCodes(tView, lView);
12769
+ // Refresh child component views.
12770
+ const components = tView.components;
12771
+ if (components !== null) {
12772
+ refreshChildComponents(lView, components);
12773
+ }
12774
+ // View queries must execute after refreshing child components because a template in this view
12775
+ // could be inserted in a child component. If the view query executes before child component
12776
+ // refresh, the template might not yet be inserted.
12777
+ const viewQuery = tView.viewQuery;
12778
+ if (viewQuery !== null) {
12779
+ executeViewQueryFn(2 /* RenderFlags.Update */, viewQuery, context);
12780
+ }
12781
+ // execute view hooks (AfterViewInit, AfterViewChecked)
12782
+ // PERF WARNING: do NOT extract this to a separate function without running benchmarks
12783
+ if (!isInCheckNoChangesPass) {
12784
+ if (hooksInitPhaseCompleted) {
12785
+ const viewCheckHooks = tView.viewCheckHooks;
12786
+ if (viewCheckHooks !== null) {
12787
+ executeCheckHooks(lView, viewCheckHooks);
12788
+ }
12789
+ }
12790
+ else {
12791
+ const viewHooks = tView.viewHooks;
12792
+ if (viewHooks !== null) {
12793
+ executeInitAndCheckHooks(lView, viewHooks, 2 /* InitPhaseState.AfterViewInitHooksToBeRun */);
12794
+ }
12795
+ incrementInitPhaseFlags(lView, 2 /* InitPhaseState.AfterViewInitHooksToBeRun */);
12796
+ }
12797
+ }
12798
+ if (tView.firstUpdatePass === true) {
12799
+ // We need to make sure that we only flip the flag on successful `refreshView` only
12800
+ // Don't do this in `finally` block.
12801
+ // If we did this in `finally` block then an exception could block the execution of styling
12802
+ // instructions which in turn would be unable to insert themselves into the styling linked
12803
+ // list. The result of this would be that if the exception would not be throw on subsequent CD
12804
+ // the styling would be unable to process it data and reflect to the DOM.
12805
+ tView.firstUpdatePass = false;
12806
+ }
12807
+ // Do not reset the dirty state when running in check no changes mode. We don't want components
12808
+ // to behave differently depending on whether check no changes is enabled or not. For example:
12809
+ // Marking an OnPush component as dirty from within the `ngAfterViewInit` hook in order to
12810
+ // refresh a `NgClass` binding should work. If we would reset the dirty state in the check
12811
+ // no changes cycle, the component would be not be dirty for the next update pass. This would
12812
+ // be different in production mode where the component dirty state is not reset.
12813
+ if (!isInCheckNoChangesPass) {
12814
+ lView[FLAGS] &= ~(64 /* LViewFlags.Dirty */ | 8 /* LViewFlags.FirstLViewPass */);
12815
+ }
12816
+ clearViewRefreshFlag(lView);
12728
12817
  }
12729
- onDestroy(callback) {
12730
- storeLViewOnDestroy(this._lView, callback);
12731
- return () => removeLViewOnDestroy(this._lView, callback);
12818
+ finally {
12819
+ leaveView();
12732
12820
  }
12733
12821
  }
12734
- function injectDestroyRef() {
12735
- return new NodeInjectorDestroyRef(getLView());
12736
- }
12737
-
12738
12822
  /**
12739
- * Tracks all effects registered within a given application and runs them via `flush`.
12823
+ * Goes over embedded views (ones created through ViewContainerRef APIs) and refreshes
12824
+ * them by executing an associated template function.
12740
12825
  */
12741
- class EffectManager {
12742
- constructor() {
12743
- this.all = new Set();
12744
- this.queue = new Map();
12745
- }
12746
- create(effectFn, destroyRef, allowSignalWrites) {
12747
- const zone = (typeof Zone === 'undefined') ? null : Zone.current;
12748
- const watch = new Watch(effectFn, (watch) => {
12749
- if (!this.all.has(watch)) {
12750
- return;
12751
- }
12752
- this.queue.set(watch, zone);
12753
- }, allowSignalWrites);
12754
- this.all.add(watch);
12755
- // Effects start dirty.
12756
- watch.notify();
12757
- let unregisterOnDestroy;
12758
- const destroy = () => {
12759
- watch.cleanup();
12760
- unregisterOnDestroy?.();
12761
- this.all.delete(watch);
12762
- this.queue.delete(watch);
12763
- };
12764
- unregisterOnDestroy = destroyRef?.onDestroy(destroy);
12765
- return {
12766
- destroy,
12767
- };
12768
- }
12769
- flush() {
12770
- if (this.queue.size === 0) {
12771
- return;
12772
- }
12773
- for (const [watch, zone] of this.queue) {
12774
- this.queue.delete(watch);
12775
- if (zone) {
12776
- zone.run(() => watch.run());
12777
- }
12778
- else {
12779
- watch.run();
12826
+ function refreshEmbeddedViews(lView) {
12827
+ for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
12828
+ for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
12829
+ const embeddedLView = lContainer[i];
12830
+ const embeddedTView = embeddedLView[TVIEW];
12831
+ ngDevMode && assertDefined(embeddedTView, 'TView must be allocated');
12832
+ if (viewAttachedToChangeDetector(embeddedLView)) {
12833
+ refreshView(embeddedTView, embeddedLView, embeddedTView.template, embeddedLView[CONTEXT]);
12780
12834
  }
12781
12835
  }
12782
12836
  }
12783
- get isQueueEmpty() {
12784
- return this.queue.size === 0;
12785
- }
12786
- /** @nocollapse */
12787
- static { this.ɵprov = ɵɵdefineInjectable({
12788
- token: EffectManager,
12789
- providedIn: 'root',
12790
- factory: () => new EffectManager(),
12791
- }); }
12792
12837
  }
12793
12838
  /**
12794
- * Create a global `Effect` for the given reactive function.
12839
+ * Mark transplanted views as needing to be refreshed at their insertion points.
12795
12840
  *
12796
- * @developerPreview
12841
+ * @param lView The `LView` that may have transplanted views.
12797
12842
  */
12798
- function effect(effectFn, options) {
12799
- !options?.injector && assertInInjectionContext(effect);
12800
- const injector = options?.injector ?? inject$1(Injector);
12801
- const effectManager = injector.get(EffectManager);
12802
- const destroyRef = options?.manualCleanup !== true ? injector.get(DestroyRef) : null;
12803
- return effectManager.create(effectFn, destroyRef, !!options?.allowSignalWrites);
12843
+ function markTransplantedViewsForRefresh(lView) {
12844
+ for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
12845
+ if (!lContainer[HAS_TRANSPLANTED_VIEWS])
12846
+ continue;
12847
+ const movedViews = lContainer[MOVED_VIEWS];
12848
+ ngDevMode && assertDefined(movedViews, 'Transplanted View flags set but missing MOVED_VIEWS');
12849
+ for (let i = 0; i < movedViews.length; i++) {
12850
+ const movedLView = movedViews[i];
12851
+ const insertionLContainer = movedLView[PARENT];
12852
+ ngDevMode && assertLContainer(insertionLContainer);
12853
+ markViewForRefresh(movedLView);
12854
+ }
12855
+ }
12804
12856
  }
12805
-
12806
12857
  /**
12807
- * Compute the static styling (class/style) from `TAttributes`.
12808
- *
12809
- * This function should be called during `firstCreatePass` only.
12858
+ * Refreshes components by entering the component view and processing its bindings, queries, etc.
12810
12859
  *
12811
- * @param tNode The `TNode` into which the styling information should be loaded.
12812
- * @param attrs `TAttributes` containing the styling information.
12813
- * @param writeToHost Where should the resulting static styles be written?
12814
- * - `false` Write to `TNode.stylesWithoutHost` / `TNode.classesWithoutHost`
12815
- * - `true` Write to `TNode.styles` / `TNode.classes`
12860
+ * @param componentHostIdx Element index in LView[] (adjusted for HEADER_OFFSET)
12816
12861
  */
12817
- function computeStaticStyling(tNode, attrs, writeToHost) {
12818
- ngDevMode &&
12819
- assertFirstCreatePass(getTView(), 'Expecting to be called in first template pass only');
12820
- let styles = writeToHost ? tNode.styles : null;
12821
- let classes = writeToHost ? tNode.classes : null;
12822
- let mode = 0;
12823
- if (attrs !== null) {
12824
- for (let i = 0; i < attrs.length; i++) {
12825
- const value = attrs[i];
12826
- if (typeof value === 'number') {
12827
- mode = value;
12828
- }
12829
- else if (mode == 1 /* AttributeMarker.Classes */) {
12830
- classes = concatStringsWithSpace(classes, value);
12831
- }
12832
- else if (mode == 2 /* AttributeMarker.Styles */) {
12833
- const style = value;
12834
- const styleValue = attrs[++i];
12835
- styles = concatStringsWithSpace(styles, style + ': ' + styleValue + ';');
12836
- }
12862
+ function refreshComponent(hostLView, componentHostIdx) {
12863
+ ngDevMode && assertEqual(isCreationMode(hostLView), false, 'Should be run in update mode');
12864
+ const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
12865
+ // Only attached components that are CheckAlways or OnPush and dirty should be refreshed
12866
+ if (viewAttachedToChangeDetector(componentView)) {
12867
+ const tView = componentView[TVIEW];
12868
+ if (componentView[FLAGS] & (16 /* LViewFlags.CheckAlways */ | 64 /* LViewFlags.Dirty */)) {
12869
+ refreshView(tView, componentView, tView.template, componentView[CONTEXT]);
12870
+ }
12871
+ else if (componentView[DESCENDANT_VIEWS_TO_REFRESH] > 0) {
12872
+ // Only attached components that are CheckAlways or OnPush and dirty should be refreshed
12873
+ refreshContainsDirtyView(componentView);
12837
12874
  }
12838
12875
  }
12839
- writeToHost ? tNode.styles = styles : tNode.stylesWithoutHost = styles;
12840
- writeToHost ? tNode.classes = classes : tNode.classesWithoutHost = classes;
12841
12876
  }
12842
-
12843
- function collectNativeNodes(tView, lView, tNode, result, isProjection = false) {
12844
- while (tNode !== null) {
12845
- ngDevMode &&
12846
- assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 16 /* TNodeType.Projection */ | 32 /* TNodeType.Icu */);
12847
- const lNode = lView[tNode.index];
12848
- if (lNode !== null) {
12849
- result.push(unwrapRNode(lNode));
12850
- }
12851
- // A given lNode can represent either a native node or a LContainer (when it is a host of a
12852
- // ViewContainerRef). When we find a LContainer we need to descend into it to collect root nodes
12853
- // from the views in this container.
12854
- if (isLContainer(lNode)) {
12855
- for (let i = CONTAINER_HEADER_OFFSET; i < lNode.length; i++) {
12856
- const lViewInAContainer = lNode[i];
12857
- const lViewFirstChildTNode = lViewInAContainer[TVIEW].firstChild;
12858
- if (lViewFirstChildTNode !== null) {
12859
- collectNativeNodes(lViewInAContainer[TVIEW], lViewInAContainer, lViewFirstChildTNode, result);
12877
+ /**
12878
+ * Refreshes all transplanted views marked with `LViewFlags.RefreshTransplantedView` that are
12879
+ * children or descendants of the given lView.
12880
+ *
12881
+ * @param lView The lView which contains descendant transplanted views that need to be refreshed.
12882
+ */
12883
+ function refreshContainsDirtyView(lView) {
12884
+ for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
12885
+ for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
12886
+ const embeddedLView = lContainer[i];
12887
+ if (viewAttachedToChangeDetector(embeddedLView)) {
12888
+ if (embeddedLView[FLAGS] & 1024 /* LViewFlags.RefreshView */) {
12889
+ const embeddedTView = embeddedLView[TVIEW];
12890
+ ngDevMode && assertDefined(embeddedTView, 'TView must be allocated');
12891
+ refreshView(embeddedTView, embeddedLView, embeddedTView.template, embeddedLView[CONTEXT]);
12892
+ }
12893
+ else if (embeddedLView[DESCENDANT_VIEWS_TO_REFRESH] > 0) {
12894
+ refreshContainsDirtyView(embeddedLView);
12860
12895
  }
12861
- }
12862
- // When an LContainer is created, the anchor (comment) node is:
12863
- // - (1) either reused in case of an ElementContainer (<ng-container>)
12864
- // - (2) or a new comment node is created
12865
- // In the first case, the anchor comment node would be added to the final
12866
- // list by the code above (`result.push(unwrapRNode(lNode))`), but the second
12867
- // case requires extra handling: the anchor node needs to be added to the
12868
- // final list manually. See additional information in the `createAnchorNode`
12869
- // function in the `view_container_ref.ts`.
12870
- //
12871
- // In the first case, the same reference would be stored in the `NATIVE`
12872
- // and `HOST` slots in an LContainer. Otherwise, this is the second case and
12873
- // we should add an element to the final list.
12874
- if (lNode[NATIVE] !== lNode[HOST]) {
12875
- result.push(lNode[NATIVE]);
12876
- }
12877
- }
12878
- const tNodeType = tNode.type;
12879
- if (tNodeType & 8 /* TNodeType.ElementContainer */) {
12880
- collectNativeNodes(tView, lView, tNode.child, result);
12881
- }
12882
- else if (tNodeType & 32 /* TNodeType.Icu */) {
12883
- const nextRNode = icuContainerIterate(tNode, lView);
12884
- let rNode;
12885
- while (rNode = nextRNode()) {
12886
- result.push(rNode);
12887
12896
  }
12888
12897
  }
12889
- else if (tNodeType & 16 /* TNodeType.Projection */) {
12890
- const nodesInSlot = getProjectionNodes(lView, tNode);
12891
- if (Array.isArray(nodesInSlot)) {
12892
- result.push(...nodesInSlot);
12893
- }
12894
- else {
12895
- const parentView = getLViewParent(lView[DECLARATION_COMPONENT_VIEW]);
12896
- ngDevMode && assertParentView(parentView);
12897
- collectNativeNodes(parentView[TVIEW], parentView, nodesInSlot, result, true);
12898
+ }
12899
+ const tView = lView[TVIEW];
12900
+ // Refresh child component views.
12901
+ const components = tView.components;
12902
+ if (components !== null) {
12903
+ for (let i = 0; i < components.length; i++) {
12904
+ const componentView = getComponentLViewByIndex(components[i], lView);
12905
+ // Only attached components that are CheckAlways or OnPush and dirty should be refreshed
12906
+ if (viewAttachedToChangeDetector(componentView) &&
12907
+ componentView[DESCENDANT_VIEWS_TO_REFRESH] > 0) {
12908
+ refreshContainsDirtyView(componentView);
12898
12909
  }
12899
12910
  }
12900
- tNode = isProjection ? tNode.projectionNext : tNode.next;
12901
12911
  }
12902
- return result;
12912
+ }
12913
+ /** Refreshes child components in the current view (update mode). */
12914
+ function refreshChildComponents(hostLView, components) {
12915
+ for (let i = 0; i < components.length; i++) {
12916
+ refreshComponent(hostLView, components[i]);
12917
+ }
12903
12918
  }
12904
12919
 
12905
12920
  class ViewRef {
@@ -14584,18 +14599,6 @@ function ɵɵattributeInterpolateV(attrName, values, sanitizer, namespace) {
14584
14599
  return ɵɵattributeInterpolateV;
14585
14600
  }
14586
14601
 
14587
- /**
14588
- * Synchronously perform change detection on a component (and possibly its sub-components).
14589
- *
14590
- * This function triggers change detection in a synchronous way on a component.
14591
- *
14592
- * @param component The component which the change detection should be performed on.
14593
- */
14594
- function detectChanges(component) {
14595
- const view = getComponentViewByInstance(component);
14596
- detectChangesInternal(view[TVIEW], view, component);
14597
- }
14598
-
14599
14602
  const AT_THIS_LOCATION = '<-- AT THIS LOCATION';
14600
14603
  /**
14601
14604
  * Retrieves a user friendly string for a given TNodeType for use in
@@ -14669,9 +14672,16 @@ function validateSiblingNodeExists(node) {
14669
14672
  /**
14670
14673
  * Validates that a node exists or throws
14671
14674
  */
14672
- function validateNodeExists(node) {
14675
+ function validateNodeExists(node, lView = null, tNode = null) {
14673
14676
  if (!node) {
14674
- throw new RuntimeError(-502 /* RuntimeErrorCode.HYDRATION_MISSING_NODE */, `Hydration expected an element to be present at this location.`);
14677
+ const header = 'During hydration, Angular expected an element to be present at this location.\n\n';
14678
+ let expected = '';
14679
+ let footer = '';
14680
+ if (lView !== null && tNode !== null) {
14681
+ expected = `${describeExpectedDom(lView, tNode, false)}\n\n`;
14682
+ footer = getHydrationErrorFooter();
14683
+ }
14684
+ throw new RuntimeError(-502 /* RuntimeErrorCode.HYDRATION_MISSING_NODE */, header + expected + footer);
14675
14685
  }
14676
14686
  }
14677
14687
  /**
@@ -14728,7 +14738,7 @@ function invalidSkipHydrationHost(rNode) {
14728
14738
  'that doesn\'t act as a component host. Hydration can be ' +
14729
14739
  'skipped only on per-component basis.\n\n';
14730
14740
  const actual = `${describeDomFromNode(rNode)}\n\n`;
14731
- const footer = 'Please move the `ngSkipHydration` attribute to the component host element.';
14741
+ const footer = 'Please move the `ngSkipHydration` attribute to the component host element.\n\n';
14732
14742
  const message = header + actual + footer;
14733
14743
  return new RuntimeError(-504 /* RuntimeErrorCode.INVALID_SKIP_HYDRATION_HOST */, message);
14734
14744
  }
@@ -14913,8 +14923,9 @@ function getHydrationErrorFooter(componentClassName) {
14913
14923
  const componentInfo = componentClassName ? `the "${componentClassName}"` : 'corresponding';
14914
14924
  return `To fix this problem:\n` +
14915
14925
  ` * check ${componentInfo} component for hydration-related issues\n` +
14926
+ ` * check to see if your template has valid HTML structure\n` +
14916
14927
  ` * or skip hydration by adding the \`ngSkipHydration\` attribute ` +
14917
- `to its host node in a template`;
14928
+ `to its host node in a template\n\n`;
14918
14929
  }
14919
14930
  /**
14920
14931
  * An attribute related note for hydration errors
@@ -15274,17 +15285,11 @@ function templateFirstCreatePass(index, tView, lView, templateFn, decls, vars, t
15274
15285
  ngDevMode && assertFirstCreatePass(tView);
15275
15286
  ngDevMode && ngDevMode.firstCreatePass++;
15276
15287
  const tViewConsts = tView.consts;
15277
- let ssrId = null;
15278
- const hydrationInfo = lView[HYDRATION];
15279
- if (hydrationInfo) {
15280
- const noOffsetIndex = index - HEADER_OFFSET;
15281
- ssrId = hydrationInfo.data[TEMPLATES]?.[noOffsetIndex] ?? null;
15282
- }
15283
15288
  // TODO(pk): refactor getOrCreateTNode to have the "create" only version
15284
15289
  const tNode = getOrCreateTNode(tView, index, 4 /* TNodeType.Container */, tagName || null, getConstant(tViewConsts, attrsIndex));
15285
15290
  resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex));
15286
15291
  registerPostOrderHooks(tView, tNode);
15287
- const embeddedTView = tNode.tView = createTView(2 /* TViewType.Embedded */, tNode, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, tView.schemas, tViewConsts, ssrId);
15292
+ const embeddedTView = tNode.tView = createTView(2 /* TViewType.Embedded */, tNode, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, tView.schemas, tViewConsts, null /* ssrId */);
15288
15293
  if (tView.queries !== null) {
15289
15294
  tView.queries.template(tView, tNode);
15290
15295
  embeddedTView.queries = tView.queries.embeddedTView(tNode);
@@ -15351,9 +15356,27 @@ function locateOrCreateContainerAnchorImpl(tView, lView, tNode, index) {
15351
15356
  if (isNodeCreationMode) {
15352
15357
  return createContainerAnchorImpl(tView, lView, tNode, index);
15353
15358
  }
15359
+ const ssrId = hydrationInfo.data[TEMPLATES]?.[index] ?? null;
15360
+ // Apply `ssrId` value to the underlying TView if it was not previously set.
15361
+ //
15362
+ // There might be situations when the same component is present in a template
15363
+ // multiple times and some instances are opted-out of using hydration via
15364
+ // `ngSkipHydration` attribute. In this scenario, at the time a TView is created,
15365
+ // the `ssrId` might be `null` (if the first component is opted-out of hydration).
15366
+ // The code below makes sure that the `ssrId` is applied to the TView if it's still
15367
+ // `null` and verifies we never try to override it with a different value.
15368
+ if (ssrId !== null && tNode.tView !== null) {
15369
+ if (tNode.tView.ssrId === null) {
15370
+ tNode.tView.ssrId = ssrId;
15371
+ }
15372
+ else {
15373
+ ngDevMode &&
15374
+ assertEqual(tNode.tView.ssrId, ssrId, 'Unexpected value of the `ssrId` for this TView');
15375
+ }
15376
+ }
15354
15377
  // Hydration mode, looking up existing elements in DOM.
15355
15378
  const currentRNode = locateNextRNode(hydrationInfo, tView, lView, tNode);
15356
- ngDevMode && validateNodeExists(currentRNode);
15379
+ ngDevMode && validateNodeExists(currentRNode, lView, tNode);
15357
15380
  setSegmentHead(hydrationInfo, index, currentRNode);
15358
15381
  const viewContainerSize = calcSerializedContainerSize(hydrationInfo, index);
15359
15382
  const comment = siblingAfter(viewContainerSize, currentRNode);
@@ -15587,7 +15610,7 @@ function locateOrCreateElementNodeImpl(tView, lView, tNode, renderer, name, inde
15587
15610
  // `<div #vcrTarget>` is represented in the DOM as `<div></div>...<!--container-->`,
15588
15611
  // so while processing a `<div>` instruction, point to the next sibling as a
15589
15612
  // start of a segment.
15590
- ngDevMode && validateNodeExists(native.nextSibling);
15613
+ ngDevMode && validateNodeExists(native.nextSibling, lView, tNode);
15591
15614
  setSegmentHead(hydrationInfo, index, native.nextSibling);
15592
15615
  }
15593
15616
  // Checks if the skip hydration attribute is present during hydration so we know to
@@ -15732,7 +15755,7 @@ function locateOrCreateElementContainerNode(tView, lView, tNode, index) {
15732
15755
  }
15733
15756
  // Hydration mode, looking up existing elements in DOM.
15734
15757
  const currentRNode = locateNextRNode(hydrationInfo, tView, lView, tNode);
15735
- ngDevMode && validateNodeExists(currentRNode);
15758
+ ngDevMode && validateNodeExists(currentRNode, lView, tNode);
15736
15759
  const ngContainerSize = getNgContainerSize(hydrationInfo, index);
15737
15760
  ngDevMode &&
15738
15761
  assertNumber(ngContainerSize, 'Unexpected state: hydrating an <ng-container>, ' +
@@ -26202,9 +26225,7 @@ class TestBedImpl {
26202
26225
  if (!componentDef) {
26203
26226
  throw new Error(`It looks like '${ɵstringify(type)}' has not been compiled.`);
26204
26227
  }
26205
- // TODO: Don't cast as `InjectionToken<boolean>`, proper type is boolean[]
26206
26228
  const noNgZone = this.inject(ComponentFixtureNoNgZone, false);
26207
- // TODO: Don't cast as `InjectionToken<boolean>`, proper type is boolean[]
26208
26229
  const autoDetect = this.inject(ComponentFixtureAutoDetect, false);
26209
26230
  const ngZone = noNgZone ? null : this.inject(NgZone, null);
26210
26231
  const componentFactory = new ɵRender3ComponentFactory(componentDef);