@angular/core 18.0.0-next.1 → 18.0.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/rxjs-interop/src/to_signal.mjs +2 -2
- package/esm2022/src/application/application_init.mjs +2 -2
- package/esm2022/src/application/application_ref.mjs +10 -15
- package/esm2022/src/application/application_tokens.mjs +3 -3
- package/esm2022/src/authoring/input/input.mjs +37 -14
- package/esm2022/src/authoring/input/input_signal.mjs +1 -1
- package/esm2022/src/authoring/input/input_signal_node.mjs +1 -1
- package/esm2022/src/authoring/input/input_type_checking.mjs +1 -1
- package/esm2022/src/authoring/model/model.mjs +35 -15
- package/esm2022/src/authoring/output/output.mjs +32 -10
- package/esm2022/src/authoring/queries.mjs +11 -1
- package/esm2022/src/change_detection/scheduling/ng_zone_scheduling.mjs +38 -4
- package/esm2022/src/change_detection/scheduling/zoneless_scheduling.mjs +5 -1
- package/esm2022/src/change_detection/scheduling/zoneless_scheduling_impl.mjs +91 -22
- package/esm2022/src/compiler/compiler_facade_interface.mjs +1 -1
- package/esm2022/src/core_private_export.mjs +4 -4
- package/esm2022/src/core_render3_private_export.mjs +2 -2
- package/esm2022/src/defer/instructions.mjs +23 -9
- package/esm2022/src/di/interface/provider.mjs +1 -1
- package/esm2022/src/di/metadata.mjs +1 -1
- package/esm2022/src/hydration/annotate.mjs +35 -49
- package/esm2022/src/hydration/api.mjs +36 -13
- package/esm2022/src/hydration/cleanup.mjs +4 -18
- package/esm2022/src/hydration/i18n.mjs +378 -0
- package/esm2022/src/hydration/interfaces.mjs +2 -1
- package/esm2022/src/hydration/node_lookup_utils.mjs +24 -10
- package/esm2022/src/hydration/utils.mjs +61 -2
- package/esm2022/src/linker/component_factory.mjs +1 -1
- package/esm2022/src/linker/view_container_ref.mjs +2 -2
- package/esm2022/src/linker/view_ref.mjs +4 -4
- package/esm2022/src/metadata/directives.mjs +1 -1
- package/esm2022/src/metadata/ng_module.mjs +1 -1
- package/esm2022/src/platform/platform_ref.mjs +3 -2
- package/esm2022/src/render3/component_ref.mjs +1 -1
- package/esm2022/src/render3/debug/framework_injector_profiler.mjs +2 -2
- package/esm2022/src/render3/i18n/i18n_apply.mjs +28 -6
- package/esm2022/src/render3/i18n/i18n_parse.mjs +2 -5
- package/esm2022/src/render3/i18n/i18n_util.mjs +6 -1
- package/esm2022/src/render3/instructions/control_flow.mjs +24 -15
- package/esm2022/src/render3/instructions/i18n.mjs +3 -1
- package/esm2022/src/render3/instructions/projection.mjs +35 -9
- package/esm2022/src/render3/instructions/template.mjs +45 -24
- package/esm2022/src/render3/jit/partial.mjs +13 -2
- package/esm2022/src/render3/util/injector_discovery_utils.mjs +13 -9
- package/esm2022/src/render3/util/view_utils.mjs +4 -14
- package/esm2022/src/util/callback_scheduler.mjs +25 -30
- package/esm2022/src/version.mjs +1 -1
- package/esm2022/src/zone/ng_zone.mjs +3 -3
- package/esm2022/testing/src/component_fixture.mjs +24 -59
- package/esm2022/testing/src/fake_async.mjs +6 -1
- package/esm2022/testing/src/logger.mjs +3 -3
- package/esm2022/testing/src/private_export.mjs +9 -0
- package/esm2022/testing/src/test_bed.mjs +4 -4
- package/esm2022/testing/src/test_hooks.mjs +3 -3
- package/esm2022/testing/src/testing.mjs +3 -2
- package/fesm2022/core.mjs +1499 -823
- package/fesm2022/core.mjs.map +1 -1
- package/fesm2022/primitives/signals.mjs +1 -1
- package/fesm2022/rxjs-interop.mjs +2 -2
- package/fesm2022/rxjs-interop.mjs.map +1 -1
- package/fesm2022/testing.mjs +64 -94
- package/fesm2022/testing.mjs.map +1 -1
- package/index.d.ts +269 -149
- package/package.json +1 -1
- package/primitives/signals/index.d.ts +1 -1
- package/rxjs-interop/index.d.ts +2 -2
- package/schematics/migrations/block-template-entities/bundle.js +313 -255
- package/schematics/migrations/block-template-entities/bundle.js.map +3 -3
- package/schematics/migrations/compiler-options/bundle.js +13 -13
- package/schematics/migrations/invalid-two-way-bindings/bundle.js +316 -257
- package/schematics/migrations/invalid-two-way-bindings/bundle.js.map +3 -3
- package/schematics/migrations/transfer-state/bundle.js +13 -13
- package/schematics/ng-generate/control-flow-migration/bundle.js +349 -276
- package/schematics/ng-generate/control-flow-migration/bundle.js.map +3 -3
- package/schematics/ng-generate/standalone-migration/bundle.js +1811 -1330
- package/schematics/ng-generate/standalone-migration/bundle.js.map +4 -4
- package/testing/index.d.ts +10 -3
- package/esm2022/src/change_detection/flags.mjs +0 -17
package/fesm2022/core.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v18.0.0-next.
|
|
2
|
+
* @license Angular v18.0.0-next.3
|
|
3
3
|
* (c) 2010-2022 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -4152,16 +4152,6 @@ const profiler = function (event, instance, hookOrListener) {
|
|
|
4152
4152
|
const SVG_NAMESPACE = 'svg';
|
|
4153
4153
|
const MATH_ML_NAMESPACE = 'math';
|
|
4154
4154
|
|
|
4155
|
-
// TODO(atscott): Remove prior to v18 release. Keeping this around in case anyone internally needs
|
|
4156
|
-
// to opt out temporarily.
|
|
4157
|
-
let _ensureDirtyViewsAreAlwaysReachable = true;
|
|
4158
|
-
function getEnsureDirtyViewsAreAlwaysReachable() {
|
|
4159
|
-
return _ensureDirtyViewsAreAlwaysReachable;
|
|
4160
|
-
}
|
|
4161
|
-
function setEnsureDirtyViewsAreAlwaysReachable(v) {
|
|
4162
|
-
_ensureDirtyViewsAreAlwaysReachable = v;
|
|
4163
|
-
}
|
|
4164
|
-
|
|
4165
4155
|
/**
|
|
4166
4156
|
* For efficiency reasons we often put several different data types (`RNode`, `LView`, `LContainer`)
|
|
4167
4157
|
* in same location in `LView`. This is because we don't want to pre-allocate space for it
|
|
@@ -4329,21 +4319,12 @@ function requiresRefreshOrTraversal(lView) {
|
|
|
4329
4319
|
*/
|
|
4330
4320
|
function updateAncestorTraversalFlagsOnAttach(lView) {
|
|
4331
4321
|
lView[ENVIRONMENT].changeDetectionScheduler?.notify(1 /* NotificationType.AfterRenderHooks */);
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4322
|
+
if (lView[FLAGS] & 64 /* LViewFlags.Dirty */) {
|
|
4323
|
+
lView[FLAGS] |= 1024 /* LViewFlags.RefreshView */;
|
|
4324
|
+
}
|
|
4335
4325
|
if (requiresRefreshOrTraversal(lView)) {
|
|
4336
4326
|
markAncestorsForTraversal(lView);
|
|
4337
4327
|
}
|
|
4338
|
-
else if (lView[FLAGS] & 64 /* LViewFlags.Dirty */) {
|
|
4339
|
-
if (getEnsureDirtyViewsAreAlwaysReachable()) {
|
|
4340
|
-
lView[FLAGS] |= 1024 /* LViewFlags.RefreshView */;
|
|
4341
|
-
markAncestorsForTraversal(lView);
|
|
4342
|
-
}
|
|
4343
|
-
else {
|
|
4344
|
-
lView[ENVIRONMENT].changeDetectionScheduler?.notify();
|
|
4345
|
-
}
|
|
4346
|
-
}
|
|
4347
4328
|
}
|
|
4348
4329
|
/**
|
|
4349
4330
|
* Ensures views above the given `lView` are traversed during change detection even when they are
|
|
@@ -6677,25 +6658,47 @@ function getOutputDestroyRef(ref) {
|
|
|
6677
6658
|
}
|
|
6678
6659
|
|
|
6679
6660
|
/**
|
|
6680
|
-
* The `output` function allows declaration of outputs in
|
|
6681
|
-
* components.
|
|
6661
|
+
* The `output` function allows declaration of Angular outputs in
|
|
6662
|
+
* directives and components.
|
|
6663
|
+
*
|
|
6664
|
+
* You can use outputs to emit values to parent directives and component.
|
|
6665
|
+
* Parents can subscribe to changes via:
|
|
6682
6666
|
*
|
|
6683
|
-
*
|
|
6684
|
-
*
|
|
6667
|
+
* - template event bindings. For example, `(myOutput)="doSomething($event)"`
|
|
6668
|
+
* - programmatic subscription by using `OutputRef#subscribe`.
|
|
6685
6669
|
*
|
|
6686
6670
|
* @usageNotes
|
|
6687
|
-
*
|
|
6688
|
-
*
|
|
6671
|
+
*
|
|
6672
|
+
* To use `output()`, import the function from `@angular/core`.
|
|
6673
|
+
*
|
|
6674
|
+
* ```
|
|
6675
|
+
* import {output} from '@angular/core`;
|
|
6676
|
+
* ```
|
|
6677
|
+
*
|
|
6678
|
+
* Inside your component, introduce a new class member and initialize
|
|
6679
|
+
* it with a call to `output`.
|
|
6689
6680
|
*
|
|
6690
6681
|
* ```ts
|
|
6691
|
-
* @Directive({
|
|
6682
|
+
* @Directive({
|
|
6683
|
+
* ...
|
|
6684
|
+
* })
|
|
6692
6685
|
* export class MyDir {
|
|
6693
|
-
* nameChange = output<string>();
|
|
6694
|
-
* onClick
|
|
6686
|
+
* nameChange = output<string>(); // OutputEmitterRef<string>
|
|
6687
|
+
* onClick = output(); // OutputEmitterRef<void>
|
|
6688
|
+
* }
|
|
6689
|
+
* ```
|
|
6690
|
+
*
|
|
6691
|
+
* You can emit values to consumers of your directive, by using
|
|
6692
|
+
* the `emit` method from `OutputEmitterRef`.
|
|
6693
|
+
*
|
|
6694
|
+
* ```ts
|
|
6695
|
+
* updateName(newName: string): void {
|
|
6696
|
+
* this.nameChange.emit(newName);
|
|
6695
6697
|
* }
|
|
6696
6698
|
* ```
|
|
6697
6699
|
*
|
|
6698
6700
|
* @developerPreview
|
|
6701
|
+
* @initializerApiFunction {"showTypesInSignaturePreview": true}
|
|
6699
6702
|
*/
|
|
6700
6703
|
function output(opts) {
|
|
6701
6704
|
ngDevMode && assertInInjectionContext(output);
|
|
@@ -6711,29 +6714,52 @@ function inputRequiredFunction(opts) {
|
|
|
6711
6714
|
return createInputSignal(REQUIRED_UNSET_VALUE, opts);
|
|
6712
6715
|
}
|
|
6713
6716
|
/**
|
|
6714
|
-
* The `input` function allows declaration of inputs in directives
|
|
6715
|
-
* components.
|
|
6717
|
+
* The `input` function allows declaration of Angular inputs in directives
|
|
6718
|
+
* and components.
|
|
6719
|
+
*
|
|
6720
|
+
* There are two variants of inputs that can be declared:
|
|
6721
|
+
*
|
|
6722
|
+
* 1. **Optional inputs** with an initial value.
|
|
6723
|
+
* 2. **Required inputs** that consumers need to set.
|
|
6716
6724
|
*
|
|
6717
|
-
*
|
|
6718
|
-
*
|
|
6725
|
+
* By default, the `input` function will declare optional inputs that
|
|
6726
|
+
* always have an initial value. Required inputs can be declared
|
|
6727
|
+
* using the `input.required()` function.
|
|
6719
6728
|
*
|
|
6720
|
-
*
|
|
6721
|
-
*
|
|
6729
|
+
* Inputs are signals. The values of an input are exposed as a `Signal`.
|
|
6730
|
+
* The signal always holds the latest value of the input that is bound
|
|
6731
|
+
* from the parent.
|
|
6722
6732
|
*
|
|
6723
6733
|
* @usageNotes
|
|
6724
|
-
*
|
|
6725
|
-
*
|
|
6734
|
+
* To use signal-based inputs, import `input` from `@angular/core`.
|
|
6735
|
+
*
|
|
6736
|
+
* ```
|
|
6737
|
+
* import {input} from '@angular/core`;
|
|
6738
|
+
* ```
|
|
6739
|
+
*
|
|
6740
|
+
* Inside your component, introduce a new class member and initialize
|
|
6741
|
+
* it with a call to `input` or `input.required`.
|
|
6726
6742
|
*
|
|
6727
6743
|
* ```ts
|
|
6728
|
-
* @
|
|
6729
|
-
*
|
|
6730
|
-
*
|
|
6731
|
-
*
|
|
6732
|
-
*
|
|
6744
|
+
* @Component({
|
|
6745
|
+
* ...
|
|
6746
|
+
* })
|
|
6747
|
+
* export class UserProfileComponent {
|
|
6748
|
+
* firstName = input<string>(); // Signal<string|undefined>
|
|
6749
|
+
* lastName = input.required<string>(); // Signal<string>
|
|
6750
|
+
* age = input(0) // Signal<number>
|
|
6733
6751
|
* }
|
|
6734
6752
|
* ```
|
|
6735
6753
|
*
|
|
6754
|
+
* Inside your component template, you can display values of the inputs
|
|
6755
|
+
* by calling the signal.
|
|
6756
|
+
*
|
|
6757
|
+
* ```html
|
|
6758
|
+
* <span>{{firstName()}}</span>
|
|
6759
|
+
* ```
|
|
6760
|
+
*
|
|
6736
6761
|
* @developerPreview
|
|
6762
|
+
* @initializerApiFunction
|
|
6737
6763
|
*/
|
|
6738
6764
|
const input = (() => {
|
|
6739
6765
|
// Note: This may be considered a side-effect, but nothing will depend on
|
|
@@ -7938,7 +7964,7 @@ function getDocument() {
|
|
|
7938
7964
|
}
|
|
7939
7965
|
|
|
7940
7966
|
/**
|
|
7941
|
-
* A
|
|
7967
|
+
* A DI token representing a string ID, used
|
|
7942
7968
|
* primarily for prefixing application attributes and CSS styles when
|
|
7943
7969
|
* {@link ViewEncapsulation#Emulated} is being used.
|
|
7944
7970
|
*
|
|
@@ -7987,7 +8013,7 @@ const PLATFORM_ID = new InjectionToken(ngDevMode ? 'Platform ID' : '', {
|
|
|
7987
8013
|
factory: () => 'unknown', // set a default platform name, when none set explicitly
|
|
7988
8014
|
});
|
|
7989
8015
|
/**
|
|
7990
|
-
* A
|
|
8016
|
+
* A DI token that indicates the root directory of
|
|
7991
8017
|
* the application
|
|
7992
8018
|
* @publicApi
|
|
7993
8019
|
* @deprecated
|
|
@@ -8201,6 +8227,7 @@ const NUM_ROOT_NODES = 'r';
|
|
|
8201
8227
|
const TEMPLATE_ID = 'i'; // as it's also an "id"
|
|
8202
8228
|
const NODES = 'n';
|
|
8203
8229
|
const DISCONNECTED_NODES = 'd';
|
|
8230
|
+
const I18N_DATA = 'l';
|
|
8204
8231
|
|
|
8205
8232
|
/**
|
|
8206
8233
|
* The name of the key used in the TransferState collection,
|
|
@@ -8469,6 +8496,9 @@ function getNgContainerSize(hydrationInfo, index) {
|
|
|
8469
8496
|
}
|
|
8470
8497
|
return size;
|
|
8471
8498
|
}
|
|
8499
|
+
function isSerializedElementContainer(hydrationInfo, index) {
|
|
8500
|
+
return hydrationInfo.data[ELEMENT_CONTAINERS]?.[index] !== undefined;
|
|
8501
|
+
}
|
|
8472
8502
|
function getSerializedContainerViews(hydrationInfo, index) {
|
|
8473
8503
|
return hydrationInfo.data[CONTAINERS]?.[index] ?? null;
|
|
8474
8504
|
}
|
|
@@ -8484,6 +8514,18 @@ function calcSerializedContainerSize(hydrationInfo, index) {
|
|
|
8484
8514
|
}
|
|
8485
8515
|
return numNodes;
|
|
8486
8516
|
}
|
|
8517
|
+
/**
|
|
8518
|
+
* Attempt to initialize the `disconnectedNodes` field of the given
|
|
8519
|
+
* `DehydratedView`. Returns the initialized value.
|
|
8520
|
+
*/
|
|
8521
|
+
function initDisconnectedNodes(hydrationInfo) {
|
|
8522
|
+
// Check if we are processing disconnected info for the first time.
|
|
8523
|
+
if (typeof hydrationInfo.disconnectedNodes === 'undefined') {
|
|
8524
|
+
const nodeIds = hydrationInfo.data[DISCONNECTED_NODES];
|
|
8525
|
+
hydrationInfo.disconnectedNodes = nodeIds ? new Set(nodeIds) : null;
|
|
8526
|
+
}
|
|
8527
|
+
return hydrationInfo.disconnectedNodes;
|
|
8528
|
+
}
|
|
8487
8529
|
/**
|
|
8488
8530
|
* Checks whether a node is annotated as "disconnected", i.e. not present
|
|
8489
8531
|
* in the DOM at serialization time. We should not attempt hydration for
|
|
@@ -8495,7 +8537,51 @@ function isDisconnectedNode$1(hydrationInfo, index) {
|
|
|
8495
8537
|
const nodeIds = hydrationInfo.data[DISCONNECTED_NODES];
|
|
8496
8538
|
hydrationInfo.disconnectedNodes = nodeIds ? new Set(nodeIds) : null;
|
|
8497
8539
|
}
|
|
8498
|
-
return !!hydrationInfo
|
|
8540
|
+
return !!initDisconnectedNodes(hydrationInfo)?.has(index);
|
|
8541
|
+
}
|
|
8542
|
+
/**
|
|
8543
|
+
* Helper function to prepare text nodes for serialization by ensuring
|
|
8544
|
+
* that seperate logical text blocks in the DOM remain separate after
|
|
8545
|
+
* serialization.
|
|
8546
|
+
*/
|
|
8547
|
+
function processTextNodeBeforeSerialization(context, node) {
|
|
8548
|
+
// Handle cases where text nodes can be lost after DOM serialization:
|
|
8549
|
+
// 1. When there is an *empty text node* in DOM: in this case, this
|
|
8550
|
+
// node would not make it into the serialized string and as a result,
|
|
8551
|
+
// this node wouldn't be created in a browser. This would result in
|
|
8552
|
+
// a mismatch during the hydration, where the runtime logic would expect
|
|
8553
|
+
// a text node to be present in live DOM, but no text node would exist.
|
|
8554
|
+
// Example: `<span>{{ name }}</span>` when the `name` is an empty string.
|
|
8555
|
+
// This would result in `<span></span>` string after serialization and
|
|
8556
|
+
// in a browser only the `span` element would be created. To resolve that,
|
|
8557
|
+
// an extra comment node is appended in place of an empty text node and
|
|
8558
|
+
// that special comment node is replaced with an empty text node *before*
|
|
8559
|
+
// hydration.
|
|
8560
|
+
// 2. When there are 2 consecutive text nodes present in the DOM.
|
|
8561
|
+
// Example: `<div>Hello <ng-container *ngIf="true">world</ng-container></div>`.
|
|
8562
|
+
// In this scenario, the live DOM would look like this:
|
|
8563
|
+
// <div>#text('Hello ') #text('world') #comment('container')</div>
|
|
8564
|
+
// Serialized string would look like this: `<div>Hello world<!--container--></div>`.
|
|
8565
|
+
// The live DOM in a browser after that would be:
|
|
8566
|
+
// <div>#text('Hello world') #comment('container')</div>
|
|
8567
|
+
// Notice how 2 text nodes are now "merged" into one. This would cause hydration
|
|
8568
|
+
// logic to fail, since it'd expect 2 text nodes being present, not one.
|
|
8569
|
+
// To fix this, we insert a special comment node in between those text nodes, so
|
|
8570
|
+
// serialized representation is: `<div>Hello <!--ngtns-->world<!--container--></div>`.
|
|
8571
|
+
// This forces browser to create 2 text nodes separated by a comment node.
|
|
8572
|
+
// Before running a hydration process, this special comment node is removed, so the
|
|
8573
|
+
// live DOM has exactly the same state as it was before serialization.
|
|
8574
|
+
// Collect this node as required special annotation only when its
|
|
8575
|
+
// contents is empty. Otherwise, such text node would be present on
|
|
8576
|
+
// the client after server-side rendering and no special handling needed.
|
|
8577
|
+
const el = node;
|
|
8578
|
+
const corruptedTextNodes = context.corruptedTextNodes;
|
|
8579
|
+
if (el.textContent === '') {
|
|
8580
|
+
corruptedTextNodes.set(el, "ngetn" /* TextNodeMarker.EmptyNode */);
|
|
8581
|
+
}
|
|
8582
|
+
else if (el.nextSibling?.nodeType === Node.TEXT_NODE) {
|
|
8583
|
+
corruptedTextNodes.set(el, "ngtns" /* TextNodeMarker.Separator */);
|
|
8584
|
+
}
|
|
8499
8585
|
}
|
|
8500
8586
|
|
|
8501
8587
|
/**
|
|
@@ -12700,7 +12786,7 @@ function detectChangesInViewWhileDirty(lView, mode) {
|
|
|
12700
12786
|
const lastIsRefreshingViewsValue = isRefreshingViews();
|
|
12701
12787
|
try {
|
|
12702
12788
|
setIsRefreshingViews(true);
|
|
12703
|
-
detectChangesInView
|
|
12789
|
+
detectChangesInView(lView, mode);
|
|
12704
12790
|
let retries = 0;
|
|
12705
12791
|
// If after running change detection, this view still needs to be refreshed or there are
|
|
12706
12792
|
// descendants views that need to be refreshed due to re-dirtying during the change detection
|
|
@@ -12716,7 +12802,7 @@ function detectChangesInViewWhileDirty(lView, mode) {
|
|
|
12716
12802
|
retries++;
|
|
12717
12803
|
// Even if this view is detached, we still detect changes in targeted mode because this was
|
|
12718
12804
|
// the root of the change detection run.
|
|
12719
|
-
detectChangesInView
|
|
12805
|
+
detectChangesInView(lView, 1 /* ChangeDetectionMode.Targeted */);
|
|
12720
12806
|
}
|
|
12721
12807
|
}
|
|
12722
12808
|
finally {
|
|
@@ -12951,7 +13037,7 @@ function detectChangesInViewIfAttached(lView, mode) {
|
|
|
12951
13037
|
if (!viewAttachedToChangeDetector(lView)) {
|
|
12952
13038
|
return;
|
|
12953
13039
|
}
|
|
12954
|
-
detectChangesInView
|
|
13040
|
+
detectChangesInView(lView, mode);
|
|
12955
13041
|
}
|
|
12956
13042
|
/**
|
|
12957
13043
|
* Visits a view as part of change detection traversal.
|
|
@@ -12963,7 +13049,7 @@ function detectChangesInViewIfAttached(lView, mode) {
|
|
|
12963
13049
|
* The view is not refreshed, but descendants are traversed in `ChangeDetectionMode.Targeted` if the
|
|
12964
13050
|
* view HasChildViewsToRefresh flag is set.
|
|
12965
13051
|
*/
|
|
12966
|
-
function detectChangesInView
|
|
13052
|
+
function detectChangesInView(lView, mode) {
|
|
12967
13053
|
const isInCheckNoChangesPass = ngDevMode && isInCheckNoChangesMode();
|
|
12968
13054
|
const tView = lView[TVIEW];
|
|
12969
13055
|
const flags = lView[FLAGS];
|
|
@@ -13774,109 +13860,245 @@ function shorten(input, maxLength = 50) {
|
|
|
13774
13860
|
}
|
|
13775
13861
|
|
|
13776
13862
|
/**
|
|
13777
|
-
*
|
|
13778
|
-
*
|
|
13779
|
-
*
|
|
13863
|
+
* Find a node in front of which `currentTNode` should be inserted (takes i18n into account).
|
|
13864
|
+
*
|
|
13865
|
+
* This method determines the `RNode` in front of which we should insert the `currentRNode`. This
|
|
13866
|
+
* takes `TNode.insertBeforeIndex` into account.
|
|
13867
|
+
*
|
|
13868
|
+
* @param parentTNode parent `TNode`
|
|
13869
|
+
* @param currentTNode current `TNode` (The node which we would like to insert into the DOM)
|
|
13870
|
+
* @param lView current `LView`
|
|
13780
13871
|
*/
|
|
13781
|
-
function
|
|
13782
|
-
const
|
|
13783
|
-
const
|
|
13784
|
-
|
|
13785
|
-
|
|
13786
|
-
|
|
13787
|
-
|
|
13872
|
+
function getInsertInFrontOfRNodeWithI18n(parentTNode, currentTNode, lView) {
|
|
13873
|
+
const tNodeInsertBeforeIndex = currentTNode.insertBeforeIndex;
|
|
13874
|
+
const insertBeforeIndex = Array.isArray(tNodeInsertBeforeIndex) ? tNodeInsertBeforeIndex[0] : tNodeInsertBeforeIndex;
|
|
13875
|
+
if (insertBeforeIndex === null) {
|
|
13876
|
+
return getInsertInFrontOfRNodeWithNoI18n(parentTNode, currentTNode, lView);
|
|
13877
|
+
}
|
|
13878
|
+
else {
|
|
13879
|
+
ngDevMode && assertIndexInRange(lView, insertBeforeIndex);
|
|
13880
|
+
return unwrapRNode(lView[insertBeforeIndex]);
|
|
13788
13881
|
}
|
|
13789
|
-
// Reset the value to an empty array to indicate that no
|
|
13790
|
-
// further processing of dehydrated views is needed for
|
|
13791
|
-
// this view container (i.e. do not trigger the lookup process
|
|
13792
|
-
// once again in case a `ViewContainerRef` is created later).
|
|
13793
|
-
lContainer[DEHYDRATED_VIEWS] = EMPTY_ARRAY;
|
|
13794
13882
|
}
|
|
13795
13883
|
/**
|
|
13796
|
-
*
|
|
13884
|
+
* Process `TNode.insertBeforeIndex` by adding i18n text nodes.
|
|
13885
|
+
*
|
|
13886
|
+
* See `TNode.insertBeforeIndex`
|
|
13797
13887
|
*/
|
|
13798
|
-
function
|
|
13799
|
-
|
|
13800
|
-
|
|
13801
|
-
|
|
13802
|
-
|
|
13803
|
-
|
|
13804
|
-
|
|
13805
|
-
|
|
13806
|
-
|
|
13807
|
-
|
|
13808
|
-
|
|
13888
|
+
function processI18nInsertBefore(renderer, childTNode, lView, childRNode, parentRElement) {
|
|
13889
|
+
const tNodeInsertBeforeIndex = childTNode.insertBeforeIndex;
|
|
13890
|
+
if (Array.isArray(tNodeInsertBeforeIndex)) {
|
|
13891
|
+
// An array indicates that there are i18n nodes that need to be added as children of this
|
|
13892
|
+
// `childRNode`. These i18n nodes were created before this `childRNode` was available and so
|
|
13893
|
+
// only now can be added. The first element of the array is the normal index where we should
|
|
13894
|
+
// insert the `childRNode`. Additional elements are the extra nodes to be added as children of
|
|
13895
|
+
// `childRNode`.
|
|
13896
|
+
ngDevMode && assertDomNode(childRNode);
|
|
13897
|
+
let i18nParent = childRNode;
|
|
13898
|
+
let anchorRNode = null;
|
|
13899
|
+
if (!(childTNode.type & 3 /* TNodeType.AnyRNode */)) {
|
|
13900
|
+
anchorRNode = i18nParent;
|
|
13901
|
+
i18nParent = parentRElement;
|
|
13902
|
+
}
|
|
13903
|
+
if (i18nParent !== null && childTNode.componentOffset === -1) {
|
|
13904
|
+
for (let i = 1; i < tNodeInsertBeforeIndex.length; i++) {
|
|
13905
|
+
// No need to `unwrapRNode` because all of the indexes point to i18n text nodes.
|
|
13906
|
+
// see `assertDomNode` below.
|
|
13907
|
+
const i18nChild = lView[tNodeInsertBeforeIndex[i]];
|
|
13908
|
+
nativeInsertBefore(renderer, i18nParent, i18nChild, anchorRNode, false);
|
|
13909
|
+
}
|
|
13809
13910
|
}
|
|
13810
13911
|
}
|
|
13811
13912
|
}
|
|
13913
|
+
|
|
13812
13914
|
/**
|
|
13813
|
-
*
|
|
13814
|
-
*
|
|
13915
|
+
* Add `tNode` to `previousTNodes` list and update relevant `TNode`s in `previousTNodes` list
|
|
13916
|
+
* `tNode.insertBeforeIndex`.
|
|
13917
|
+
*
|
|
13918
|
+
* Things to keep in mind:
|
|
13919
|
+
* 1. All i18n text nodes are encoded as `TNodeType.Element` and are created eagerly by the
|
|
13920
|
+
* `ɵɵi18nStart` instruction.
|
|
13921
|
+
* 2. All `TNodeType.Placeholder` `TNodes` are elements which will be created later by
|
|
13922
|
+
* `ɵɵelementStart` instruction.
|
|
13923
|
+
* 3. `ɵɵelementStart` instruction will create `TNode`s in the ascending `TNode.index` order. (So a
|
|
13924
|
+
* smaller index `TNode` is guaranteed to be created before a larger one)
|
|
13925
|
+
*
|
|
13926
|
+
* We use the above three invariants to determine `TNode.insertBeforeIndex`.
|
|
13927
|
+
*
|
|
13928
|
+
* In an ideal world `TNode.insertBeforeIndex` would always be `TNode.next.index`. However,
|
|
13929
|
+
* this will not work because `TNode.next.index` may be larger than `TNode.index` which means that
|
|
13930
|
+
* the next node is not yet created and therefore we can't insert in front of it.
|
|
13931
|
+
*
|
|
13932
|
+
* Rule1: `TNode.insertBeforeIndex = null` if `TNode.next === null` (Initial condition, as we don't
|
|
13933
|
+
* know if there will be further `TNode`s inserted after.)
|
|
13934
|
+
* Rule2: If `previousTNode` is created after the `tNode` being inserted, then
|
|
13935
|
+
* `previousTNode.insertBeforeNode = tNode.index` (So when a new `tNode` is added we check
|
|
13936
|
+
* previous to see if we can update its `insertBeforeTNode`)
|
|
13937
|
+
*
|
|
13938
|
+
* See `TNode.insertBeforeIndex` for more context.
|
|
13939
|
+
*
|
|
13940
|
+
* @param previousTNodes A list of previous TNodes so that we can easily traverse `TNode`s in
|
|
13941
|
+
* reverse order. (If `TNode` would have `previous` this would not be necessary.)
|
|
13942
|
+
* @param newTNode A TNode to add to the `previousTNodes` list.
|
|
13815
13943
|
*/
|
|
13816
|
-
function
|
|
13817
|
-
|
|
13818
|
-
|
|
13819
|
-
|
|
13944
|
+
function addTNodeAndUpdateInsertBeforeIndex(previousTNodes, newTNode) {
|
|
13945
|
+
// Start with Rule1
|
|
13946
|
+
ngDevMode &&
|
|
13947
|
+
assertEqual(newTNode.insertBeforeIndex, null, 'We expect that insertBeforeIndex is not set');
|
|
13948
|
+
previousTNodes.push(newTNode);
|
|
13949
|
+
if (previousTNodes.length > 1) {
|
|
13950
|
+
for (let i = previousTNodes.length - 2; i >= 0; i--) {
|
|
13951
|
+
const existingTNode = previousTNodes[i];
|
|
13952
|
+
// Text nodes are created eagerly and so they don't need their `indexBeforeIndex` updated.
|
|
13953
|
+
// It is safe to ignore them.
|
|
13954
|
+
if (!isI18nText(existingTNode)) {
|
|
13955
|
+
if (isNewTNodeCreatedBefore(existingTNode, newTNode) &&
|
|
13956
|
+
getInsertBeforeIndex(existingTNode) === null) {
|
|
13957
|
+
// If it was created before us in time, (and it does not yet have `insertBeforeIndex`)
|
|
13958
|
+
// then add the `insertBeforeIndex`.
|
|
13959
|
+
setInsertBeforeIndex(existingTNode, newTNode.index);
|
|
13960
|
+
}
|
|
13961
|
+
}
|
|
13962
|
+
}
|
|
13820
13963
|
}
|
|
13821
13964
|
}
|
|
13965
|
+
function isI18nText(tNode) {
|
|
13966
|
+
return !(tNode.type & 64 /* TNodeType.Placeholder */);
|
|
13967
|
+
}
|
|
13968
|
+
function isNewTNodeCreatedBefore(existingTNode, newTNode) {
|
|
13969
|
+
return isI18nText(newTNode) || existingTNode.index > newTNode.index;
|
|
13970
|
+
}
|
|
13971
|
+
function getInsertBeforeIndex(tNode) {
|
|
13972
|
+
const index = tNode.insertBeforeIndex;
|
|
13973
|
+
return Array.isArray(index) ? index[0] : index;
|
|
13974
|
+
}
|
|
13975
|
+
function setInsertBeforeIndex(tNode, value) {
|
|
13976
|
+
const index = tNode.insertBeforeIndex;
|
|
13977
|
+
if (Array.isArray(index)) {
|
|
13978
|
+
// Array is stored if we have to insert child nodes. See `TNode.insertBeforeIndex`
|
|
13979
|
+
index[0] = value;
|
|
13980
|
+
}
|
|
13981
|
+
else {
|
|
13982
|
+
setI18nHandling(getInsertInFrontOfRNodeWithI18n, processI18nInsertBefore);
|
|
13983
|
+
tNode.insertBeforeIndex = value;
|
|
13984
|
+
}
|
|
13985
|
+
}
|
|
13986
|
+
|
|
13822
13987
|
/**
|
|
13823
|
-
*
|
|
13824
|
-
*
|
|
13825
|
-
*
|
|
13988
|
+
* Retrieve `TIcu` at a given `index`.
|
|
13989
|
+
*
|
|
13990
|
+
* The `TIcu` can be stored either directly (if it is nested ICU) OR
|
|
13991
|
+
* it is stored inside tho `TIcuContainer` if it is top level ICU.
|
|
13992
|
+
*
|
|
13993
|
+
* The reason for this is that the top level ICU need a `TNode` so that they are part of the render
|
|
13994
|
+
* tree, but nested ICU's have no TNode, because we don't know ahead of time if the nested ICU is
|
|
13995
|
+
* expressed (parent ICU may have selected a case which does not contain it.)
|
|
13996
|
+
*
|
|
13997
|
+
* @param tView Current `TView`.
|
|
13998
|
+
* @param index Index where the value should be read from.
|
|
13826
13999
|
*/
|
|
13827
|
-
function
|
|
13828
|
-
const
|
|
13829
|
-
if (
|
|
13830
|
-
|
|
13831
|
-
|
|
13832
|
-
|
|
13833
|
-
|
|
13834
|
-
lView[HYDRATION].i18nNodes = undefined;
|
|
14000
|
+
function getTIcu(tView, index) {
|
|
14001
|
+
const value = tView.data[index];
|
|
14002
|
+
if (value === null || typeof value === 'string')
|
|
14003
|
+
return null;
|
|
14004
|
+
if (ngDevMode &&
|
|
14005
|
+
!(value.hasOwnProperty('tView') || value.hasOwnProperty('currentCaseLViewIndex'))) {
|
|
14006
|
+
throwError('We expect to get \'null\'|\'TIcu\'|\'TIcuContainer\', but got: ' + value);
|
|
13835
14007
|
}
|
|
14008
|
+
// Here the `value.hasOwnProperty('currentCaseLViewIndex')` is a polymorphic read as it can be
|
|
14009
|
+
// either TIcu or TIcuContainerNode. This is not ideal, but we still think it is OK because it
|
|
14010
|
+
// will be just two cases which fits into the browser inline cache (inline cache can take up to
|
|
14011
|
+
// 4)
|
|
14012
|
+
const tIcu = value.hasOwnProperty('currentCaseLViewIndex') ? value :
|
|
14013
|
+
value.value;
|
|
14014
|
+
ngDevMode && assertTIcu(tIcu);
|
|
14015
|
+
return tIcu;
|
|
13836
14016
|
}
|
|
13837
14017
|
/**
|
|
13838
|
-
*
|
|
13839
|
-
*
|
|
14018
|
+
* Store `TIcu` at a give `index`.
|
|
14019
|
+
*
|
|
14020
|
+
* The `TIcu` can be stored either directly (if it is nested ICU) OR
|
|
14021
|
+
* it is stored inside tho `TIcuContainer` if it is top level ICU.
|
|
14022
|
+
*
|
|
14023
|
+
* The reason for this is that the top level ICU need a `TNode` so that they are part of the render
|
|
14024
|
+
* tree, but nested ICU's have no TNode, because we don't know ahead of time if the nested ICU is
|
|
14025
|
+
* expressed (parent ICU may have selected a case which does not contain it.)
|
|
14026
|
+
*
|
|
14027
|
+
* @param tView Current `TView`.
|
|
14028
|
+
* @param index Index where the value should be stored at in `Tview.data`
|
|
14029
|
+
* @param tIcu The TIcu to store.
|
|
13840
14030
|
*/
|
|
13841
|
-
function
|
|
13842
|
-
|
|
13843
|
-
|
|
13844
|
-
|
|
13845
|
-
|
|
13846
|
-
|
|
13847
|
-
|
|
13848
|
-
|
|
13849
|
-
|
|
13850
|
-
|
|
13851
|
-
cleanupLView(lView[i]);
|
|
13852
|
-
}
|
|
14031
|
+
function setTIcu(tView, index, tIcu) {
|
|
14032
|
+
const tNode = tView.data[index];
|
|
14033
|
+
ngDevMode &&
|
|
14034
|
+
assertEqual(tNode === null || tNode.hasOwnProperty('tView'), true, 'We expect to get \'null\'|\'TIcuContainer\'');
|
|
14035
|
+
if (tNode === null) {
|
|
14036
|
+
tView.data[index] = tIcu;
|
|
14037
|
+
}
|
|
14038
|
+
else {
|
|
14039
|
+
ngDevMode && assertTNodeType(tNode, 32 /* TNodeType.Icu */);
|
|
14040
|
+
tNode.value = tIcu;
|
|
13853
14041
|
}
|
|
13854
14042
|
}
|
|
13855
14043
|
/**
|
|
13856
|
-
*
|
|
13857
|
-
*
|
|
14044
|
+
* Set `TNode.insertBeforeIndex` taking the `Array` into account.
|
|
14045
|
+
*
|
|
14046
|
+
* See `TNode.insertBeforeIndex`
|
|
13858
14047
|
*/
|
|
13859
|
-
function
|
|
13860
|
-
|
|
13861
|
-
|
|
13862
|
-
|
|
13863
|
-
|
|
13864
|
-
|
|
13865
|
-
|
|
13866
|
-
|
|
13867
|
-
|
|
13868
|
-
|
|
13869
|
-
|
|
13870
|
-
// Cleanup in the root component view
|
|
13871
|
-
const componentLView = lNode[HOST];
|
|
13872
|
-
cleanupLView(componentLView);
|
|
13873
|
-
// Cleanup in all views within this view container
|
|
13874
|
-
cleanupLContainer(lNode);
|
|
13875
|
-
}
|
|
13876
|
-
ngDevMode && ngDevMode.dehydratedViewsCleanupRuns++;
|
|
13877
|
-
}
|
|
14048
|
+
function setTNodeInsertBeforeIndex(tNode, index) {
|
|
14049
|
+
ngDevMode && assertTNode(tNode);
|
|
14050
|
+
let insertBeforeIndex = tNode.insertBeforeIndex;
|
|
14051
|
+
if (insertBeforeIndex === null) {
|
|
14052
|
+
setI18nHandling(getInsertInFrontOfRNodeWithI18n, processI18nInsertBefore);
|
|
14053
|
+
insertBeforeIndex = tNode.insertBeforeIndex =
|
|
14054
|
+
[null /* may be updated to number later */, index];
|
|
14055
|
+
}
|
|
14056
|
+
else {
|
|
14057
|
+
assertEqual(Array.isArray(insertBeforeIndex), true, 'Expecting array here');
|
|
14058
|
+
insertBeforeIndex.push(index);
|
|
13878
14059
|
}
|
|
13879
14060
|
}
|
|
14061
|
+
/**
|
|
14062
|
+
* Create `TNode.type=TNodeType.Placeholder` node.
|
|
14063
|
+
*
|
|
14064
|
+
* See `TNodeType.Placeholder` for more information.
|
|
14065
|
+
*/
|
|
14066
|
+
function createTNodePlaceholder(tView, previousTNodes, index) {
|
|
14067
|
+
const tNode = createTNodeAtIndex(tView, index, 64 /* TNodeType.Placeholder */, null, null);
|
|
14068
|
+
addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tNode);
|
|
14069
|
+
return tNode;
|
|
14070
|
+
}
|
|
14071
|
+
/**
|
|
14072
|
+
* Returns current ICU case.
|
|
14073
|
+
*
|
|
14074
|
+
* ICU cases are stored as index into the `TIcu.cases`.
|
|
14075
|
+
* At times it is necessary to communicate that the ICU case just switched and that next ICU update
|
|
14076
|
+
* should update all bindings regardless of the mask. In such a case the we store negative numbers
|
|
14077
|
+
* for cases which have just been switched. This function removes the negative flag.
|
|
14078
|
+
*/
|
|
14079
|
+
function getCurrentICUCaseIndex(tIcu, lView) {
|
|
14080
|
+
const currentCase = lView[tIcu.currentCaseLViewIndex];
|
|
14081
|
+
return currentCase === null ? currentCase : (currentCase < 0 ? ~currentCase : currentCase);
|
|
14082
|
+
}
|
|
14083
|
+
function getParentFromIcuCreateOpCode(mergedCode) {
|
|
14084
|
+
return mergedCode >>> 17 /* IcuCreateOpCode.SHIFT_PARENT */;
|
|
14085
|
+
}
|
|
14086
|
+
function getRefFromIcuCreateOpCode(mergedCode) {
|
|
14087
|
+
return (mergedCode & 131070 /* IcuCreateOpCode.MASK_REF */) >>> 1 /* IcuCreateOpCode.SHIFT_REF */;
|
|
14088
|
+
}
|
|
14089
|
+
function getInstructionFromIcuCreateOpCode(mergedCode) {
|
|
14090
|
+
return mergedCode & 1 /* IcuCreateOpCode.MASK_INSTRUCTION */;
|
|
14091
|
+
}
|
|
14092
|
+
function icuCreateOpCode(opCode, parentIdx, refIdx) {
|
|
14093
|
+
ngDevMode && assertGreaterThanOrEqual(parentIdx, 0, 'Missing parent index');
|
|
14094
|
+
ngDevMode && assertGreaterThan(refIdx, 0, 'Missing ref index');
|
|
14095
|
+
return opCode | parentIdx << 17 /* IcuCreateOpCode.SHIFT_PARENT */ | refIdx << 1 /* IcuCreateOpCode.SHIFT_REF */;
|
|
14096
|
+
}
|
|
14097
|
+
// Returns whether the given value corresponds to a root template message,
|
|
14098
|
+
// or a sub-template.
|
|
14099
|
+
function isRootTemplateMessage(subTemplateIndex) {
|
|
14100
|
+
return subTemplateIndex === -1;
|
|
14101
|
+
}
|
|
13880
14102
|
|
|
13881
14103
|
/**
|
|
13882
14104
|
* Regexp that extracts a reference node information from the compressed node location.
|
|
@@ -13968,13 +14190,22 @@ function isDisconnectedNode(tNode, lView) {
|
|
|
13968
14190
|
function locateI18nRNodeByIndex(hydrationInfo, noOffsetIndex) {
|
|
13969
14191
|
const i18nNodes = hydrationInfo.i18nNodes;
|
|
13970
14192
|
if (i18nNodes) {
|
|
13971
|
-
|
|
13972
|
-
if (native) {
|
|
13973
|
-
i18nNodes.delete(noOffsetIndex);
|
|
13974
|
-
}
|
|
13975
|
-
return native;
|
|
14193
|
+
return i18nNodes.get(noOffsetIndex);
|
|
13976
14194
|
}
|
|
13977
|
-
return
|
|
14195
|
+
return undefined;
|
|
14196
|
+
}
|
|
14197
|
+
/**
|
|
14198
|
+
* Attempt to locate an RNode by a path, if it exists.
|
|
14199
|
+
*
|
|
14200
|
+
* @param hydrationInfo The hydration annotation data
|
|
14201
|
+
* @param lView the current lView
|
|
14202
|
+
* @param noOffsetIndex the instruction index
|
|
14203
|
+
* @returns an RNode that corresponds to the instruction index or null if no path exists
|
|
14204
|
+
*/
|
|
14205
|
+
function tryLocateRNodeByPath(hydrationInfo, lView, noOffsetIndex) {
|
|
14206
|
+
const nodes = hydrationInfo.data[NODES];
|
|
14207
|
+
const path = nodes?.[noOffsetIndex];
|
|
14208
|
+
return path ? locateRNodeByPath(path, lView) : null;
|
|
13978
14209
|
}
|
|
13979
14210
|
/**
|
|
13980
14211
|
* Locate a node in DOM tree that corresponds to a given TNode.
|
|
@@ -13988,7 +14219,7 @@ function locateI18nRNodeByIndex(hydrationInfo, noOffsetIndex) {
|
|
|
13988
14219
|
function locateNextRNode(hydrationInfo, tView, lView, tNode) {
|
|
13989
14220
|
const noOffsetIndex = getNoOffsetIndex(tNode);
|
|
13990
14221
|
let native = locateI18nRNodeByIndex(hydrationInfo, noOffsetIndex);
|
|
13991
|
-
if (
|
|
14222
|
+
if (native === undefined) {
|
|
13992
14223
|
const nodes = hydrationInfo.data[NODES];
|
|
13993
14224
|
if (nodes?.[noOffsetIndex]) {
|
|
13994
14225
|
// We know the exact location of the node.
|
|
@@ -14178,7 +14409,7 @@ function calcPathBetween(from, to, fromNodeName) {
|
|
|
14178
14409
|
* Invoked at serialization time (on the server) when a set of navigation
|
|
14179
14410
|
* instructions needs to be generated for a TNode.
|
|
14180
14411
|
*/
|
|
14181
|
-
function calcPathForNode(tNode, lView) {
|
|
14412
|
+
function calcPathForNode(tNode, lView, excludedParentNodes) {
|
|
14182
14413
|
let parentTNode = tNode.parent;
|
|
14183
14414
|
let parentIndex;
|
|
14184
14415
|
let parentRNode;
|
|
@@ -14190,7 +14421,12 @@ function calcPathForNode(tNode, lView) {
|
|
|
14190
14421
|
// a content of an element is projected and used, when a parent element
|
|
14191
14422
|
// itself remains detached from DOM. In this scenario we try to find a parent
|
|
14192
14423
|
// element that is attached to DOM and can act as an anchor instead.
|
|
14193
|
-
|
|
14424
|
+
//
|
|
14425
|
+
// It can also happen that the parent node should be excluded, for example,
|
|
14426
|
+
// because it belongs to an i18n block, which requires paths which aren't
|
|
14427
|
+
// relative to other views in an i18n block.
|
|
14428
|
+
while (parentTNode !== null &&
|
|
14429
|
+
(isDisconnectedNode(parentTNode, lView) || (excludedParentNodes?.has(parentTNode.index)))) {
|
|
14194
14430
|
parentTNode = parentTNode.parent;
|
|
14195
14431
|
}
|
|
14196
14432
|
if (parentTNode === null || !(parentTNode.type & 3 /* TNodeType.AnyRNode */)) {
|
|
@@ -14241,6 +14477,457 @@ function calcPathForNode(tNode, lView) {
|
|
|
14241
14477
|
return path;
|
|
14242
14478
|
}
|
|
14243
14479
|
|
|
14480
|
+
let _isI18nHydrationSupportEnabled = false;
|
|
14481
|
+
let _prepareI18nBlockForHydrationImpl = () => {
|
|
14482
|
+
// noop unless `enablePrepareI18nBlockForHydrationImpl` is invoked.
|
|
14483
|
+
};
|
|
14484
|
+
function setIsI18nHydrationSupportEnabled(enabled) {
|
|
14485
|
+
_isI18nHydrationSupportEnabled = enabled;
|
|
14486
|
+
}
|
|
14487
|
+
function isI18nHydrationSupportEnabled() {
|
|
14488
|
+
return _isI18nHydrationSupportEnabled;
|
|
14489
|
+
}
|
|
14490
|
+
/**
|
|
14491
|
+
* Prepares an i18n block and its children, located at the given
|
|
14492
|
+
* view and instruction index, for hydration.
|
|
14493
|
+
*
|
|
14494
|
+
* @param lView lView with the i18n block
|
|
14495
|
+
* @param index index of the i18n block in the lView
|
|
14496
|
+
* @param parentTNode TNode of the parent of the i18n block
|
|
14497
|
+
* @param subTemplateIndex sub-template index, or -1 for the main template
|
|
14498
|
+
*/
|
|
14499
|
+
function prepareI18nBlockForHydration(lView, index, parentTNode, subTemplateIndex) {
|
|
14500
|
+
_prepareI18nBlockForHydrationImpl(lView, index, parentTNode, subTemplateIndex);
|
|
14501
|
+
}
|
|
14502
|
+
function enablePrepareI18nBlockForHydrationImpl() {
|
|
14503
|
+
_prepareI18nBlockForHydrationImpl = prepareI18nBlockForHydrationImpl;
|
|
14504
|
+
}
|
|
14505
|
+
function isI18nHydrationEnabled(injector) {
|
|
14506
|
+
injector = injector ?? inject(Injector);
|
|
14507
|
+
return injector.get(IS_I18N_HYDRATION_ENABLED, false);
|
|
14508
|
+
}
|
|
14509
|
+
/**
|
|
14510
|
+
* Collects, if not already cached, all of the indices in the
|
|
14511
|
+
* given TView which are children of an i18n block.
|
|
14512
|
+
*
|
|
14513
|
+
* Since i18n blocks don't introduce a parent TNode, this is necessary
|
|
14514
|
+
* in order to determine which indices in a LView are translated.
|
|
14515
|
+
*/
|
|
14516
|
+
function getOrComputeI18nChildren(tView, context) {
|
|
14517
|
+
let i18nChildren = context.i18nChildren.get(tView);
|
|
14518
|
+
if (i18nChildren === undefined) {
|
|
14519
|
+
i18nChildren = collectI18nChildren(tView);
|
|
14520
|
+
context.i18nChildren.set(tView, i18nChildren);
|
|
14521
|
+
}
|
|
14522
|
+
return i18nChildren;
|
|
14523
|
+
}
|
|
14524
|
+
function collectI18nChildren(tView) {
|
|
14525
|
+
const children = new Set();
|
|
14526
|
+
function collectI18nViews(node) {
|
|
14527
|
+
children.add(node.index);
|
|
14528
|
+
switch (node.kind) {
|
|
14529
|
+
case 1 /* I18nNodeKind.ELEMENT */:
|
|
14530
|
+
case 2 /* I18nNodeKind.PLACEHOLDER */: {
|
|
14531
|
+
for (const childNode of node.children) {
|
|
14532
|
+
collectI18nViews(childNode);
|
|
14533
|
+
}
|
|
14534
|
+
break;
|
|
14535
|
+
}
|
|
14536
|
+
case 3 /* I18nNodeKind.ICU */: {
|
|
14537
|
+
for (const caseNodes of node.cases) {
|
|
14538
|
+
for (const caseNode of caseNodes) {
|
|
14539
|
+
collectI18nViews(caseNode);
|
|
14540
|
+
}
|
|
14541
|
+
}
|
|
14542
|
+
break;
|
|
14543
|
+
}
|
|
14544
|
+
}
|
|
14545
|
+
}
|
|
14546
|
+
// Traverse through the AST of each i18n block in the LView,
|
|
14547
|
+
// and collect every instruction index.
|
|
14548
|
+
for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
|
|
14549
|
+
const tI18n = tView.data[i];
|
|
14550
|
+
if (!tI18n || !tI18n.ast) {
|
|
14551
|
+
continue;
|
|
14552
|
+
}
|
|
14553
|
+
for (const node of tI18n.ast) {
|
|
14554
|
+
collectI18nViews(node);
|
|
14555
|
+
}
|
|
14556
|
+
}
|
|
14557
|
+
return children.size === 0 ? null : children;
|
|
14558
|
+
}
|
|
14559
|
+
/**
|
|
14560
|
+
* Attempts to serialize i18n data for an i18n block, located at
|
|
14561
|
+
* the given view and instruction index.
|
|
14562
|
+
*
|
|
14563
|
+
* @param lView lView with the i18n block
|
|
14564
|
+
* @param index index of the i18n block in the lView
|
|
14565
|
+
* @param context the hydration context
|
|
14566
|
+
* @returns the i18n data, or null if there is no relevant data
|
|
14567
|
+
*/
|
|
14568
|
+
function trySerializeI18nBlock(lView, index, context) {
|
|
14569
|
+
if (!context.isI18nHydrationEnabled) {
|
|
14570
|
+
return null;
|
|
14571
|
+
}
|
|
14572
|
+
const tView = lView[TVIEW];
|
|
14573
|
+
const tI18n = tView.data[index];
|
|
14574
|
+
if (!tI18n || !tI18n.ast) {
|
|
14575
|
+
return null;
|
|
14576
|
+
}
|
|
14577
|
+
const caseQueue = [];
|
|
14578
|
+
tI18n.ast.forEach(node => serializeI18nBlock(lView, caseQueue, context, node));
|
|
14579
|
+
return caseQueue.length > 0 ? caseQueue : null;
|
|
14580
|
+
}
|
|
14581
|
+
function serializeI18nBlock(lView, caseQueue, context, node) {
|
|
14582
|
+
switch (node.kind) {
|
|
14583
|
+
case 0 /* I18nNodeKind.TEXT */:
|
|
14584
|
+
const rNode = unwrapRNode(lView[node.index]);
|
|
14585
|
+
processTextNodeBeforeSerialization(context, rNode);
|
|
14586
|
+
break;
|
|
14587
|
+
case 1 /* I18nNodeKind.ELEMENT */:
|
|
14588
|
+
case 2 /* I18nNodeKind.PLACEHOLDER */:
|
|
14589
|
+
node.children.forEach(node => serializeI18nBlock(lView, caseQueue, context, node));
|
|
14590
|
+
break;
|
|
14591
|
+
case 3 /* I18nNodeKind.ICU */:
|
|
14592
|
+
const currentCase = lView[node.currentCaseLViewIndex];
|
|
14593
|
+
if (currentCase != null) {
|
|
14594
|
+
// i18n uses a negative value to signal a change to a new case, so we
|
|
14595
|
+
// need to invert it to get the proper value.
|
|
14596
|
+
const caseIdx = currentCase < 0 ? ~currentCase : currentCase;
|
|
14597
|
+
caseQueue.push(caseIdx);
|
|
14598
|
+
node.cases[caseIdx].forEach(node => serializeI18nBlock(lView, caseQueue, context, node));
|
|
14599
|
+
}
|
|
14600
|
+
break;
|
|
14601
|
+
}
|
|
14602
|
+
}
|
|
14603
|
+
function setCurrentNode(state, node) {
|
|
14604
|
+
state.currentNode = node;
|
|
14605
|
+
}
|
|
14606
|
+
/**
|
|
14607
|
+
* Marks the current RNode as the hydration root for the given
|
|
14608
|
+
* AST node.
|
|
14609
|
+
*/
|
|
14610
|
+
function appendI18nNodeToCollection(context, state, astNode) {
|
|
14611
|
+
const noOffsetIndex = astNode.index - HEADER_OFFSET;
|
|
14612
|
+
const { disconnectedNodes } = context;
|
|
14613
|
+
const currentNode = state.currentNode;
|
|
14614
|
+
if (state.isConnected) {
|
|
14615
|
+
context.i18nNodes.set(noOffsetIndex, currentNode);
|
|
14616
|
+
// We expect the node to be connected, so ensure that it
|
|
14617
|
+
// is not in the set, regardless of whether we found it,
|
|
14618
|
+
// so that the downstream error handling can provide the
|
|
14619
|
+
// proper context.
|
|
14620
|
+
disconnectedNodes.delete(noOffsetIndex);
|
|
14621
|
+
}
|
|
14622
|
+
else {
|
|
14623
|
+
disconnectedNodes.add(noOffsetIndex);
|
|
14624
|
+
}
|
|
14625
|
+
return currentNode;
|
|
14626
|
+
}
|
|
14627
|
+
/**
|
|
14628
|
+
* Skip over some sibling nodes during hydration.
|
|
14629
|
+
*
|
|
14630
|
+
* Note: we use this instead of `siblingAfter` as it's expected that
|
|
14631
|
+
* sometimes we might encounter null nodes. In those cases, we want to
|
|
14632
|
+
* defer to downstream error handling to provide proper context.
|
|
14633
|
+
*/
|
|
14634
|
+
function skipSiblingNodes(state, skip) {
|
|
14635
|
+
let currentNode = state.currentNode;
|
|
14636
|
+
for (let i = 0; i < skip; i++) {
|
|
14637
|
+
if (!currentNode) {
|
|
14638
|
+
break;
|
|
14639
|
+
}
|
|
14640
|
+
currentNode = currentNode?.nextSibling ?? null;
|
|
14641
|
+
}
|
|
14642
|
+
return currentNode;
|
|
14643
|
+
}
|
|
14644
|
+
/**
|
|
14645
|
+
* Fork the given state into a new state for hydrating children.
|
|
14646
|
+
*/
|
|
14647
|
+
function forkHydrationState(state, nextNode) {
|
|
14648
|
+
return { currentNode: nextNode, isConnected: state.isConnected };
|
|
14649
|
+
}
|
|
14650
|
+
function prepareI18nBlockForHydrationImpl(lView, index, parentTNode, subTemplateIndex) {
|
|
14651
|
+
if (!isI18nHydrationSupportEnabled()) {
|
|
14652
|
+
return;
|
|
14653
|
+
}
|
|
14654
|
+
const hydrationInfo = lView[HYDRATION];
|
|
14655
|
+
if (!hydrationInfo) {
|
|
14656
|
+
return;
|
|
14657
|
+
}
|
|
14658
|
+
const tView = lView[TVIEW];
|
|
14659
|
+
const tI18n = tView.data[index];
|
|
14660
|
+
ngDevMode &&
|
|
14661
|
+
assertDefined(tI18n, 'Expected i18n data to be present in a given TView slot during hydration');
|
|
14662
|
+
function findHydrationRoot() {
|
|
14663
|
+
if (isRootTemplateMessage(subTemplateIndex)) {
|
|
14664
|
+
// This is the root of an i18n block. In this case, our hydration root will
|
|
14665
|
+
// depend on where our parent TNode (i.e. the block with i18n applied) is
|
|
14666
|
+
// in the DOM.
|
|
14667
|
+
ngDevMode && assertDefined(parentTNode, 'Expected parent TNode while hydrating i18n root');
|
|
14668
|
+
const rootNode = locateNextRNode(hydrationInfo, tView, lView, parentTNode);
|
|
14669
|
+
// If this i18n block is attached to an <ng-container>, then we want to begin
|
|
14670
|
+
// hydrating directly with the RNode. Otherwise, for a TNode with a physical DOM
|
|
14671
|
+
// element, we want to recurse into the first child and begin there.
|
|
14672
|
+
return (parentTNode.type & 8 /* TNodeType.ElementContainer */) ? rootNode : rootNode.firstChild;
|
|
14673
|
+
}
|
|
14674
|
+
// This is a nested template in an i18n block. In this case, the entire view
|
|
14675
|
+
// is translated, and part of a dehydrated view in a container. This means that
|
|
14676
|
+
// we can simply begin hydration with the first dehydrated child.
|
|
14677
|
+
return hydrationInfo?.firstChild;
|
|
14678
|
+
}
|
|
14679
|
+
const currentNode = findHydrationRoot();
|
|
14680
|
+
ngDevMode && assertDefined(currentNode, 'Expected root i18n node during hydration');
|
|
14681
|
+
const disconnectedNodes = initDisconnectedNodes(hydrationInfo) ?? new Set();
|
|
14682
|
+
const i18nNodes = hydrationInfo.i18nNodes ??= new Map();
|
|
14683
|
+
const caseQueue = hydrationInfo.data[I18N_DATA]?.[index - HEADER_OFFSET] ?? [];
|
|
14684
|
+
const dehydratedIcuData = hydrationInfo.dehydratedIcuData ??=
|
|
14685
|
+
new Map();
|
|
14686
|
+
collectI18nNodesFromDom({ hydrationInfo, lView, i18nNodes, disconnectedNodes, caseQueue, dehydratedIcuData }, { currentNode, isConnected: true }, tI18n.ast);
|
|
14687
|
+
// Nodes from inactive ICU cases should be considered disconnected. We track them above
|
|
14688
|
+
// because they aren't (and shouldn't be) serialized. Since we may mutate or create a
|
|
14689
|
+
// new set, we need to be sure to write the expected value back to the DehydratedView.
|
|
14690
|
+
hydrationInfo.disconnectedNodes = disconnectedNodes.size === 0 ? null : disconnectedNodes;
|
|
14691
|
+
}
|
|
14692
|
+
function collectI18nNodesFromDom(context, state, nodeOrNodes) {
|
|
14693
|
+
if (Array.isArray(nodeOrNodes)) {
|
|
14694
|
+
for (const node of nodeOrNodes) {
|
|
14695
|
+
// If the node is being projected elsewhere, we need to temporarily
|
|
14696
|
+
// branch the state to that location to continue hydration.
|
|
14697
|
+
// Otherwise, we continue hydration from the current location.
|
|
14698
|
+
const targetNode = tryLocateRNodeByPath(context.hydrationInfo, context.lView, node.index - HEADER_OFFSET);
|
|
14699
|
+
const nextState = targetNode ? forkHydrationState(state, targetNode) : state;
|
|
14700
|
+
collectI18nNodesFromDom(context, nextState, node);
|
|
14701
|
+
}
|
|
14702
|
+
}
|
|
14703
|
+
else {
|
|
14704
|
+
switch (nodeOrNodes.kind) {
|
|
14705
|
+
case 0 /* I18nNodeKind.TEXT */: {
|
|
14706
|
+
// Claim a text node for hydration
|
|
14707
|
+
const currentNode = appendI18nNodeToCollection(context, state, nodeOrNodes);
|
|
14708
|
+
setCurrentNode(state, currentNode?.nextSibling ?? null);
|
|
14709
|
+
break;
|
|
14710
|
+
}
|
|
14711
|
+
case 1 /* I18nNodeKind.ELEMENT */: {
|
|
14712
|
+
// Recurse into the current element's children...
|
|
14713
|
+
collectI18nNodesFromDom(context, forkHydrationState(state, state.currentNode?.firstChild ?? null), nodeOrNodes.children);
|
|
14714
|
+
// And claim the parent element itself.
|
|
14715
|
+
const currentNode = appendI18nNodeToCollection(context, state, nodeOrNodes);
|
|
14716
|
+
setCurrentNode(state, currentNode?.nextSibling ?? null);
|
|
14717
|
+
break;
|
|
14718
|
+
}
|
|
14719
|
+
case 2 /* I18nNodeKind.PLACEHOLDER */: {
|
|
14720
|
+
const noOffsetIndex = nodeOrNodes.index - HEADER_OFFSET;
|
|
14721
|
+
const { hydrationInfo } = context;
|
|
14722
|
+
const containerSize = getNgContainerSize(hydrationInfo, noOffsetIndex);
|
|
14723
|
+
switch (nodeOrNodes.type) {
|
|
14724
|
+
case 0 /* I18nPlaceholderType.ELEMENT */: {
|
|
14725
|
+
// Hydration expects to find the head of the element.
|
|
14726
|
+
const currentNode = appendI18nNodeToCollection(context, state, nodeOrNodes);
|
|
14727
|
+
// A TNode for the node may not yet if we're hydrating during the first pass,
|
|
14728
|
+
// so use the serialized data to determine if this is an <ng-container>.
|
|
14729
|
+
if (isSerializedElementContainer(hydrationInfo, noOffsetIndex)) {
|
|
14730
|
+
// An <ng-container> doesn't have a physical DOM node, so we need to
|
|
14731
|
+
// continue hydrating from siblings.
|
|
14732
|
+
collectI18nNodesFromDom(context, state, nodeOrNodes.children);
|
|
14733
|
+
// Skip over the anchor element. It will be claimed by the
|
|
14734
|
+
// downstream container hydration.
|
|
14735
|
+
const nextNode = skipSiblingNodes(state, 1);
|
|
14736
|
+
setCurrentNode(state, nextNode);
|
|
14737
|
+
}
|
|
14738
|
+
else {
|
|
14739
|
+
// Non-container elements represent an actual node in the DOM, so we
|
|
14740
|
+
// need to continue hydration with the children, and claim the node.
|
|
14741
|
+
collectI18nNodesFromDom(context, forkHydrationState(state, state.currentNode?.firstChild ?? null), nodeOrNodes.children);
|
|
14742
|
+
setCurrentNode(state, currentNode?.nextSibling ?? null);
|
|
14743
|
+
// Elements can also be the anchor of a view container, so there may
|
|
14744
|
+
// be elements after this node that we need to skip.
|
|
14745
|
+
if (containerSize !== null) {
|
|
14746
|
+
// `+1` stands for an anchor node after all of the views in the container.
|
|
14747
|
+
const nextNode = skipSiblingNodes(state, containerSize + 1);
|
|
14748
|
+
setCurrentNode(state, nextNode);
|
|
14749
|
+
}
|
|
14750
|
+
}
|
|
14751
|
+
break;
|
|
14752
|
+
}
|
|
14753
|
+
case 1 /* I18nPlaceholderType.SUBTEMPLATE */: {
|
|
14754
|
+
ngDevMode &&
|
|
14755
|
+
assertNotEqual(containerSize, null, 'Expected a container size while hydrating i18n subtemplate');
|
|
14756
|
+
// Hydration expects to find the head of the template.
|
|
14757
|
+
appendI18nNodeToCollection(context, state, nodeOrNodes);
|
|
14758
|
+
// Skip over all of the template children, as well as the anchor
|
|
14759
|
+
// node, since the template itself will handle them instead.
|
|
14760
|
+
const nextNode = skipSiblingNodes(state, containerSize + 1);
|
|
14761
|
+
setCurrentNode(state, nextNode);
|
|
14762
|
+
break;
|
|
14763
|
+
}
|
|
14764
|
+
}
|
|
14765
|
+
break;
|
|
14766
|
+
}
|
|
14767
|
+
case 3 /* I18nNodeKind.ICU */: {
|
|
14768
|
+
// If the current node is connected, we need to pop the next case from the
|
|
14769
|
+
// queue, so that the active case is also considered connected.
|
|
14770
|
+
const selectedCase = state.isConnected ? context.caseQueue.shift() : null;
|
|
14771
|
+
const childState = { currentNode: null, isConnected: false };
|
|
14772
|
+
// We traverse through each case, even if it's not active,
|
|
14773
|
+
// so that we correctly populate disconnected nodes.
|
|
14774
|
+
for (let i = 0; i < nodeOrNodes.cases.length; i++) {
|
|
14775
|
+
collectI18nNodesFromDom(context, i === selectedCase ? state : childState, nodeOrNodes.cases[i]);
|
|
14776
|
+
}
|
|
14777
|
+
if (selectedCase !== null) {
|
|
14778
|
+
// ICUs represent a branching state, and the selected case could be different
|
|
14779
|
+
// than what it was on the server. In that case, we need to be able to clean
|
|
14780
|
+
// up the nodes from the original case. To do that, we store the selected case.
|
|
14781
|
+
context.dehydratedIcuData.set(nodeOrNodes.index, { case: selectedCase, node: nodeOrNodes });
|
|
14782
|
+
}
|
|
14783
|
+
// Hydration expects to find the ICU anchor element.
|
|
14784
|
+
const currentNode = appendI18nNodeToCollection(context, state, nodeOrNodes);
|
|
14785
|
+
setCurrentNode(state, currentNode?.nextSibling ?? null);
|
|
14786
|
+
break;
|
|
14787
|
+
}
|
|
14788
|
+
}
|
|
14789
|
+
}
|
|
14790
|
+
}
|
|
14791
|
+
let _claimDehydratedIcuCaseImpl = () => {
|
|
14792
|
+
// noop unless `enableClaimDehydratedIcuCaseImpl` is invoked
|
|
14793
|
+
};
|
|
14794
|
+
/**
|
|
14795
|
+
* Mark the case for the ICU node at the given index in the view as claimed,
|
|
14796
|
+
* allowing its nodes to be hydrated and not cleaned up.
|
|
14797
|
+
*/
|
|
14798
|
+
function claimDehydratedIcuCase(lView, icuIndex, caseIndex) {
|
|
14799
|
+
_claimDehydratedIcuCaseImpl(lView, icuIndex, caseIndex);
|
|
14800
|
+
}
|
|
14801
|
+
function enableClaimDehydratedIcuCaseImpl() {
|
|
14802
|
+
_claimDehydratedIcuCaseImpl = claimDehydratedIcuCaseImpl;
|
|
14803
|
+
}
|
|
14804
|
+
function claimDehydratedIcuCaseImpl(lView, icuIndex, caseIndex) {
|
|
14805
|
+
const dehydratedIcuDataMap = lView[HYDRATION]?.dehydratedIcuData;
|
|
14806
|
+
if (dehydratedIcuDataMap) {
|
|
14807
|
+
const dehydratedIcuData = dehydratedIcuDataMap.get(icuIndex);
|
|
14808
|
+
if (dehydratedIcuData?.case === caseIndex) {
|
|
14809
|
+
// If the case we're attempting to claim matches the dehydrated one,
|
|
14810
|
+
// we remove it from the map to mark it as "claimed."
|
|
14811
|
+
dehydratedIcuDataMap.delete(icuIndex);
|
|
14812
|
+
}
|
|
14813
|
+
}
|
|
14814
|
+
}
|
|
14815
|
+
/**
|
|
14816
|
+
* Clean up all i18n hydration data associated with the given view.
|
|
14817
|
+
*/
|
|
14818
|
+
function cleanupI18nHydrationData(lView) {
|
|
14819
|
+
const hydrationInfo = lView[HYDRATION];
|
|
14820
|
+
if (hydrationInfo) {
|
|
14821
|
+
const { i18nNodes, dehydratedIcuData: dehydratedIcuDataMap } = hydrationInfo;
|
|
14822
|
+
if (i18nNodes && dehydratedIcuDataMap) {
|
|
14823
|
+
const renderer = lView[RENDERER];
|
|
14824
|
+
for (const dehydratedIcuData of dehydratedIcuDataMap.values()) {
|
|
14825
|
+
cleanupDehydratedIcuData(renderer, i18nNodes, dehydratedIcuData);
|
|
14826
|
+
}
|
|
14827
|
+
}
|
|
14828
|
+
hydrationInfo.i18nNodes = undefined;
|
|
14829
|
+
hydrationInfo.dehydratedIcuData = undefined;
|
|
14830
|
+
}
|
|
14831
|
+
}
|
|
14832
|
+
function cleanupDehydratedIcuData(renderer, i18nNodes, dehydratedIcuData) {
|
|
14833
|
+
for (const node of dehydratedIcuData.node.cases[dehydratedIcuData.case]) {
|
|
14834
|
+
const rNode = i18nNodes.get(node.index - HEADER_OFFSET);
|
|
14835
|
+
if (rNode) {
|
|
14836
|
+
nativeRemoveNode(renderer, rNode, false);
|
|
14837
|
+
}
|
|
14838
|
+
}
|
|
14839
|
+
}
|
|
14840
|
+
|
|
14841
|
+
/**
|
|
14842
|
+
* Removes all dehydrated views from a given LContainer:
|
|
14843
|
+
* both in internal data structure, as well as removing
|
|
14844
|
+
* corresponding DOM nodes that belong to that dehydrated view.
|
|
14845
|
+
*/
|
|
14846
|
+
function removeDehydratedViews(lContainer) {
|
|
14847
|
+
const views = lContainer[DEHYDRATED_VIEWS] ?? [];
|
|
14848
|
+
const parentLView = lContainer[PARENT];
|
|
14849
|
+
const renderer = parentLView[RENDERER];
|
|
14850
|
+
for (const view of views) {
|
|
14851
|
+
removeDehydratedView(view, renderer);
|
|
14852
|
+
ngDevMode && ngDevMode.dehydratedViewsRemoved++;
|
|
14853
|
+
}
|
|
14854
|
+
// Reset the value to an empty array to indicate that no
|
|
14855
|
+
// further processing of dehydrated views is needed for
|
|
14856
|
+
// this view container (i.e. do not trigger the lookup process
|
|
14857
|
+
// once again in case a `ViewContainerRef` is created later).
|
|
14858
|
+
lContainer[DEHYDRATED_VIEWS] = EMPTY_ARRAY;
|
|
14859
|
+
}
|
|
14860
|
+
/**
|
|
14861
|
+
* Helper function to remove all nodes from a dehydrated view.
|
|
14862
|
+
*/
|
|
14863
|
+
function removeDehydratedView(dehydratedView, renderer) {
|
|
14864
|
+
let nodesRemoved = 0;
|
|
14865
|
+
let currentRNode = dehydratedView.firstChild;
|
|
14866
|
+
if (currentRNode) {
|
|
14867
|
+
const numNodes = dehydratedView.data[NUM_ROOT_NODES];
|
|
14868
|
+
while (nodesRemoved < numNodes) {
|
|
14869
|
+
ngDevMode && validateSiblingNodeExists(currentRNode);
|
|
14870
|
+
const nextSibling = currentRNode.nextSibling;
|
|
14871
|
+
nativeRemoveNode(renderer, currentRNode, false);
|
|
14872
|
+
currentRNode = nextSibling;
|
|
14873
|
+
nodesRemoved++;
|
|
14874
|
+
}
|
|
14875
|
+
}
|
|
14876
|
+
}
|
|
14877
|
+
/**
|
|
14878
|
+
* Walks over all views within this LContainer invokes dehydrated views
|
|
14879
|
+
* cleanup function for each one.
|
|
14880
|
+
*/
|
|
14881
|
+
function cleanupLContainer(lContainer) {
|
|
14882
|
+
removeDehydratedViews(lContainer);
|
|
14883
|
+
for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
|
|
14884
|
+
cleanupLView(lContainer[i]);
|
|
14885
|
+
}
|
|
14886
|
+
}
|
|
14887
|
+
/**
|
|
14888
|
+
* Walks over `LContainer`s and components registered within
|
|
14889
|
+
* this LView and invokes dehydrated views cleanup function for each one.
|
|
14890
|
+
*/
|
|
14891
|
+
function cleanupLView(lView) {
|
|
14892
|
+
cleanupI18nHydrationData(lView);
|
|
14893
|
+
const tView = lView[TVIEW];
|
|
14894
|
+
for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
|
|
14895
|
+
if (isLContainer(lView[i])) {
|
|
14896
|
+
const lContainer = lView[i];
|
|
14897
|
+
cleanupLContainer(lContainer);
|
|
14898
|
+
}
|
|
14899
|
+
else if (isLView(lView[i])) {
|
|
14900
|
+
// This is a component, enter the `cleanupLView` recursively.
|
|
14901
|
+
cleanupLView(lView[i]);
|
|
14902
|
+
}
|
|
14903
|
+
}
|
|
14904
|
+
}
|
|
14905
|
+
/**
|
|
14906
|
+
* Walks over all views registered within the ApplicationRef and removes
|
|
14907
|
+
* all dehydrated views from all `LContainer`s along the way.
|
|
14908
|
+
*/
|
|
14909
|
+
function cleanupDehydratedViews(appRef) {
|
|
14910
|
+
const viewRefs = appRef._views;
|
|
14911
|
+
for (const viewRef of viewRefs) {
|
|
14912
|
+
const lNode = getLNodeForHydration(viewRef);
|
|
14913
|
+
// An `lView` might be `null` if a `ViewRef` represents
|
|
14914
|
+
// an embedded view (not a component view).
|
|
14915
|
+
if (lNode !== null && lNode[HOST] !== null) {
|
|
14916
|
+
if (isLView(lNode)) {
|
|
14917
|
+
cleanupLView(lNode);
|
|
14918
|
+
}
|
|
14919
|
+
else {
|
|
14920
|
+
// Cleanup in the root component view
|
|
14921
|
+
const componentLView = lNode[HOST];
|
|
14922
|
+
cleanupLView(componentLView);
|
|
14923
|
+
// Cleanup in all views within this view container
|
|
14924
|
+
cleanupLContainer(lNode);
|
|
14925
|
+
}
|
|
14926
|
+
ngDevMode && ngDevMode.dehydratedViewsCleanupRuns++;
|
|
14927
|
+
}
|
|
14928
|
+
}
|
|
14929
|
+
}
|
|
14930
|
+
|
|
14244
14931
|
/**
|
|
14245
14932
|
* Given a current DOM node and a serialized information about the views
|
|
14246
14933
|
* in a container, walks over the DOM structure, collecting the list of
|
|
@@ -14319,6 +15006,9 @@ function findMatchingDehydratedView(lContainer, template) {
|
|
|
14319
15006
|
*/
|
|
14320
15007
|
class ChangeDetectionScheduler {
|
|
14321
15008
|
}
|
|
15009
|
+
/** Token used to indicate if zoneless was enabled via provideZonelessChangeDetection(). */
|
|
15010
|
+
const ZONELESS_ENABLED = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'Zoneless enabled' : '', { providedIn: 'root', factory: () => false });
|
|
15011
|
+
const ZONELESS_SCHEDULER_DISABLED = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'scheduler disabled' : '');
|
|
14322
15012
|
|
|
14323
15013
|
/**
|
|
14324
15014
|
* Represents a component created by a `ComponentFactory`.
|
|
@@ -14511,9 +15201,11 @@ function performanceMarkFeature(feature) {
|
|
|
14511
15201
|
*
|
|
14512
15202
|
* By running change detection after the first of `setTimeout` and `rAF` to execute, we get the
|
|
14513
15203
|
* best of both worlds.
|
|
15204
|
+
*
|
|
15205
|
+
* @returns a function to cancel the scheduled callback
|
|
14514
15206
|
*/
|
|
14515
|
-
function
|
|
14516
|
-
// Note: the `
|
|
15207
|
+
function scheduleCallback(callback) {
|
|
15208
|
+
// Note: the `scheduleCallback` is used in the `NgZone` class, but we cannot use the
|
|
14517
15209
|
// `inject` function. The `NgZone` instance may be created manually, and thus the injection
|
|
14518
15210
|
// context will be unavailable. This might be enough to check whether `requestAnimationFrame` is
|
|
14519
15211
|
// available because otherwise, we'll fall back to `setTimeout`.
|
|
@@ -14521,36 +15213,29 @@ function getCallbackScheduler() {
|
|
|
14521
15213
|
let nativeRequestAnimationFrame = hasRequestAnimationFrame ? _global['requestAnimationFrame'] : null;
|
|
14522
15214
|
let nativeSetTimeout = _global['setTimeout'];
|
|
14523
15215
|
if (typeof Zone !== 'undefined') {
|
|
14524
|
-
|
|
14525
|
-
// `__zone_symbol__OriginalDelegate` key (see `attachOriginToPatched`). Given the following
|
|
14526
|
-
// example: `window.requestAnimationFrame.__zone_symbol__OriginalDelegate`; this would return an
|
|
14527
|
-
// unpatched implementation of the `requestAnimationFrame`, which isn't intercepted by the
|
|
14528
|
-
// Angular zone. We use the unpatched implementation to avoid another change detection when
|
|
14529
|
-
// coalescing tasks.
|
|
14530
|
-
const ORIGINAL_DELEGATE_SYMBOL = Zone.__symbol__('OriginalDelegate');
|
|
14531
|
-
if (nativeRequestAnimationFrame) {
|
|
15216
|
+
if (hasRequestAnimationFrame) {
|
|
14532
15217
|
nativeRequestAnimationFrame =
|
|
14533
|
-
|
|
14534
|
-
nativeRequestAnimationFrame;
|
|
15218
|
+
_global[Zone.__symbol__('requestAnimationFrame')] ?? nativeRequestAnimationFrame;
|
|
14535
15219
|
}
|
|
14536
|
-
nativeSetTimeout =
|
|
15220
|
+
nativeSetTimeout = _global[Zone.__symbol__('setTimeout')] ?? nativeSetTimeout;
|
|
14537
15221
|
}
|
|
14538
|
-
|
|
14539
|
-
|
|
14540
|
-
|
|
14541
|
-
|
|
14542
|
-
|
|
14543
|
-
|
|
14544
|
-
|
|
14545
|
-
|
|
14546
|
-
|
|
14547
|
-
|
|
14548
|
-
|
|
14549
|
-
|
|
14550
|
-
|
|
14551
|
-
|
|
14552
|
-
|
|
14553
|
-
|
|
15222
|
+
let executeCallback = true;
|
|
15223
|
+
nativeSetTimeout(() => {
|
|
15224
|
+
if (!executeCallback) {
|
|
15225
|
+
return;
|
|
15226
|
+
}
|
|
15227
|
+
executeCallback = false;
|
|
15228
|
+
callback();
|
|
15229
|
+
});
|
|
15230
|
+
nativeRequestAnimationFrame?.(() => {
|
|
15231
|
+
if (!executeCallback) {
|
|
15232
|
+
return;
|
|
15233
|
+
}
|
|
15234
|
+
executeCallback = false;
|
|
15235
|
+
callback();
|
|
15236
|
+
});
|
|
15237
|
+
return () => {
|
|
15238
|
+
executeCallback = false;
|
|
14554
15239
|
};
|
|
14555
15240
|
}
|
|
14556
15241
|
|
|
@@ -14708,7 +15393,7 @@ class NgZone {
|
|
|
14708
15393
|
!shouldCoalesceRunChangeDetection && shouldCoalesceEventChangeDetection;
|
|
14709
15394
|
self.shouldCoalesceRunChangeDetection = shouldCoalesceRunChangeDetection;
|
|
14710
15395
|
self.callbackScheduled = false;
|
|
14711
|
-
self.scheduleCallback =
|
|
15396
|
+
self.scheduleCallback = scheduleCallback;
|
|
14712
15397
|
forkInnerZoneWithAngularBehavior(self);
|
|
14713
15398
|
}
|
|
14714
15399
|
/**
|
|
@@ -16040,7 +16725,7 @@ function createRootComponent(componentView, rootComponentDef, rootDirectives, ho
|
|
|
16040
16725
|
function setRootNodeAttributes(hostRenderer, componentDef, hostRNode, rootSelectorOrNode) {
|
|
16041
16726
|
if (rootSelectorOrNode) {
|
|
16042
16727
|
// The placeholder will be replaced with the actual version at build time.
|
|
16043
|
-
setUpAttributes(hostRenderer, hostRNode, ['ng-version', '18.0.0-next.
|
|
16728
|
+
setUpAttributes(hostRenderer, hostRNode, ['ng-version', '18.0.0-next.3']);
|
|
16044
16729
|
}
|
|
16045
16730
|
else {
|
|
16046
16731
|
// If host element is created as a part of this function call (i.e. `rootSelectorOrNode`
|
|
@@ -16095,7 +16780,7 @@ function LifecycleHooksFeature() {
|
|
|
16095
16780
|
* (created by instantiating a `TemplateRef` with the `createEmbeddedView()` method).
|
|
16096
16781
|
*
|
|
16097
16782
|
* A view container instance can contain other view containers,
|
|
16098
|
-
* creating a
|
|
16783
|
+
* creating a view hierarchy.
|
|
16099
16784
|
*
|
|
16100
16785
|
* @usageNotes
|
|
16101
16786
|
*
|
|
@@ -17093,6 +17778,7 @@ function viewChildRequiredFn(locator, opts) {
|
|
|
17093
17778
|
* ```
|
|
17094
17779
|
*
|
|
17095
17780
|
* @developerPreview
|
|
17781
|
+
* @initializerApiFunction
|
|
17096
17782
|
*/
|
|
17097
17783
|
const viewChild = (() => {
|
|
17098
17784
|
// Note: This may be considered a side-effect, but nothing will depend on
|
|
@@ -17117,6 +17803,9 @@ const viewChild = (() => {
|
|
|
17117
17803
|
* divEls = viewChildren<ElementRef>('el'); // Signal<ReadonlyArray<ElementRef>>
|
|
17118
17804
|
* }
|
|
17119
17805
|
* ```
|
|
17806
|
+
*
|
|
17807
|
+
* @initializerApiFunction
|
|
17808
|
+
* @developerPreview
|
|
17120
17809
|
*/
|
|
17121
17810
|
function viewChildren(locator, opts) {
|
|
17122
17811
|
ngDevMode && assertInInjectionContext(viewChildren);
|
|
@@ -17147,6 +17836,9 @@ function contentChildRequiredFn(locator, opts) {
|
|
|
17147
17836
|
* headerRequired = contentChild.required(MyHeader); // Signal<MyHeader>
|
|
17148
17837
|
* }
|
|
17149
17838
|
* ```
|
|
17839
|
+
*
|
|
17840
|
+
* @initializerApiFunction
|
|
17841
|
+
* @developerPreview
|
|
17150
17842
|
*/
|
|
17151
17843
|
const contentChild = (() => {
|
|
17152
17844
|
// Note: This may be considered a side-effect, but nothing will depend on
|
|
@@ -17171,6 +17863,9 @@ const contentChild = (() => {
|
|
|
17171
17863
|
* headerEl = contentChildren<ElementRef>('h'); // Signal<ReadonlyArray<ElementRef>>
|
|
17172
17864
|
* }
|
|
17173
17865
|
* ```
|
|
17866
|
+
*
|
|
17867
|
+
* @initializerApiFunction
|
|
17868
|
+
* @developerPreview
|
|
17174
17869
|
*/
|
|
17175
17870
|
function contentChildren(locator, opts) {
|
|
17176
17871
|
return createMultiResultQuerySignalFn();
|
|
@@ -17228,31 +17923,51 @@ function modelRequiredFunction() {
|
|
|
17228
17923
|
return createModelSignal(REQUIRED_UNSET_VALUE);
|
|
17229
17924
|
}
|
|
17230
17925
|
/**
|
|
17231
|
-
* `model` declares a writeable signal that is exposed as an input/output
|
|
17232
|
-
*
|
|
17926
|
+
* `model` declares a writeable signal that is exposed as an input/output
|
|
17927
|
+
* pair on the containing directive.
|
|
17928
|
+
*
|
|
17929
|
+
* The input name is taken either from the class member or from the `alias` option.
|
|
17233
17930
|
* The output name is generated by taking the input name and appending `Change`.
|
|
17234
17931
|
*
|
|
17235
|
-
*
|
|
17236
|
-
*
|
|
17932
|
+
* @usageNotes
|
|
17933
|
+
*
|
|
17934
|
+
* To use `model()`, import the function from `@angular/core`.
|
|
17237
17935
|
*
|
|
17238
|
-
*
|
|
17239
|
-
*
|
|
17936
|
+
* ```
|
|
17937
|
+
* import {model} from '@angular/core`;
|
|
17938
|
+
* ```
|
|
17240
17939
|
*
|
|
17241
|
-
*
|
|
17242
|
-
*
|
|
17243
|
-
* class field and initializing it with the `model()` or `model.required()`
|
|
17244
|
-
* function.
|
|
17940
|
+
* Inside your component, introduce a new class member and initialize
|
|
17941
|
+
* it with a call to `model` or `model.required`.
|
|
17245
17942
|
*
|
|
17246
17943
|
* ```ts
|
|
17247
|
-
* @Directive({
|
|
17944
|
+
* @Directive({
|
|
17945
|
+
* ...
|
|
17946
|
+
* })
|
|
17248
17947
|
* export class MyDir {
|
|
17249
|
-
* firstName = model<string>(); // string|undefined
|
|
17250
|
-
* lastName
|
|
17251
|
-
* age
|
|
17948
|
+
* firstName = model<string>(); // ModelSignal<string|undefined>
|
|
17949
|
+
* lastName = model.required<string>(); // ModelSignal<string>
|
|
17950
|
+
* age = model(0); // ModelSignal<number>
|
|
17951
|
+
* }
|
|
17952
|
+
* ```
|
|
17953
|
+
*
|
|
17954
|
+
* Inside your component template, you can display the value of a `model`
|
|
17955
|
+
* by calling the signal.
|
|
17956
|
+
*
|
|
17957
|
+
* ```html
|
|
17958
|
+
* <span>{{firstName()}}</span>
|
|
17959
|
+
* ```
|
|
17960
|
+
*
|
|
17961
|
+
* Updating the `model` is equivalent to updating a writable signal.
|
|
17962
|
+
*
|
|
17963
|
+
* ```ts
|
|
17964
|
+
* updateName(newFirstName: string): void {
|
|
17965
|
+
* this.firstName.set(newFirstName);
|
|
17252
17966
|
* }
|
|
17253
17967
|
* ```
|
|
17254
17968
|
*
|
|
17255
17969
|
* @developerPreview
|
|
17970
|
+
* @initializerApiFunction
|
|
17256
17971
|
*/
|
|
17257
17972
|
const model = (() => {
|
|
17258
17973
|
// Note: This may be considered a side-effect, but nothing will depend on
|
|
@@ -18411,12 +19126,12 @@ function isDetachedByI18n(tNode) {
|
|
|
18411
19126
|
return (tNode.flags & 32 /* TNodeFlags.isDetached */) === 32 /* TNodeFlags.isDetached */;
|
|
18412
19127
|
}
|
|
18413
19128
|
|
|
18414
|
-
function templateFirstCreatePass(index, tView, lView, templateFn, decls, vars, tagName,
|
|
19129
|
+
function templateFirstCreatePass(index, tView, lView, templateFn, decls, vars, tagName, attrs, localRefsIndex) {
|
|
18415
19130
|
ngDevMode && assertFirstCreatePass(tView);
|
|
18416
19131
|
ngDevMode && ngDevMode.firstCreatePass++;
|
|
18417
19132
|
const tViewConsts = tView.consts;
|
|
18418
19133
|
// TODO(pk): refactor getOrCreateTNode to have the "create" only version
|
|
18419
|
-
const tNode = getOrCreateTNode(tView, index, 4 /* TNodeType.Container */, tagName || null,
|
|
19134
|
+
const tNode = getOrCreateTNode(tView, index, 4 /* TNodeType.Container */, tagName || null, attrs || null);
|
|
18420
19135
|
resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex));
|
|
18421
19136
|
registerPostOrderHooks(tView, tNode);
|
|
18422
19137
|
const embeddedTView = tNode.tView = createTView(2 /* TViewType.Embedded */, tNode, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, tView.schemas, tViewConsts, null /* ssrId */);
|
|
@@ -18427,12 +19142,10 @@ function templateFirstCreatePass(index, tView, lView, templateFn, decls, vars, t
|
|
|
18427
19142
|
return tNode;
|
|
18428
19143
|
}
|
|
18429
19144
|
/**
|
|
18430
|
-
* Creates an LContainer for an
|
|
18431
|
-
*
|
|
18432
|
-
* <ng-template #foo>
|
|
18433
|
-
* <div></div>
|
|
18434
|
-
* </ng-template>
|
|
19145
|
+
* Creates an LContainer for an embedded view.
|
|
18435
19146
|
*
|
|
19147
|
+
* @param declarationLView LView in which the template was declared.
|
|
19148
|
+
* @param declarationTView TView in which the template wa declared.
|
|
18436
19149
|
* @param index The index of the container in the data array
|
|
18437
19150
|
* @param templateFn Inline template
|
|
18438
19151
|
* @param decls The number of nodes, local refs, and pipes for this template
|
|
@@ -18442,34 +19155,57 @@ function templateFirstCreatePass(index, tView, lView, templateFn, decls, vars, t
|
|
|
18442
19155
|
* @param localRefs Index of the local references in the `consts` array.
|
|
18443
19156
|
* @param localRefExtractor A function which extracts local-refs values from the template.
|
|
18444
19157
|
* Defaults to the current element associated with the local-ref.
|
|
18445
|
-
*
|
|
18446
|
-
* @codeGenApi
|
|
18447
19158
|
*/
|
|
18448
|
-
function
|
|
18449
|
-
const lView = getLView();
|
|
18450
|
-
const tView = getTView();
|
|
19159
|
+
function declareTemplate(declarationLView, declarationTView, index, templateFn, decls, vars, tagName, attrs, localRefsIndex, localRefExtractor) {
|
|
18451
19160
|
const adjustedIndex = index + HEADER_OFFSET;
|
|
18452
|
-
const tNode =
|
|
18453
|
-
|
|
19161
|
+
const tNode = declarationTView.firstCreatePass ?
|
|
19162
|
+
templateFirstCreatePass(adjustedIndex, declarationTView, declarationLView, templateFn, decls, vars, tagName, attrs, localRefsIndex) :
|
|
19163
|
+
declarationTView.data[adjustedIndex];
|
|
18454
19164
|
setCurrentTNode(tNode, false);
|
|
18455
|
-
const comment = _locateOrCreateContainerAnchor(
|
|
19165
|
+
const comment = _locateOrCreateContainerAnchor(declarationTView, declarationLView, tNode, index);
|
|
18456
19166
|
if (wasLastNodeCreated()) {
|
|
18457
|
-
appendChild(
|
|
19167
|
+
appendChild(declarationTView, declarationLView, comment, tNode);
|
|
18458
19168
|
}
|
|
18459
|
-
attachPatchData(comment,
|
|
18460
|
-
const lContainer = createLContainer(comment,
|
|
18461
|
-
|
|
18462
|
-
addToViewTree(
|
|
19169
|
+
attachPatchData(comment, declarationLView);
|
|
19170
|
+
const lContainer = createLContainer(comment, declarationLView, comment, tNode);
|
|
19171
|
+
declarationLView[adjustedIndex] = lContainer;
|
|
19172
|
+
addToViewTree(declarationLView, lContainer);
|
|
18463
19173
|
// If hydration is enabled, looks up dehydrated views in the DOM
|
|
18464
19174
|
// using hydration annotation info and stores those views on LContainer.
|
|
18465
19175
|
// In client-only mode, this function is a noop.
|
|
18466
|
-
populateDehydratedViewsInLContainer(lContainer, tNode,
|
|
19176
|
+
populateDehydratedViewsInLContainer(lContainer, tNode, declarationLView);
|
|
18467
19177
|
if (isDirectiveHost(tNode)) {
|
|
18468
|
-
createDirectivesInstances(
|
|
19178
|
+
createDirectivesInstances(declarationTView, declarationLView, tNode);
|
|
18469
19179
|
}
|
|
18470
19180
|
if (localRefsIndex != null) {
|
|
18471
|
-
saveResolvedLocalsInData(
|
|
19181
|
+
saveResolvedLocalsInData(declarationLView, tNode, localRefExtractor);
|
|
18472
19182
|
}
|
|
19183
|
+
return tNode;
|
|
19184
|
+
}
|
|
19185
|
+
/**
|
|
19186
|
+
* Creates an LContainer for an ng-template (dynamically-inserted view), e.g.
|
|
19187
|
+
*
|
|
19188
|
+
* <ng-template #foo>
|
|
19189
|
+
* <div></div>
|
|
19190
|
+
* </ng-template>
|
|
19191
|
+
*
|
|
19192
|
+
* @param index The index of the container in the data array
|
|
19193
|
+
* @param templateFn Inline template
|
|
19194
|
+
* @param decls The number of nodes, local refs, and pipes for this template
|
|
19195
|
+
* @param vars The number of bindings for this template
|
|
19196
|
+
* @param tagName The name of the container element, if applicable
|
|
19197
|
+
* @param attrsIndex Index of template attributes in the `consts` array.
|
|
19198
|
+
* @param localRefs Index of the local references in the `consts` array.
|
|
19199
|
+
* @param localRefExtractor A function which extracts local-refs values from the template.
|
|
19200
|
+
* Defaults to the current element associated with the local-ref.
|
|
19201
|
+
*
|
|
19202
|
+
* @codeGenApi
|
|
19203
|
+
*/
|
|
19204
|
+
function ɵɵtemplate(index, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex, localRefExtractor) {
|
|
19205
|
+
const lView = getLView();
|
|
19206
|
+
const tView = getTView();
|
|
19207
|
+
const attrs = getConstant(tView.consts, attrsIndex);
|
|
19208
|
+
declareTemplate(lView, tView, index, templateFn, decls, vars, tagName, attrs, localRefsIndex, localRefExtractor);
|
|
18473
19209
|
return ɵɵtemplate;
|
|
18474
19210
|
}
|
|
18475
19211
|
let _locateOrCreateContainerAnchor = createContainerAnchorImpl;
|
|
@@ -19363,7 +20099,7 @@ function ɵɵdefer(index, primaryTmplIndex, dependencyResolverFn, loadingTmplInd
|
|
|
19363
20099
|
const lView = getLView();
|
|
19364
20100
|
const tView = getTView();
|
|
19365
20101
|
const adjustedIndex = index + HEADER_OFFSET;
|
|
19366
|
-
|
|
20102
|
+
const tNode = declareTemplate(lView, tView, index, null, 0, 0);
|
|
19367
20103
|
if (tView.firstCreatePass) {
|
|
19368
20104
|
performanceMarkFeature('NgDefer');
|
|
19369
20105
|
const tDetails = {
|
|
@@ -19381,7 +20117,6 @@ function ɵɵdefer(index, primaryTmplIndex, dependencyResolverFn, loadingTmplInd
|
|
|
19381
20117
|
enableTimerScheduling?.(tView, tDetails, placeholderConfigIndex, loadingConfigIndex);
|
|
19382
20118
|
setTDeferBlockDetails(tView, adjustedIndex, tDetails);
|
|
19383
20119
|
}
|
|
19384
|
-
const tNode = getCurrentTNode();
|
|
19385
20120
|
const lContainer = lView[adjustedIndex];
|
|
19386
20121
|
// If hydration is enabled, looks up dehydrated views in the DOM
|
|
19387
20122
|
// using hydration annotation info and stores those views on LContainer.
|
|
@@ -19672,6 +20407,14 @@ function renderDeferBlockState(newState, tNode, lContainer, skipTimerScheduling
|
|
|
19672
20407
|
}
|
|
19673
20408
|
}
|
|
19674
20409
|
}
|
|
20410
|
+
/**
|
|
20411
|
+
* Detects whether an injector is an instance of a `ChainedInjector`,
|
|
20412
|
+
* created based on the `OutletInjector`.
|
|
20413
|
+
*/
|
|
20414
|
+
function isRouterOutletInjector(currentInjector) {
|
|
20415
|
+
return (currentInjector instanceof ChainedInjector) &&
|
|
20416
|
+
(currentInjector.injector.__ngOutletInjector);
|
|
20417
|
+
}
|
|
19675
20418
|
/**
|
|
19676
20419
|
* Applies changes to the DOM to reflect a given state.
|
|
19677
20420
|
*/
|
|
@@ -19698,14 +20441,20 @@ function applyDeferBlockState(newState, lDetails, lContainer, tNode, hostLView)
|
|
|
19698
20441
|
const providers = tDetails.providers;
|
|
19699
20442
|
if (providers && providers.length > 0) {
|
|
19700
20443
|
const parentInjector = hostLView[INJECTOR];
|
|
19701
|
-
|
|
19702
|
-
|
|
19703
|
-
|
|
19704
|
-
|
|
20444
|
+
// Note: we have a special case for Router's `OutletInjector`,
|
|
20445
|
+
// since it's not an instance of the `EnvironmentInjector`, so
|
|
20446
|
+
// we can't inject it. Once the `OutletInjector` is replaced
|
|
20447
|
+
// with the `EnvironmentInjector` in Router's code, this special
|
|
20448
|
+
// handling can be removed.
|
|
20449
|
+
const parentEnvInjector = isRouterOutletInjector(parentInjector) ?
|
|
20450
|
+
parentInjector :
|
|
20451
|
+
parentInjector.get(EnvironmentInjector);
|
|
20452
|
+
injector = parentEnvInjector.get(CachedInjectorService)
|
|
20453
|
+
.getOrCreateInjector(tDetails, parentEnvInjector, providers, ngDevMode ? 'DeferBlock Injector' : '');
|
|
19705
20454
|
}
|
|
19706
20455
|
}
|
|
19707
20456
|
const dehydratedView = findMatchingDehydratedView(lContainer, activeBlockTNode.tView.ssrId);
|
|
19708
|
-
const embeddedLView = createAndRenderEmbeddedLView(hostLView, activeBlockTNode, null, { dehydratedView,
|
|
20457
|
+
const embeddedLView = createAndRenderEmbeddedLView(hostLView, activeBlockTNode, null, { dehydratedView, injector });
|
|
19709
20458
|
addLViewToLContainer(lContainer, embeddedLView, viewIndex, shouldAddViewToDom(activeBlockTNode, dehydratedView));
|
|
19710
20459
|
markViewDirty(embeddedLView);
|
|
19711
20460
|
}
|
|
@@ -22669,41 +23418,47 @@ class UniqueValueMultiKeyMap {
|
|
|
22669
23418
|
* built-in "if" and "switch". On the high level this instruction is responsible for adding and
|
|
22670
23419
|
* removing views selected by a conditional expression.
|
|
22671
23420
|
*
|
|
22672
|
-
* @param containerIndex index of a container in a host view (indexed from HEADER_OFFSET) where
|
|
22673
|
-
* conditional views should be inserted.
|
|
22674
23421
|
* @param matchingTemplateIndex index of a template TNode representing a conditional view to be
|
|
22675
23422
|
* inserted; -1 represents a special case when there is no view to insert.
|
|
22676
23423
|
* @codeGenApi
|
|
22677
23424
|
*/
|
|
22678
23425
|
function ɵɵconditional(containerIndex, matchingTemplateIndex, value) {
|
|
23426
|
+
// TODO: we could remove the containerIndex argument to this instruction now (!)
|
|
22679
23427
|
performanceMarkFeature('NgControlFlow');
|
|
22680
23428
|
const hostLView = getLView();
|
|
22681
23429
|
const bindingIndex = nextBindingIndex();
|
|
22682
|
-
const
|
|
23430
|
+
const prevMatchingTemplateIndex = hostLView[bindingIndex] !== NO_CHANGE ? hostLView[bindingIndex] : -1;
|
|
23431
|
+
const prevContainer = prevMatchingTemplateIndex !== -1 ?
|
|
23432
|
+
getLContainer(hostLView, HEADER_OFFSET + prevMatchingTemplateIndex) :
|
|
23433
|
+
undefined;
|
|
22683
23434
|
const viewInContainerIdx = 0;
|
|
22684
23435
|
if (bindingUpdated(hostLView, bindingIndex, matchingTemplateIndex)) {
|
|
22685
23436
|
const prevConsumer = setActiveConsumer$1(null);
|
|
22686
23437
|
try {
|
|
22687
23438
|
// The index of the view to show changed - remove the previously displayed one
|
|
22688
23439
|
// (it is a noop if there are no active views in a container).
|
|
22689
|
-
|
|
23440
|
+
if (prevContainer !== undefined) {
|
|
23441
|
+
removeLViewFromLContainer(prevContainer, viewInContainerIdx);
|
|
23442
|
+
}
|
|
22690
23443
|
// Index -1 is a special case where none of the conditions evaluates to
|
|
22691
23444
|
// a truthy value and as the consequence we've got no view to show.
|
|
22692
23445
|
if (matchingTemplateIndex !== -1) {
|
|
22693
|
-
const
|
|
22694
|
-
const
|
|
23446
|
+
const nextLContainerIndex = HEADER_OFFSET + matchingTemplateIndex;
|
|
23447
|
+
const nextContainer = getLContainer(hostLView, nextLContainerIndex);
|
|
23448
|
+
const templateTNode = getExistingTNode(hostLView[TVIEW], nextLContainerIndex);
|
|
23449
|
+
const dehydratedView = findMatchingDehydratedView(nextContainer, templateTNode.tView.ssrId);
|
|
22695
23450
|
const embeddedLView = createAndRenderEmbeddedLView(hostLView, templateTNode, value, { dehydratedView });
|
|
22696
|
-
addLViewToLContainer(
|
|
23451
|
+
addLViewToLContainer(nextContainer, embeddedLView, viewInContainerIdx, shouldAddViewToDom(templateTNode, dehydratedView));
|
|
22697
23452
|
}
|
|
22698
23453
|
}
|
|
22699
23454
|
finally {
|
|
22700
23455
|
setActiveConsumer$1(prevConsumer);
|
|
22701
23456
|
}
|
|
22702
23457
|
}
|
|
22703
|
-
else {
|
|
23458
|
+
else if (prevContainer !== undefined) {
|
|
22704
23459
|
// We might keep displaying the same template but the actual value of the expression could have
|
|
22705
23460
|
// changed - re-bind in context.
|
|
22706
|
-
const lView = getLViewFromLContainer(
|
|
23461
|
+
const lView = getLViewFromLContainer(prevContainer, viewInContainerIdx);
|
|
22707
23462
|
if (lView !== undefined) {
|
|
22708
23463
|
lView[CONTEXT] = value;
|
|
22709
23464
|
}
|
|
@@ -22777,6 +23532,8 @@ function ɵɵrepeaterCreate(index, templateFn, decls, vars, tagName, attrsIndex,
|
|
|
22777
23532
|
performanceMarkFeature('NgControlFlow');
|
|
22778
23533
|
ngDevMode &&
|
|
22779
23534
|
assertFunction(trackByFn, `A track expression must be a function, was ${typeof trackByFn} instead.`);
|
|
23535
|
+
const lView = getLView();
|
|
23536
|
+
const tView = getTView();
|
|
22780
23537
|
const hasEmptyBlock = emptyTemplateFn !== undefined;
|
|
22781
23538
|
const hostLView = getLView();
|
|
22782
23539
|
const boundTrackBy = trackByUsesComponentInstance ?
|
|
@@ -22786,13 +23543,13 @@ function ɵɵrepeaterCreate(index, templateFn, decls, vars, tagName, attrsIndex,
|
|
|
22786
23543
|
trackByFn;
|
|
22787
23544
|
const metadata = new RepeaterMetadata(hasEmptyBlock, boundTrackBy);
|
|
22788
23545
|
hostLView[HEADER_OFFSET + index] = metadata;
|
|
22789
|
-
|
|
23546
|
+
declareTemplate(lView, tView, index + 1, templateFn, decls, vars, tagName, getConstant(tView.consts, attrsIndex));
|
|
22790
23547
|
if (hasEmptyBlock) {
|
|
22791
23548
|
ngDevMode &&
|
|
22792
23549
|
assertDefined(emptyDecls, 'Missing number of declarations for the empty repeater block.');
|
|
22793
23550
|
ngDevMode &&
|
|
22794
23551
|
assertDefined(emptyVars, 'Missing number of bindings for the empty repeater block.');
|
|
22795
|
-
|
|
23552
|
+
declareTemplate(lView, tView, index + 2, emptyTemplateFn, emptyDecls, emptyVars, emptyTagName, getConstant(tView.consts, emptyAttrsIndex));
|
|
22796
23553
|
}
|
|
22797
23554
|
}
|
|
22798
23555
|
class LiveCollectionLContainerImpl extends LiveCollection {
|
|
@@ -23547,242 +24304,6 @@ function getLocaleId() {
|
|
|
23547
24304
|
return LOCALE_ID$1;
|
|
23548
24305
|
}
|
|
23549
24306
|
|
|
23550
|
-
/**
|
|
23551
|
-
* Find a node in front of which `currentTNode` should be inserted (takes i18n into account).
|
|
23552
|
-
*
|
|
23553
|
-
* This method determines the `RNode` in front of which we should insert the `currentRNode`. This
|
|
23554
|
-
* takes `TNode.insertBeforeIndex` into account.
|
|
23555
|
-
*
|
|
23556
|
-
* @param parentTNode parent `TNode`
|
|
23557
|
-
* @param currentTNode current `TNode` (The node which we would like to insert into the DOM)
|
|
23558
|
-
* @param lView current `LView`
|
|
23559
|
-
*/
|
|
23560
|
-
function getInsertInFrontOfRNodeWithI18n(parentTNode, currentTNode, lView) {
|
|
23561
|
-
const tNodeInsertBeforeIndex = currentTNode.insertBeforeIndex;
|
|
23562
|
-
const insertBeforeIndex = Array.isArray(tNodeInsertBeforeIndex) ? tNodeInsertBeforeIndex[0] : tNodeInsertBeforeIndex;
|
|
23563
|
-
if (insertBeforeIndex === null) {
|
|
23564
|
-
return getInsertInFrontOfRNodeWithNoI18n(parentTNode, currentTNode, lView);
|
|
23565
|
-
}
|
|
23566
|
-
else {
|
|
23567
|
-
ngDevMode && assertIndexInRange(lView, insertBeforeIndex);
|
|
23568
|
-
return unwrapRNode(lView[insertBeforeIndex]);
|
|
23569
|
-
}
|
|
23570
|
-
}
|
|
23571
|
-
/**
|
|
23572
|
-
* Process `TNode.insertBeforeIndex` by adding i18n text nodes.
|
|
23573
|
-
*
|
|
23574
|
-
* See `TNode.insertBeforeIndex`
|
|
23575
|
-
*/
|
|
23576
|
-
function processI18nInsertBefore(renderer, childTNode, lView, childRNode, parentRElement) {
|
|
23577
|
-
const tNodeInsertBeforeIndex = childTNode.insertBeforeIndex;
|
|
23578
|
-
if (Array.isArray(tNodeInsertBeforeIndex)) {
|
|
23579
|
-
// An array indicates that there are i18n nodes that need to be added as children of this
|
|
23580
|
-
// `childRNode`. These i18n nodes were created before this `childRNode` was available and so
|
|
23581
|
-
// only now can be added. The first element of the array is the normal index where we should
|
|
23582
|
-
// insert the `childRNode`. Additional elements are the extra nodes to be added as children of
|
|
23583
|
-
// `childRNode`.
|
|
23584
|
-
ngDevMode && assertDomNode(childRNode);
|
|
23585
|
-
let i18nParent = childRNode;
|
|
23586
|
-
let anchorRNode = null;
|
|
23587
|
-
if (!(childTNode.type & 3 /* TNodeType.AnyRNode */)) {
|
|
23588
|
-
anchorRNode = i18nParent;
|
|
23589
|
-
i18nParent = parentRElement;
|
|
23590
|
-
}
|
|
23591
|
-
if (i18nParent !== null && childTNode.componentOffset === -1) {
|
|
23592
|
-
for (let i = 1; i < tNodeInsertBeforeIndex.length; i++) {
|
|
23593
|
-
// No need to `unwrapRNode` because all of the indexes point to i18n text nodes.
|
|
23594
|
-
// see `assertDomNode` below.
|
|
23595
|
-
const i18nChild = lView[tNodeInsertBeforeIndex[i]];
|
|
23596
|
-
nativeInsertBefore(renderer, i18nParent, i18nChild, anchorRNode, false);
|
|
23597
|
-
}
|
|
23598
|
-
}
|
|
23599
|
-
}
|
|
23600
|
-
}
|
|
23601
|
-
|
|
23602
|
-
/**
|
|
23603
|
-
* Add `tNode` to `previousTNodes` list and update relevant `TNode`s in `previousTNodes` list
|
|
23604
|
-
* `tNode.insertBeforeIndex`.
|
|
23605
|
-
*
|
|
23606
|
-
* Things to keep in mind:
|
|
23607
|
-
* 1. All i18n text nodes are encoded as `TNodeType.Element` and are created eagerly by the
|
|
23608
|
-
* `ɵɵi18nStart` instruction.
|
|
23609
|
-
* 2. All `TNodeType.Placeholder` `TNodes` are elements which will be created later by
|
|
23610
|
-
* `ɵɵelementStart` instruction.
|
|
23611
|
-
* 3. `ɵɵelementStart` instruction will create `TNode`s in the ascending `TNode.index` order. (So a
|
|
23612
|
-
* smaller index `TNode` is guaranteed to be created before a larger one)
|
|
23613
|
-
*
|
|
23614
|
-
* We use the above three invariants to determine `TNode.insertBeforeIndex`.
|
|
23615
|
-
*
|
|
23616
|
-
* In an ideal world `TNode.insertBeforeIndex` would always be `TNode.next.index`. However,
|
|
23617
|
-
* this will not work because `TNode.next.index` may be larger than `TNode.index` which means that
|
|
23618
|
-
* the next node is not yet created and therefore we can't insert in front of it.
|
|
23619
|
-
*
|
|
23620
|
-
* Rule1: `TNode.insertBeforeIndex = null` if `TNode.next === null` (Initial condition, as we don't
|
|
23621
|
-
* know if there will be further `TNode`s inserted after.)
|
|
23622
|
-
* Rule2: If `previousTNode` is created after the `tNode` being inserted, then
|
|
23623
|
-
* `previousTNode.insertBeforeNode = tNode.index` (So when a new `tNode` is added we check
|
|
23624
|
-
* previous to see if we can update its `insertBeforeTNode`)
|
|
23625
|
-
*
|
|
23626
|
-
* See `TNode.insertBeforeIndex` for more context.
|
|
23627
|
-
*
|
|
23628
|
-
* @param previousTNodes A list of previous TNodes so that we can easily traverse `TNode`s in
|
|
23629
|
-
* reverse order. (If `TNode` would have `previous` this would not be necessary.)
|
|
23630
|
-
* @param newTNode A TNode to add to the `previousTNodes` list.
|
|
23631
|
-
*/
|
|
23632
|
-
function addTNodeAndUpdateInsertBeforeIndex(previousTNodes, newTNode) {
|
|
23633
|
-
// Start with Rule1
|
|
23634
|
-
ngDevMode &&
|
|
23635
|
-
assertEqual(newTNode.insertBeforeIndex, null, 'We expect that insertBeforeIndex is not set');
|
|
23636
|
-
previousTNodes.push(newTNode);
|
|
23637
|
-
if (previousTNodes.length > 1) {
|
|
23638
|
-
for (let i = previousTNodes.length - 2; i >= 0; i--) {
|
|
23639
|
-
const existingTNode = previousTNodes[i];
|
|
23640
|
-
// Text nodes are created eagerly and so they don't need their `indexBeforeIndex` updated.
|
|
23641
|
-
// It is safe to ignore them.
|
|
23642
|
-
if (!isI18nText(existingTNode)) {
|
|
23643
|
-
if (isNewTNodeCreatedBefore(existingTNode, newTNode) &&
|
|
23644
|
-
getInsertBeforeIndex(existingTNode) === null) {
|
|
23645
|
-
// If it was created before us in time, (and it does not yet have `insertBeforeIndex`)
|
|
23646
|
-
// then add the `insertBeforeIndex`.
|
|
23647
|
-
setInsertBeforeIndex(existingTNode, newTNode.index);
|
|
23648
|
-
}
|
|
23649
|
-
}
|
|
23650
|
-
}
|
|
23651
|
-
}
|
|
23652
|
-
}
|
|
23653
|
-
function isI18nText(tNode) {
|
|
23654
|
-
return !(tNode.type & 64 /* TNodeType.Placeholder */);
|
|
23655
|
-
}
|
|
23656
|
-
function isNewTNodeCreatedBefore(existingTNode, newTNode) {
|
|
23657
|
-
return isI18nText(newTNode) || existingTNode.index > newTNode.index;
|
|
23658
|
-
}
|
|
23659
|
-
function getInsertBeforeIndex(tNode) {
|
|
23660
|
-
const index = tNode.insertBeforeIndex;
|
|
23661
|
-
return Array.isArray(index) ? index[0] : index;
|
|
23662
|
-
}
|
|
23663
|
-
function setInsertBeforeIndex(tNode, value) {
|
|
23664
|
-
const index = tNode.insertBeforeIndex;
|
|
23665
|
-
if (Array.isArray(index)) {
|
|
23666
|
-
// Array is stored if we have to insert child nodes. See `TNode.insertBeforeIndex`
|
|
23667
|
-
index[0] = value;
|
|
23668
|
-
}
|
|
23669
|
-
else {
|
|
23670
|
-
setI18nHandling(getInsertInFrontOfRNodeWithI18n, processI18nInsertBefore);
|
|
23671
|
-
tNode.insertBeforeIndex = value;
|
|
23672
|
-
}
|
|
23673
|
-
}
|
|
23674
|
-
|
|
23675
|
-
/**
|
|
23676
|
-
* Retrieve `TIcu` at a given `index`.
|
|
23677
|
-
*
|
|
23678
|
-
* The `TIcu` can be stored either directly (if it is nested ICU) OR
|
|
23679
|
-
* it is stored inside tho `TIcuContainer` if it is top level ICU.
|
|
23680
|
-
*
|
|
23681
|
-
* The reason for this is that the top level ICU need a `TNode` so that they are part of the render
|
|
23682
|
-
* tree, but nested ICU's have no TNode, because we don't know ahead of time if the nested ICU is
|
|
23683
|
-
* expressed (parent ICU may have selected a case which does not contain it.)
|
|
23684
|
-
*
|
|
23685
|
-
* @param tView Current `TView`.
|
|
23686
|
-
* @param index Index where the value should be read from.
|
|
23687
|
-
*/
|
|
23688
|
-
function getTIcu(tView, index) {
|
|
23689
|
-
const value = tView.data[index];
|
|
23690
|
-
if (value === null || typeof value === 'string')
|
|
23691
|
-
return null;
|
|
23692
|
-
if (ngDevMode &&
|
|
23693
|
-
!(value.hasOwnProperty('tView') || value.hasOwnProperty('currentCaseLViewIndex'))) {
|
|
23694
|
-
throwError('We expect to get \'null\'|\'TIcu\'|\'TIcuContainer\', but got: ' + value);
|
|
23695
|
-
}
|
|
23696
|
-
// Here the `value.hasOwnProperty('currentCaseLViewIndex')` is a polymorphic read as it can be
|
|
23697
|
-
// either TIcu or TIcuContainerNode. This is not ideal, but we still think it is OK because it
|
|
23698
|
-
// will be just two cases which fits into the browser inline cache (inline cache can take up to
|
|
23699
|
-
// 4)
|
|
23700
|
-
const tIcu = value.hasOwnProperty('currentCaseLViewIndex') ? value :
|
|
23701
|
-
value.value;
|
|
23702
|
-
ngDevMode && assertTIcu(tIcu);
|
|
23703
|
-
return tIcu;
|
|
23704
|
-
}
|
|
23705
|
-
/**
|
|
23706
|
-
* Store `TIcu` at a give `index`.
|
|
23707
|
-
*
|
|
23708
|
-
* The `TIcu` can be stored either directly (if it is nested ICU) OR
|
|
23709
|
-
* it is stored inside tho `TIcuContainer` if it is top level ICU.
|
|
23710
|
-
*
|
|
23711
|
-
* The reason for this is that the top level ICU need a `TNode` so that they are part of the render
|
|
23712
|
-
* tree, but nested ICU's have no TNode, because we don't know ahead of time if the nested ICU is
|
|
23713
|
-
* expressed (parent ICU may have selected a case which does not contain it.)
|
|
23714
|
-
*
|
|
23715
|
-
* @param tView Current `TView`.
|
|
23716
|
-
* @param index Index where the value should be stored at in `Tview.data`
|
|
23717
|
-
* @param tIcu The TIcu to store.
|
|
23718
|
-
*/
|
|
23719
|
-
function setTIcu(tView, index, tIcu) {
|
|
23720
|
-
const tNode = tView.data[index];
|
|
23721
|
-
ngDevMode &&
|
|
23722
|
-
assertEqual(tNode === null || tNode.hasOwnProperty('tView'), true, 'We expect to get \'null\'|\'TIcuContainer\'');
|
|
23723
|
-
if (tNode === null) {
|
|
23724
|
-
tView.data[index] = tIcu;
|
|
23725
|
-
}
|
|
23726
|
-
else {
|
|
23727
|
-
ngDevMode && assertTNodeType(tNode, 32 /* TNodeType.Icu */);
|
|
23728
|
-
tNode.value = tIcu;
|
|
23729
|
-
}
|
|
23730
|
-
}
|
|
23731
|
-
/**
|
|
23732
|
-
* Set `TNode.insertBeforeIndex` taking the `Array` into account.
|
|
23733
|
-
*
|
|
23734
|
-
* See `TNode.insertBeforeIndex`
|
|
23735
|
-
*/
|
|
23736
|
-
function setTNodeInsertBeforeIndex(tNode, index) {
|
|
23737
|
-
ngDevMode && assertTNode(tNode);
|
|
23738
|
-
let insertBeforeIndex = tNode.insertBeforeIndex;
|
|
23739
|
-
if (insertBeforeIndex === null) {
|
|
23740
|
-
setI18nHandling(getInsertInFrontOfRNodeWithI18n, processI18nInsertBefore);
|
|
23741
|
-
insertBeforeIndex = tNode.insertBeforeIndex =
|
|
23742
|
-
[null /* may be updated to number later */, index];
|
|
23743
|
-
}
|
|
23744
|
-
else {
|
|
23745
|
-
assertEqual(Array.isArray(insertBeforeIndex), true, 'Expecting array here');
|
|
23746
|
-
insertBeforeIndex.push(index);
|
|
23747
|
-
}
|
|
23748
|
-
}
|
|
23749
|
-
/**
|
|
23750
|
-
* Create `TNode.type=TNodeType.Placeholder` node.
|
|
23751
|
-
*
|
|
23752
|
-
* See `TNodeType.Placeholder` for more information.
|
|
23753
|
-
*/
|
|
23754
|
-
function createTNodePlaceholder(tView, previousTNodes, index) {
|
|
23755
|
-
const tNode = createTNodeAtIndex(tView, index, 64 /* TNodeType.Placeholder */, null, null);
|
|
23756
|
-
addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tNode);
|
|
23757
|
-
return tNode;
|
|
23758
|
-
}
|
|
23759
|
-
/**
|
|
23760
|
-
* Returns current ICU case.
|
|
23761
|
-
*
|
|
23762
|
-
* ICU cases are stored as index into the `TIcu.cases`.
|
|
23763
|
-
* At times it is necessary to communicate that the ICU case just switched and that next ICU update
|
|
23764
|
-
* should update all bindings regardless of the mask. In such a case the we store negative numbers
|
|
23765
|
-
* for cases which have just been switched. This function removes the negative flag.
|
|
23766
|
-
*/
|
|
23767
|
-
function getCurrentICUCaseIndex(tIcu, lView) {
|
|
23768
|
-
const currentCase = lView[tIcu.currentCaseLViewIndex];
|
|
23769
|
-
return currentCase === null ? currentCase : (currentCase < 0 ? ~currentCase : currentCase);
|
|
23770
|
-
}
|
|
23771
|
-
function getParentFromIcuCreateOpCode(mergedCode) {
|
|
23772
|
-
return mergedCode >>> 17 /* IcuCreateOpCode.SHIFT_PARENT */;
|
|
23773
|
-
}
|
|
23774
|
-
function getRefFromIcuCreateOpCode(mergedCode) {
|
|
23775
|
-
return (mergedCode & 131070 /* IcuCreateOpCode.MASK_REF */) >>> 1 /* IcuCreateOpCode.SHIFT_REF */;
|
|
23776
|
-
}
|
|
23777
|
-
function getInstructionFromIcuCreateOpCode(mergedCode) {
|
|
23778
|
-
return mergedCode & 1 /* IcuCreateOpCode.MASK_INSTRUCTION */;
|
|
23779
|
-
}
|
|
23780
|
-
function icuCreateOpCode(opCode, parentIdx, refIdx) {
|
|
23781
|
-
ngDevMode && assertGreaterThanOrEqual(parentIdx, 0, 'Missing parent index');
|
|
23782
|
-
ngDevMode && assertGreaterThan(refIdx, 0, 'Missing ref index');
|
|
23783
|
-
return opCode | parentIdx << 17 /* IcuCreateOpCode.SHIFT_PARENT */ | refIdx << 1 /* IcuCreateOpCode.SHIFT_REF */;
|
|
23784
|
-
}
|
|
23785
|
-
|
|
23786
24307
|
/**
|
|
23787
24308
|
* Keep track of which input bindings in `ɵɵi18nExp` have changed.
|
|
23788
24309
|
*
|
|
@@ -23845,9 +24366,27 @@ let _locateOrCreateNode = (lView, index, textOrName, nodeType) => {
|
|
|
23845
24366
|
return createNodeWithoutHydration(lView, textOrName, nodeType);
|
|
23846
24367
|
};
|
|
23847
24368
|
function locateOrCreateNodeImpl(lView, index, textOrName, nodeType) {
|
|
23848
|
-
|
|
23849
|
-
|
|
23850
|
-
|
|
24369
|
+
const hydrationInfo = lView[HYDRATION];
|
|
24370
|
+
const noOffsetIndex = index - HEADER_OFFSET;
|
|
24371
|
+
const isNodeCreationMode = !isI18nHydrationSupportEnabled() || !hydrationInfo ||
|
|
24372
|
+
isInSkipHydrationBlock$1() || isDisconnectedNode$1(hydrationInfo, noOffsetIndex);
|
|
24373
|
+
lastNodeWasCreated(isNodeCreationMode);
|
|
24374
|
+
if (isNodeCreationMode) {
|
|
24375
|
+
return createNodeWithoutHydration(lView, textOrName, nodeType);
|
|
24376
|
+
}
|
|
24377
|
+
const native = locateI18nRNodeByIndex(hydrationInfo, noOffsetIndex);
|
|
24378
|
+
// TODO: Improve error handling
|
|
24379
|
+
//
|
|
24380
|
+
// Other hydration paths use validateMatchingNode() in order to provide
|
|
24381
|
+
// detailed information in development mode about the expected DOM.
|
|
24382
|
+
// However, not every node in an i18n block has a TNode. Instead, we
|
|
24383
|
+
// need to be able to use the AST to generate a similar message.
|
|
24384
|
+
ngDevMode && assertDefined(native, 'expected native element');
|
|
24385
|
+
ngDevMode && assertEqual(native.nodeType, nodeType, 'expected matching nodeType');
|
|
24386
|
+
ngDevMode && nodeType === Node.ELEMENT_NODE &&
|
|
24387
|
+
assertEqual(native.tagName.toLowerCase(), textOrName.toLowerCase(), 'expecting matching tagName');
|
|
24388
|
+
ngDevMode && markRNodeAsClaimedByHydration(native);
|
|
24389
|
+
return native;
|
|
23851
24390
|
}
|
|
23852
24391
|
function enableLocateOrCreateI18nNodeImpl() {
|
|
23853
24392
|
_locateOrCreateNode = locateOrCreateNodeImpl;
|
|
@@ -24139,6 +24678,7 @@ function applyIcuSwitchCase(tView, tIcu, lView, value) {
|
|
|
24139
24678
|
ngDevMode && assertDomNode(anchorRNode);
|
|
24140
24679
|
applyMutableOpCodes(tView, tIcu.create[caseIndex], lView, anchorRNode);
|
|
24141
24680
|
}
|
|
24681
|
+
claimDehydratedIcuCase(lView, tIcu.anchorIdx, caseIndex);
|
|
24142
24682
|
}
|
|
24143
24683
|
}
|
|
24144
24684
|
}
|
|
@@ -24789,9 +25329,6 @@ function countBindings(opCodes) {
|
|
|
24789
25329
|
function toMaskBit(bindingIndex) {
|
|
24790
25330
|
return 1 << Math.min(bindingIndex, 31);
|
|
24791
25331
|
}
|
|
24792
|
-
function isRootTemplateMessage(subTemplateIndex) {
|
|
24793
|
-
return subTemplateIndex === -1;
|
|
24794
|
-
}
|
|
24795
25332
|
/**
|
|
24796
25333
|
* Removes everything inside the sub-templates of a message.
|
|
24797
25334
|
*/
|
|
@@ -25295,6 +25832,7 @@ function ɵɵi18nStart(index, messageIndex, subTemplateIndex = -1) {
|
|
|
25295
25832
|
const insertInFrontOf = parentTNode && (parentTNode.type & 8 /* TNodeType.ElementContainer */) ?
|
|
25296
25833
|
lView[parentTNode.index] :
|
|
25297
25834
|
null;
|
|
25835
|
+
prepareI18nBlockForHydration(lView, adjustedIndex, parentTNode, subTemplateIndex);
|
|
25298
25836
|
applyCreateOpCodes(lView, tI18n.create, parentRNode, insertInFrontOf);
|
|
25299
25837
|
setInI18nBlock(true);
|
|
25300
25838
|
}
|
|
@@ -25737,30 +26275,52 @@ function ɵɵprojectionDef(projectionSlots) {
|
|
|
25737
26275
|
* Inserts previously re-distributed projected nodes. This instruction must be preceded by a call
|
|
25738
26276
|
* to the projectionDef instruction.
|
|
25739
26277
|
*
|
|
25740
|
-
* @param nodeIndex
|
|
25741
|
-
* @param selectorIndex
|
|
25742
|
-
*
|
|
25743
|
-
*
|
|
26278
|
+
* @param nodeIndex Index of the projection node.
|
|
26279
|
+
* @param selectorIndex Index of the slot selector.
|
|
26280
|
+
* - 0 when the selector is `*` (or unspecified as this is the default value),
|
|
26281
|
+
* - 1 based index of the selector from the {@link projectionDef}
|
|
26282
|
+
* @param attrs Static attributes set on the `ng-content` node.
|
|
26283
|
+
* @param fallbackTemplateFn Template function with fallback content.
|
|
26284
|
+
* Will be rendered if the slot is empty at runtime.
|
|
26285
|
+
* @param fallbackDecls Number of declarations in the fallback template.
|
|
26286
|
+
* @param fallbackVars Number of variables in the fallback template.
|
|
25744
26287
|
*
|
|
25745
26288
|
* @codeGenApi
|
|
25746
26289
|
*/
|
|
25747
|
-
function ɵɵprojection(nodeIndex, selectorIndex = 0, attrs) {
|
|
26290
|
+
function ɵɵprojection(nodeIndex, selectorIndex = 0, attrs, fallbackTemplateFn, fallbackDecls, fallbackVars) {
|
|
25748
26291
|
const lView = getLView();
|
|
25749
26292
|
const tView = getTView();
|
|
25750
26293
|
const tProjectionNode = getOrCreateTNode(tView, HEADER_OFFSET + nodeIndex, 16 /* TNodeType.Projection */, null, attrs || null);
|
|
25751
26294
|
// We can't use viewData[HOST_NODE] because projection nodes can be nested in embedded views.
|
|
25752
|
-
if (tProjectionNode.projection === null)
|
|
26295
|
+
if (tProjectionNode.projection === null) {
|
|
25753
26296
|
tProjectionNode.projection = selectorIndex;
|
|
25754
|
-
|
|
26297
|
+
}
|
|
26298
|
+
// `<ng-content>` has no content. Even if there's fallback
|
|
26299
|
+
// content, the fallback is shown next to it.
|
|
25755
26300
|
setCurrentTNodeAsNotParent();
|
|
25756
26301
|
const hydrationInfo = lView[HYDRATION];
|
|
25757
26302
|
const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1();
|
|
25758
|
-
|
|
26303
|
+
const componentHostNode = lView[DECLARATION_COMPONENT_VIEW][T_HOST];
|
|
26304
|
+
const isEmpty = componentHostNode.projection[tProjectionNode.projection] === null;
|
|
26305
|
+
if (isEmpty && fallbackTemplateFn) {
|
|
26306
|
+
insertFallbackContent(lView, tView, nodeIndex, fallbackTemplateFn, fallbackDecls, fallbackVars, attrs);
|
|
26307
|
+
}
|
|
26308
|
+
else if (isNodeCreationMode &&
|
|
25759
26309
|
(tProjectionNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
|
|
25760
26310
|
// re-distribution of projectable nodes is stored on a component's view level
|
|
25761
26311
|
applyProjection(tView, lView, tProjectionNode);
|
|
25762
26312
|
}
|
|
25763
26313
|
}
|
|
26314
|
+
/** Inserts the fallback content of a projection slot. Assumes there's no projected content. */
|
|
26315
|
+
function insertFallbackContent(lView, tView, projectionIndex, templateFn, decls, vars, attrs) {
|
|
26316
|
+
const fallbackIndex = projectionIndex + 1;
|
|
26317
|
+
const fallbackTNode = declareTemplate(lView, tView, fallbackIndex, templateFn, decls, vars, null, attrs);
|
|
26318
|
+
const fallbackLContainer = lView[HEADER_OFFSET + fallbackIndex];
|
|
26319
|
+
ngDevMode && assertLContainer(fallbackLContainer);
|
|
26320
|
+
const dehydratedView = findMatchingDehydratedView(fallbackLContainer, fallbackTNode.tView.ssrId);
|
|
26321
|
+
const fallbackLView = createAndRenderEmbeddedLView(lView, fallbackTNode, undefined, { dehydratedView });
|
|
26322
|
+
addLViewToLContainer(fallbackLContainer, fallbackLView, 0, shouldAddViewToDom(fallbackTNode, dehydratedView));
|
|
26323
|
+
}
|
|
25764
26324
|
|
|
25765
26325
|
/**
|
|
25766
26326
|
*
|
|
@@ -29804,7 +30364,7 @@ class Version {
|
|
|
29804
30364
|
/**
|
|
29805
30365
|
* @publicApi
|
|
29806
30366
|
*/
|
|
29807
|
-
const VERSION = new Version('18.0.0-next.
|
|
30367
|
+
const VERSION = new Version('18.0.0-next.3');
|
|
29808
30368
|
|
|
29809
30369
|
class Console {
|
|
29810
30370
|
log(message) {
|
|
@@ -29987,7 +30547,7 @@ function handleInstanceCreatedByInjectorEvent(context, data) {
|
|
|
29987
30547
|
}
|
|
29988
30548
|
const environmentInjector = context.injector.get(EnvironmentInjector, null, { optional: true });
|
|
29989
30549
|
// Standalone components should have an environment injector. If one cannot be
|
|
29990
|
-
// found we may be in a test case for low level functionality that did not
|
|
30550
|
+
// found we may be in a test case for low level functionality that did not explicitly
|
|
29991
30551
|
// setup this injector. In those cases, we simply ignore this event.
|
|
29992
30552
|
if (environmentInjector === null) {
|
|
29993
30553
|
return;
|
|
@@ -30566,8 +31126,11 @@ function getInjectorParent(injector) {
|
|
|
30566
31126
|
else if (injector instanceof NullInjector) {
|
|
30567
31127
|
return null;
|
|
30568
31128
|
}
|
|
31129
|
+
else if (injector instanceof ChainedInjector) {
|
|
31130
|
+
return injector.parentInjector;
|
|
31131
|
+
}
|
|
30569
31132
|
else {
|
|
30570
|
-
throwError('getInjectorParent only support injectors of type R3Injector, NodeInjector, NullInjector');
|
|
31133
|
+
throwError('getInjectorParent only support injectors of type R3Injector, NodeInjector, NullInjector, ChainedInjector');
|
|
30571
31134
|
}
|
|
30572
31135
|
const parentLocation = getParentInjectorLocation(tNode, lView);
|
|
30573
31136
|
if (hasParentInjector(parentLocation)) {
|
|
@@ -30584,7 +31147,7 @@ function getInjectorParent(injector) {
|
|
|
30584
31147
|
// todo(aleksanderbodurri): ideally nothing in packages/core should deal
|
|
30585
31148
|
// directly with router concerns. Refactor this so that we can make the jump from
|
|
30586
31149
|
// NodeInjector -> OutletInjector -> NodeInjector
|
|
30587
|
-
// without
|
|
31150
|
+
// without explicitly relying on types contracts from packages/router
|
|
30588
31151
|
const injectorParent = chainedInjector.injector?.parent;
|
|
30589
31152
|
if (injectorParent instanceof NodeInjector) {
|
|
30590
31153
|
return injectorParent;
|
|
@@ -30606,8 +31169,8 @@ function getModuleInjectorOfNodeInjector(injector) {
|
|
|
30606
31169
|
else {
|
|
30607
31170
|
throwError('getModuleInjectorOfNodeInjector must be called with a NodeInjector');
|
|
30608
31171
|
}
|
|
30609
|
-
const
|
|
30610
|
-
const moduleInjector =
|
|
31172
|
+
const inj = lView[INJECTOR];
|
|
31173
|
+
const moduleInjector = (inj instanceof ChainedInjector) ? inj.parentInjector : inj.parent;
|
|
30611
31174
|
if (!moduleInjector) {
|
|
30612
31175
|
throwError('NodeInjector must have some connection to the module injector tree');
|
|
30613
31176
|
}
|
|
@@ -30985,7 +31548,7 @@ function isSubscribable(obj) {
|
|
|
30985
31548
|
}
|
|
30986
31549
|
|
|
30987
31550
|
/**
|
|
30988
|
-
* A
|
|
31551
|
+
* A DI token that you can use to provide
|
|
30989
31552
|
* one or more initialization functions.
|
|
30990
31553
|
*
|
|
30991
31554
|
* The provided functions are injected at application startup and executed during
|
|
@@ -31169,7 +31732,7 @@ class ApplicationInitStatus {
|
|
|
31169
31732
|
}], () => [], null); })();
|
|
31170
31733
|
|
|
31171
31734
|
/**
|
|
31172
|
-
* A
|
|
31735
|
+
* A DI token that provides a set of callbacks to
|
|
31173
31736
|
* be called for every component that is bootstrapped.
|
|
31174
31737
|
*
|
|
31175
31738
|
* Each callback must take a `ComponentRef` instance and return nothing.
|
|
@@ -31336,6 +31899,7 @@ class ApplicationRef {
|
|
|
31336
31899
|
this._views = [];
|
|
31337
31900
|
this.internalErrorHandler = inject(INTERNAL_APPLICATION_ERROR_HANDLER);
|
|
31338
31901
|
this.afterRenderEffectManager = inject(AfterRenderEventManager);
|
|
31902
|
+
this.zonelessEnabled = inject(ZONELESS_ENABLED);
|
|
31339
31903
|
// Needed for ComponentFixture temporarily during migration of autoDetect behavior
|
|
31340
31904
|
// Eventually the hostView of the fixture should just attach to ApplicationRef.
|
|
31341
31905
|
this.externalTestViews = new Set();
|
|
@@ -31493,20 +32057,20 @@ class ApplicationRef {
|
|
|
31493
32057
|
const isFirstPass = runs === 0;
|
|
31494
32058
|
this.beforeRender.next(isFirstPass);
|
|
31495
32059
|
for (let { _lView, notifyErrorHandler } of this._views) {
|
|
31496
|
-
detectChangesInViewIfRequired(_lView, isFirstPass,
|
|
32060
|
+
detectChangesInViewIfRequired(_lView, notifyErrorHandler, isFirstPass, this.zonelessEnabled);
|
|
31497
32061
|
}
|
|
31498
32062
|
}
|
|
31499
32063
|
runs++;
|
|
31500
32064
|
afterRenderEffectManager.executeInternalCallbacks();
|
|
31501
32065
|
// If we have a newly dirty view after running internal callbacks, recheck the views again
|
|
31502
32066
|
// before running user-provided callbacks
|
|
31503
|
-
if ([...this.externalTestViews.keys(), ...this._views].some(({ _lView }) =>
|
|
32067
|
+
if ([...this.externalTestViews.keys(), ...this._views].some(({ _lView }) => requiresRefreshOrTraversal(_lView))) {
|
|
31504
32068
|
continue;
|
|
31505
32069
|
}
|
|
31506
32070
|
afterRenderEffectManager.execute();
|
|
31507
32071
|
// If after running all afterRender callbacks we have no more views that need to be refreshed,
|
|
31508
32072
|
// we can break out of the loop
|
|
31509
|
-
if (![...this.externalTestViews.keys(), ...this._views].some(({ _lView }) =>
|
|
32073
|
+
if (![...this.externalTestViews.keys(), ...this._views].some(({ _lView }) => requiresRefreshOrTraversal(_lView))) {
|
|
31510
32074
|
break;
|
|
31511
32075
|
}
|
|
31512
32076
|
}
|
|
@@ -31639,18 +32203,12 @@ function whenStable(applicationRef) {
|
|
|
31639
32203
|
applicationRef.onDestroy(() => whenStableStore?.delete(applicationRef));
|
|
31640
32204
|
return whenStablePromise;
|
|
31641
32205
|
}
|
|
31642
|
-
function detectChangesInViewIfRequired(lView, isFirstPass,
|
|
32206
|
+
function detectChangesInViewIfRequired(lView, notifyErrorHandler, isFirstPass, zonelessEnabled) {
|
|
31643
32207
|
// When re-checking, only check views which actually need it.
|
|
31644
|
-
if (!isFirstPass && !
|
|
32208
|
+
if (!isFirstPass && !requiresRefreshOrTraversal(lView)) {
|
|
31645
32209
|
return;
|
|
31646
32210
|
}
|
|
31647
|
-
|
|
31648
|
-
}
|
|
31649
|
-
function shouldRecheckView(view) {
|
|
31650
|
-
return requiresRefreshOrTraversal(view);
|
|
31651
|
-
}
|
|
31652
|
-
function detectChangesInView(lView, notifyErrorHandler, isFirstPass) {
|
|
31653
|
-
const mode = isFirstPass || lView[FLAGS] & 64 /* LViewFlags.Dirty */ ?
|
|
32211
|
+
const mode = (isFirstPass && !zonelessEnabled) ?
|
|
31654
32212
|
0 /* ChangeDetectionMode.Global */ :
|
|
31655
32213
|
1 /* ChangeDetectionMode.Targeted */;
|
|
31656
32214
|
detectChangesInternal(lView, notifyErrorHandler, mode);
|
|
@@ -31809,9 +32367,130 @@ function _lastDefined(args) {
|
|
|
31809
32367
|
return undefined;
|
|
31810
32368
|
}
|
|
31811
32369
|
|
|
32370
|
+
class ChangeDetectionSchedulerImpl {
|
|
32371
|
+
constructor() {
|
|
32372
|
+
this.appRef = inject(ApplicationRef);
|
|
32373
|
+
this.taskService = inject(PendingTasks);
|
|
32374
|
+
this.pendingRenderTaskId = null;
|
|
32375
|
+
this.shouldRefreshViews = false;
|
|
32376
|
+
this.ngZone = inject(NgZone);
|
|
32377
|
+
this.runningTick = false;
|
|
32378
|
+
this.cancelScheduledCallback = null;
|
|
32379
|
+
this.zonelessEnabled = inject(ZONELESS_ENABLED);
|
|
32380
|
+
this.disableScheduling = inject(ZONELESS_SCHEDULER_DISABLED, { optional: true }) ?? false;
|
|
32381
|
+
this.zoneIsDefined = typeof Zone !== 'undefined';
|
|
32382
|
+
// TODO(atscott): These conditions will need to change when zoneless is the default
|
|
32383
|
+
// Instead, they should flip to checking if ZoneJS scheduling is provided
|
|
32384
|
+
this.disableScheduling ||= !this.zonelessEnabled &&
|
|
32385
|
+
// NoopNgZone without enabling zoneless means no scheduling whatsoever
|
|
32386
|
+
(this.ngZone instanceof NoopNgZone ||
|
|
32387
|
+
// The same goes for the lack of Zone without enabling zoneless scheduling
|
|
32388
|
+
!this.zoneIsDefined);
|
|
32389
|
+
}
|
|
32390
|
+
notify(type = 0 /* NotificationType.RefreshViews */) {
|
|
32391
|
+
// When the only source of notification is an afterRender hook will skip straight to the hooks
|
|
32392
|
+
// rather than refreshing views in ApplicationRef.tick
|
|
32393
|
+
this.shouldRefreshViews ||= type === 0 /* NotificationType.RefreshViews */;
|
|
32394
|
+
if (!this.shouldScheduleTick()) {
|
|
32395
|
+
return;
|
|
32396
|
+
}
|
|
32397
|
+
this.pendingRenderTaskId = this.taskService.add();
|
|
32398
|
+
// TODO(atscott): This zone.root.run can maybe just be removed when we more
|
|
32399
|
+
// effectively get the unpatched versions of setTimeout and rAF (#55092)
|
|
32400
|
+
if (typeof Zone !== 'undefined' && Zone.root?.run) {
|
|
32401
|
+
Zone.root.run(() => {
|
|
32402
|
+
this.cancelScheduledCallback = scheduleCallback(() => {
|
|
32403
|
+
this.tick(this.shouldRefreshViews);
|
|
32404
|
+
});
|
|
32405
|
+
});
|
|
32406
|
+
}
|
|
32407
|
+
else {
|
|
32408
|
+
this.cancelScheduledCallback = scheduleCallback(() => {
|
|
32409
|
+
this.tick(this.shouldRefreshViews);
|
|
32410
|
+
});
|
|
32411
|
+
}
|
|
32412
|
+
}
|
|
32413
|
+
shouldScheduleTick() {
|
|
32414
|
+
if (this.disableScheduling) {
|
|
32415
|
+
return false;
|
|
32416
|
+
}
|
|
32417
|
+
// already scheduled or running
|
|
32418
|
+
if (this.pendingRenderTaskId !== null || this.runningTick) {
|
|
32419
|
+
return false;
|
|
32420
|
+
}
|
|
32421
|
+
// If we're inside the zone don't bother with scheduler. Zone will stabilize
|
|
32422
|
+
// eventually and run change detection.
|
|
32423
|
+
if (this.zoneIsDefined && NgZone.isInAngularZone()) {
|
|
32424
|
+
return false;
|
|
32425
|
+
}
|
|
32426
|
+
return true;
|
|
32427
|
+
}
|
|
32428
|
+
/**
|
|
32429
|
+
* Calls ApplicationRef._tick inside the `NgZone`.
|
|
32430
|
+
*
|
|
32431
|
+
* Calling `tick` directly runs change detection and cancels any change detection that had been
|
|
32432
|
+
* scheduled previously.
|
|
32433
|
+
*
|
|
32434
|
+
* @param shouldRefreshViews Passed directly to `ApplicationRef._tick` and skips straight to
|
|
32435
|
+
* render hooks when `false`.
|
|
32436
|
+
*/
|
|
32437
|
+
tick(shouldRefreshViews) {
|
|
32438
|
+
// When ngZone.run below exits, onMicrotaskEmpty may emit if the zone is
|
|
32439
|
+
// stable. We want to prevent double ticking so we track whether the tick is
|
|
32440
|
+
// already running and skip it if so.
|
|
32441
|
+
if (this.runningTick || this.appRef.destroyed) {
|
|
32442
|
+
return;
|
|
32443
|
+
}
|
|
32444
|
+
try {
|
|
32445
|
+
this.ngZone.run(() => {
|
|
32446
|
+
this.runningTick = true;
|
|
32447
|
+
this.appRef._tick(shouldRefreshViews);
|
|
32448
|
+
});
|
|
32449
|
+
}
|
|
32450
|
+
finally {
|
|
32451
|
+
this.cleanup();
|
|
32452
|
+
}
|
|
32453
|
+
}
|
|
32454
|
+
ngOnDestroy() {
|
|
32455
|
+
this.cleanup();
|
|
32456
|
+
}
|
|
32457
|
+
cleanup() {
|
|
32458
|
+
this.shouldRefreshViews = false;
|
|
32459
|
+
this.runningTick = false;
|
|
32460
|
+
this.cancelScheduledCallback?.();
|
|
32461
|
+
this.cancelScheduledCallback = null;
|
|
32462
|
+
// If this is the last task, the service will synchronously emit a stable
|
|
32463
|
+
// notification. If there is a subscriber that then acts in a way that
|
|
32464
|
+
// tries to notify the scheduler again, we need to be able to respond to
|
|
32465
|
+
// schedule a new change detection. Therefore, we should clear the task ID
|
|
32466
|
+
// before removing it from the pending tasks (or the tasks service should
|
|
32467
|
+
// not synchronously emit stable, similar to how Zone stableness only
|
|
32468
|
+
// happens if it's still stable after a microtask).
|
|
32469
|
+
if (this.pendingRenderTaskId !== null) {
|
|
32470
|
+
const taskId = this.pendingRenderTaskId;
|
|
32471
|
+
this.pendingRenderTaskId = null;
|
|
32472
|
+
this.taskService.remove(taskId);
|
|
32473
|
+
}
|
|
32474
|
+
}
|
|
32475
|
+
static { this.ɵfac = function ChangeDetectionSchedulerImpl_Factory(t) { return new (t || ChangeDetectionSchedulerImpl)(); }; }
|
|
32476
|
+
static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: ChangeDetectionSchedulerImpl, factory: ChangeDetectionSchedulerImpl.ɵfac, providedIn: 'root' }); }
|
|
32477
|
+
}
|
|
32478
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ChangeDetectionSchedulerImpl, [{
|
|
32479
|
+
type: Injectable,
|
|
32480
|
+
args: [{ providedIn: 'root' }]
|
|
32481
|
+
}], () => [], null); })();
|
|
32482
|
+
function provideZonelessChangeDetection() {
|
|
32483
|
+
return makeEnvironmentProviders([
|
|
32484
|
+
{ provide: ChangeDetectionScheduler, useExisting: ChangeDetectionSchedulerImpl },
|
|
32485
|
+
{ provide: NgZone, useClass: NoopNgZone },
|
|
32486
|
+
{ provide: ZONELESS_ENABLED, useValue: true },
|
|
32487
|
+
]);
|
|
32488
|
+
}
|
|
32489
|
+
|
|
31812
32490
|
class NgZoneChangeDetectionScheduler {
|
|
31813
32491
|
constructor() {
|
|
31814
32492
|
this.zone = inject(NgZone);
|
|
32493
|
+
this.changeDetectionScheduler = inject(ChangeDetectionScheduler, { optional: true });
|
|
31815
32494
|
this.applicationRef = inject(ApplicationRef);
|
|
31816
32495
|
}
|
|
31817
32496
|
initialize() {
|
|
@@ -31821,7 +32500,12 @@ class NgZoneChangeDetectionScheduler {
|
|
|
31821
32500
|
this._onMicrotaskEmptySubscription = this.zone.onMicrotaskEmpty.subscribe({
|
|
31822
32501
|
next: () => {
|
|
31823
32502
|
this.zone.run(() => {
|
|
31824
|
-
this.
|
|
32503
|
+
if (this.changeDetectionScheduler) {
|
|
32504
|
+
this.changeDetectionScheduler.tick(true /* shouldRefreshViews */);
|
|
32505
|
+
}
|
|
32506
|
+
else {
|
|
32507
|
+
this.applicationRef.tick();
|
|
32508
|
+
}
|
|
31825
32509
|
});
|
|
31826
32510
|
}
|
|
31827
32511
|
});
|
|
@@ -31841,7 +32525,24 @@ class NgZoneChangeDetectionScheduler {
|
|
|
31841
32525
|
* with the bootstrapModule API.
|
|
31842
32526
|
*/
|
|
31843
32527
|
const PROVIDED_NG_ZONE = new InjectionToken((typeof ngDevMode === 'undefined' || ngDevMode) ? 'provideZoneChangeDetection token' : '');
|
|
31844
|
-
|
|
32528
|
+
/**
|
|
32529
|
+
* Configures change detection scheduling when using ZoneJS.
|
|
32530
|
+
*/
|
|
32531
|
+
var SchedulingMode;
|
|
32532
|
+
(function (SchedulingMode) {
|
|
32533
|
+
/**
|
|
32534
|
+
* Change detection will run when the `NgZone.onMicrotaskEmpty` observable emits.
|
|
32535
|
+
* Change detection will also be scheduled to run whenever Angular is notified
|
|
32536
|
+
* of a change. This includes calling `ChangeDetectorRef.markForCheck`,
|
|
32537
|
+
* setting a `signal` value, and attaching a view.
|
|
32538
|
+
*/
|
|
32539
|
+
SchedulingMode[SchedulingMode["Hybrid"] = 0] = "Hybrid";
|
|
32540
|
+
/**
|
|
32541
|
+
* Change detection will only run when the `NgZone.onMicrotaskEmpty` observable emits.
|
|
32542
|
+
*/
|
|
32543
|
+
SchedulingMode[SchedulingMode["NgZoneOnly"] = 1] = "NgZoneOnly";
|
|
32544
|
+
})(SchedulingMode || (SchedulingMode = {}));
|
|
32545
|
+
function internalProvideZoneChangeDetection({ ngZoneFactory, schedulingMode }) {
|
|
31845
32546
|
return [
|
|
31846
32547
|
{ provide: NgZone, useFactory: ngZoneFactory },
|
|
31847
32548
|
{
|
|
@@ -31868,6 +32569,14 @@ function internalProvideZoneChangeDetection(ngZoneFactory) {
|
|
|
31868
32569
|
}
|
|
31869
32570
|
},
|
|
31870
32571
|
{ provide: INTERNAL_APPLICATION_ERROR_HANDLER, useFactory: ngZoneApplicationErrorHandlerFactory },
|
|
32572
|
+
// Always disable scheduler whenever explicitly disabled, even if Hybrid was specified elsewhere
|
|
32573
|
+
schedulingMode === SchedulingMode.NgZoneOnly ?
|
|
32574
|
+
{ provide: ZONELESS_SCHEDULER_DISABLED, useValue: true } :
|
|
32575
|
+
[],
|
|
32576
|
+
// Only provide scheduler when explicitly enabled
|
|
32577
|
+
schedulingMode === SchedulingMode.Hybrid ?
|
|
32578
|
+
{ provide: ChangeDetectionScheduler, useExisting: ChangeDetectionSchedulerImpl } :
|
|
32579
|
+
[],
|
|
31871
32580
|
];
|
|
31872
32581
|
}
|
|
31873
32582
|
function ngZoneApplicationErrorHandlerFactory() {
|
|
@@ -31896,7 +32605,8 @@ function ngZoneApplicationErrorHandlerFactory() {
|
|
|
31896
32605
|
* @see {@link NgZoneOptions}
|
|
31897
32606
|
*/
|
|
31898
32607
|
function provideZoneChangeDetection(options) {
|
|
31899
|
-
const
|
|
32608
|
+
const schedulingMode = options?.schedulingMode;
|
|
32609
|
+
const zoneProviders = internalProvideZoneChangeDetection({ ngZoneFactory: () => new NgZone(getNgZoneOptions(options)), schedulingMode });
|
|
31900
32610
|
return makeEnvironmentProviders([
|
|
31901
32611
|
(typeof ngDevMode === 'undefined' || ngDevMode) ? { provide: PROVIDED_NG_ZONE, useValue: true } :
|
|
31902
32612
|
[],
|
|
@@ -32174,7 +32884,8 @@ class PlatformRef {
|
|
|
32174
32884
|
// Do not try to replace ngZone.run with ApplicationRef#run because ApplicationRef would then be
|
|
32175
32885
|
// created outside of the Angular zone.
|
|
32176
32886
|
return ngZone.run(() => {
|
|
32177
|
-
const
|
|
32887
|
+
const schedulingMode = options?.schedulingMode;
|
|
32888
|
+
const moduleRef = createNgModuleRefWithProviders(moduleFactory.moduleType, this.injector, internalProvideZoneChangeDetection({ ngZoneFactory: () => ngZone, schedulingMode }));
|
|
32178
32889
|
if ((typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
32179
32890
|
moduleRef.injector.get(PROVIDED_NG_ZONE, null) !== null) {
|
|
32180
32891
|
throw new RuntimeError(207 /* RuntimeErrorCode.PROVIDER_IN_WRONG_CONTEXT */, '`bootstrapModule` does not support `provideZoneChangeDetection`. Use `BootstrapOptions` instead.');
|
|
@@ -32556,7 +33267,7 @@ function createViewRef(tNode, lView, isPipe) {
|
|
|
32556
33267
|
}
|
|
32557
33268
|
|
|
32558
33269
|
/**
|
|
32559
|
-
* Represents an Angular
|
|
33270
|
+
* Represents an Angular view.
|
|
32560
33271
|
*
|
|
32561
33272
|
* @see {@link ChangeDetectorRef#usage-notes Change detection usage}
|
|
32562
33273
|
*
|
|
@@ -32565,8 +33276,8 @@ function createViewRef(tNode, lView, isPipe) {
|
|
|
32565
33276
|
class ViewRef extends ChangeDetectorRef {
|
|
32566
33277
|
}
|
|
32567
33278
|
/**
|
|
32568
|
-
* Represents an Angular
|
|
32569
|
-
* An
|
|
33279
|
+
* Represents an Angular view in a view container.
|
|
33280
|
+
* An embedded view can be referenced from a component
|
|
32570
33281
|
* other than the hosting component whose template defines it, or it can be defined
|
|
32571
33282
|
* independently by a `TemplateRef`.
|
|
32572
33283
|
*
|
|
@@ -35043,59 +35754,6 @@ function internalCreateApplication(config) {
|
|
|
35043
35754
|
}
|
|
35044
35755
|
}
|
|
35045
35756
|
|
|
35046
|
-
class ChangeDetectionSchedulerImpl {
|
|
35047
|
-
constructor() {
|
|
35048
|
-
this.appRef = inject(ApplicationRef);
|
|
35049
|
-
this.taskService = inject(PendingTasks);
|
|
35050
|
-
this.pendingRenderTaskId = null;
|
|
35051
|
-
this.shouldRefreshViews = false;
|
|
35052
|
-
this.schedule = getCallbackScheduler();
|
|
35053
|
-
}
|
|
35054
|
-
notify(type = 0 /* NotificationType.RefreshViews */) {
|
|
35055
|
-
// When the only source of notification is an afterRender hook will skip straight to the hooks
|
|
35056
|
-
// rather than refreshing views in ApplicationRef.tick
|
|
35057
|
-
this.shouldRefreshViews ||= type === 0 /* NotificationType.RefreshViews */;
|
|
35058
|
-
if (this.pendingRenderTaskId !== null) {
|
|
35059
|
-
return;
|
|
35060
|
-
}
|
|
35061
|
-
this.pendingRenderTaskId = this.taskService.add();
|
|
35062
|
-
this.schedule(() => {
|
|
35063
|
-
this.tick();
|
|
35064
|
-
});
|
|
35065
|
-
}
|
|
35066
|
-
tick() {
|
|
35067
|
-
try {
|
|
35068
|
-
if (!this.appRef.destroyed) {
|
|
35069
|
-
this.appRef._tick(this.shouldRefreshViews);
|
|
35070
|
-
}
|
|
35071
|
-
}
|
|
35072
|
-
finally {
|
|
35073
|
-
this.shouldRefreshViews = false;
|
|
35074
|
-
// If this is the last task, the service will synchronously emit a stable notification. If
|
|
35075
|
-
// there is a subscriber that then acts in a way that tries to notify the scheduler again,
|
|
35076
|
-
// we need to be able to respond to schedule a new change detection. Therefore, we should
|
|
35077
|
-
// clear the task ID before removing it from the pending tasks (or the tasks service should
|
|
35078
|
-
// not synchronously emit stable, similar to how Zone stableness only happens if it's still
|
|
35079
|
-
// stable after a microtask).
|
|
35080
|
-
const taskId = this.pendingRenderTaskId;
|
|
35081
|
-
this.pendingRenderTaskId = null;
|
|
35082
|
-
this.taskService.remove(taskId);
|
|
35083
|
-
}
|
|
35084
|
-
}
|
|
35085
|
-
static { this.ɵfac = function ChangeDetectionSchedulerImpl_Factory(t) { return new (t || ChangeDetectionSchedulerImpl)(); }; }
|
|
35086
|
-
static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: ChangeDetectionSchedulerImpl, factory: ChangeDetectionSchedulerImpl.ɵfac, providedIn: 'root' }); }
|
|
35087
|
-
}
|
|
35088
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ChangeDetectionSchedulerImpl, [{
|
|
35089
|
-
type: Injectable,
|
|
35090
|
-
args: [{ providedIn: 'root' }]
|
|
35091
|
-
}], null, null); })();
|
|
35092
|
-
function provideZonelessChangeDetection() {
|
|
35093
|
-
return makeEnvironmentProviders([
|
|
35094
|
-
{ provide: ChangeDetectionScheduler, useExisting: ChangeDetectionSchedulerImpl },
|
|
35095
|
-
{ provide: NgZone, useClass: NoopNgZone },
|
|
35096
|
-
]);
|
|
35097
|
-
}
|
|
35098
|
-
|
|
35099
35757
|
/**
|
|
35100
35758
|
* Retrieves all defer blocks in a given LView.
|
|
35101
35759
|
*
|
|
@@ -35132,226 +35790,6 @@ function getDeferBlocks(lView, deferBlocks) {
|
|
|
35132
35790
|
}
|
|
35133
35791
|
}
|
|
35134
35792
|
|
|
35135
|
-
/**
|
|
35136
|
-
* Indicates whether the hydration-related code was added,
|
|
35137
|
-
* prevents adding it multiple times.
|
|
35138
|
-
*/
|
|
35139
|
-
let isHydrationSupportEnabled = false;
|
|
35140
|
-
/**
|
|
35141
|
-
* Indicates whether support for hydrating i18n blocks is enabled.
|
|
35142
|
-
*/
|
|
35143
|
-
let _isI18nHydrationSupportEnabled = false;
|
|
35144
|
-
/**
|
|
35145
|
-
* Defines a period of time that Angular waits for the `ApplicationRef.isStable` to emit `true`.
|
|
35146
|
-
* If there was no event with the `true` value during this time, Angular reports a warning.
|
|
35147
|
-
*/
|
|
35148
|
-
const APPLICATION_IS_STABLE_TIMEOUT = 10_000;
|
|
35149
|
-
/**
|
|
35150
|
-
* Brings the necessary hydration code in tree-shakable manner.
|
|
35151
|
-
* The code is only present when the `provideClientHydration` is
|
|
35152
|
-
* invoked. Otherwise, this code is tree-shaken away during the
|
|
35153
|
-
* build optimization step.
|
|
35154
|
-
*
|
|
35155
|
-
* This technique allows us to swap implementations of methods so
|
|
35156
|
-
* tree shaking works appropriately when hydration is disabled or
|
|
35157
|
-
* enabled. It brings in the appropriate version of the method that
|
|
35158
|
-
* supports hydration only when enabled.
|
|
35159
|
-
*/
|
|
35160
|
-
function enableHydrationRuntimeSupport() {
|
|
35161
|
-
if (!isHydrationSupportEnabled) {
|
|
35162
|
-
isHydrationSupportEnabled = true;
|
|
35163
|
-
enableRetrieveHydrationInfoImpl();
|
|
35164
|
-
enableLocateOrCreateElementNodeImpl();
|
|
35165
|
-
enableLocateOrCreateTextNodeImpl();
|
|
35166
|
-
enableLocateOrCreateElementContainerNodeImpl();
|
|
35167
|
-
enableLocateOrCreateContainerAnchorImpl();
|
|
35168
|
-
enableLocateOrCreateContainerRefImpl();
|
|
35169
|
-
enableFindMatchingDehydratedViewImpl();
|
|
35170
|
-
enableApplyRootElementTransformImpl();
|
|
35171
|
-
enableLocateOrCreateI18nNodeImpl();
|
|
35172
|
-
}
|
|
35173
|
-
}
|
|
35174
|
-
/**
|
|
35175
|
-
* Outputs a message with hydration stats into a console.
|
|
35176
|
-
*/
|
|
35177
|
-
function printHydrationStats(injector) {
|
|
35178
|
-
const console = injector.get(Console);
|
|
35179
|
-
const message = `Angular hydrated ${ngDevMode.hydratedComponents} component(s) ` +
|
|
35180
|
-
`and ${ngDevMode.hydratedNodes} node(s), ` +
|
|
35181
|
-
`${ngDevMode.componentsSkippedHydration} component(s) were skipped. ` +
|
|
35182
|
-
`Learn more at https://angular.io/guide/hydration.`;
|
|
35183
|
-
// tslint:disable-next-line:no-console
|
|
35184
|
-
console.log(message);
|
|
35185
|
-
}
|
|
35186
|
-
/**
|
|
35187
|
-
* Returns a Promise that is resolved when an application becomes stable.
|
|
35188
|
-
*/
|
|
35189
|
-
function whenStableWithTimeout(appRef, injector) {
|
|
35190
|
-
const whenStablePromise = whenStable(appRef);
|
|
35191
|
-
if (typeof ngDevMode !== 'undefined' && ngDevMode) {
|
|
35192
|
-
const timeoutTime = APPLICATION_IS_STABLE_TIMEOUT;
|
|
35193
|
-
const console = injector.get(Console);
|
|
35194
|
-
const ngZone = injector.get(NgZone);
|
|
35195
|
-
// The following call should not and does not prevent the app to become stable
|
|
35196
|
-
// We cannot use RxJS timer here because the app would remain unstable.
|
|
35197
|
-
// This also avoids an extra change detection cycle.
|
|
35198
|
-
const timeoutId = ngZone.runOutsideAngular(() => {
|
|
35199
|
-
return setTimeout(() => logWarningOnStableTimedout(timeoutTime, console), timeoutTime);
|
|
35200
|
-
});
|
|
35201
|
-
whenStablePromise.finally(() => clearTimeout(timeoutId));
|
|
35202
|
-
}
|
|
35203
|
-
return whenStablePromise;
|
|
35204
|
-
}
|
|
35205
|
-
/**
|
|
35206
|
-
* Returns a set of providers required to setup hydration support
|
|
35207
|
-
* for an application that is server side rendered. This function is
|
|
35208
|
-
* included into the `provideClientHydration` public API function from
|
|
35209
|
-
* the `platform-browser` package.
|
|
35210
|
-
*
|
|
35211
|
-
* The function sets up an internal flag that would be recognized during
|
|
35212
|
-
* the server side rendering time as well, so there is no need to
|
|
35213
|
-
* configure or change anything in NgUniversal to enable the feature.
|
|
35214
|
-
*/
|
|
35215
|
-
function withDomHydration() {
|
|
35216
|
-
return makeEnvironmentProviders([
|
|
35217
|
-
{
|
|
35218
|
-
provide: IS_HYDRATION_DOM_REUSE_ENABLED,
|
|
35219
|
-
useFactory: () => {
|
|
35220
|
-
let isEnabled = true;
|
|
35221
|
-
if (isPlatformBrowser()) {
|
|
35222
|
-
// On the client, verify that the server response contains
|
|
35223
|
-
// hydration annotations. Otherwise, keep hydration disabled.
|
|
35224
|
-
const transferState = inject(TransferState, { optional: true });
|
|
35225
|
-
isEnabled = !!transferState?.get(NGH_DATA_KEY, null);
|
|
35226
|
-
if (!isEnabled && (typeof ngDevMode !== 'undefined' && ngDevMode)) {
|
|
35227
|
-
const console = inject(Console);
|
|
35228
|
-
const message = formatRuntimeError(-505 /* RuntimeErrorCode.MISSING_HYDRATION_ANNOTATIONS */, 'Angular hydration was requested on the client, but there was no ' +
|
|
35229
|
-
'serialized information present in the server response, ' +
|
|
35230
|
-
'thus hydration was not enabled. ' +
|
|
35231
|
-
'Make sure the `provideClientHydration()` is included into the list ' +
|
|
35232
|
-
'of providers in the server part of the application configuration.');
|
|
35233
|
-
// tslint:disable-next-line:no-console
|
|
35234
|
-
console.warn(message);
|
|
35235
|
-
}
|
|
35236
|
-
}
|
|
35237
|
-
if (isEnabled) {
|
|
35238
|
-
performanceMarkFeature('NgHydration');
|
|
35239
|
-
}
|
|
35240
|
-
return isEnabled;
|
|
35241
|
-
},
|
|
35242
|
-
},
|
|
35243
|
-
{
|
|
35244
|
-
provide: ENVIRONMENT_INITIALIZER,
|
|
35245
|
-
useValue: () => {
|
|
35246
|
-
_isI18nHydrationSupportEnabled = !!inject(IS_I18N_HYDRATION_ENABLED, { optional: true });
|
|
35247
|
-
// Since this function is used across both server and client,
|
|
35248
|
-
// make sure that the runtime code is only added when invoked
|
|
35249
|
-
// on the client. Moving forward, the `isPlatformBrowser` check should
|
|
35250
|
-
// be replaced with a tree-shakable alternative (e.g. `isServer`
|
|
35251
|
-
// flag).
|
|
35252
|
-
if (isPlatformBrowser() && inject(IS_HYDRATION_DOM_REUSE_ENABLED)) {
|
|
35253
|
-
verifySsrContentsIntegrity();
|
|
35254
|
-
enableHydrationRuntimeSupport();
|
|
35255
|
-
}
|
|
35256
|
-
},
|
|
35257
|
-
multi: true,
|
|
35258
|
-
},
|
|
35259
|
-
{
|
|
35260
|
-
provide: PRESERVE_HOST_CONTENT,
|
|
35261
|
-
useFactory: () => {
|
|
35262
|
-
// Preserve host element content only in a browser
|
|
35263
|
-
// environment and when hydration is configured properly.
|
|
35264
|
-
// On a server, an application is rendered from scratch,
|
|
35265
|
-
// so the host content needs to be empty.
|
|
35266
|
-
return isPlatformBrowser() && inject(IS_HYDRATION_DOM_REUSE_ENABLED);
|
|
35267
|
-
}
|
|
35268
|
-
},
|
|
35269
|
-
{
|
|
35270
|
-
provide: APP_BOOTSTRAP_LISTENER,
|
|
35271
|
-
useFactory: () => {
|
|
35272
|
-
if (isPlatformBrowser() && inject(IS_HYDRATION_DOM_REUSE_ENABLED)) {
|
|
35273
|
-
const appRef = inject(ApplicationRef);
|
|
35274
|
-
const injector = inject(Injector);
|
|
35275
|
-
return () => {
|
|
35276
|
-
// Wait until an app becomes stable and cleanup all views that
|
|
35277
|
-
// were not claimed during the application bootstrap process.
|
|
35278
|
-
// The timing is similar to when we start the serialization process
|
|
35279
|
-
// on the server.
|
|
35280
|
-
//
|
|
35281
|
-
// Note: the cleanup task *MUST* be scheduled within the Angular zone
|
|
35282
|
-
// to ensure that change detection is properly run afterward.
|
|
35283
|
-
whenStableWithTimeout(appRef, injector).then(() => {
|
|
35284
|
-
NgZone.assertInAngularZone();
|
|
35285
|
-
cleanupDehydratedViews(appRef);
|
|
35286
|
-
if (typeof ngDevMode !== 'undefined' && ngDevMode) {
|
|
35287
|
-
printHydrationStats(injector);
|
|
35288
|
-
}
|
|
35289
|
-
});
|
|
35290
|
-
};
|
|
35291
|
-
}
|
|
35292
|
-
return () => { }; // noop
|
|
35293
|
-
},
|
|
35294
|
-
multi: true,
|
|
35295
|
-
}
|
|
35296
|
-
]);
|
|
35297
|
-
}
|
|
35298
|
-
/**
|
|
35299
|
-
* Returns a set of providers required to setup support for i18n hydration.
|
|
35300
|
-
* Requires hydration to be enabled separately.
|
|
35301
|
-
*/
|
|
35302
|
-
function withI18nHydration() {
|
|
35303
|
-
return makeEnvironmentProviders([
|
|
35304
|
-
{
|
|
35305
|
-
provide: IS_I18N_HYDRATION_ENABLED,
|
|
35306
|
-
useValue: true,
|
|
35307
|
-
},
|
|
35308
|
-
]);
|
|
35309
|
-
}
|
|
35310
|
-
/**
|
|
35311
|
-
* Returns whether i18n hydration support is enabled.
|
|
35312
|
-
*/
|
|
35313
|
-
function isI18nHydrationSupportEnabled() {
|
|
35314
|
-
return _isI18nHydrationSupportEnabled;
|
|
35315
|
-
}
|
|
35316
|
-
/**
|
|
35317
|
-
*
|
|
35318
|
-
* @param time The time in ms until the stable timedout warning message is logged
|
|
35319
|
-
*/
|
|
35320
|
-
function logWarningOnStableTimedout(time, console) {
|
|
35321
|
-
const message = `Angular hydration expected the ApplicationRef.isStable() to emit \`true\`, but it ` +
|
|
35322
|
-
`didn't happen within ${time}ms. Angular hydration logic depends on the application becoming stable ` +
|
|
35323
|
-
`as a signal to complete hydration process.`;
|
|
35324
|
-
console.warn(formatRuntimeError(-506 /* RuntimeErrorCode.HYDRATION_STABLE_TIMEDOUT */, message));
|
|
35325
|
-
}
|
|
35326
|
-
/**
|
|
35327
|
-
* Verifies whether the DOM contains a special marker added during SSR time to make sure
|
|
35328
|
-
* there is no SSR'ed contents transformations happen after SSR is completed. Typically that
|
|
35329
|
-
* happens either by CDN or during the build process as an optimization to remove comment nodes.
|
|
35330
|
-
* Hydration process requires comment nodes produced by Angular to locate correct DOM segments.
|
|
35331
|
-
* When this special marker is *not* present - throw an error and do not proceed with hydration,
|
|
35332
|
-
* since it will not be able to function correctly.
|
|
35333
|
-
*
|
|
35334
|
-
* Note: this function is invoked only on the client, so it's safe to use DOM APIs.
|
|
35335
|
-
*/
|
|
35336
|
-
function verifySsrContentsIntegrity() {
|
|
35337
|
-
const doc = getDocument();
|
|
35338
|
-
let hydrationMarker;
|
|
35339
|
-
for (const node of doc.body.childNodes) {
|
|
35340
|
-
if (node.nodeType === Node.COMMENT_NODE &&
|
|
35341
|
-
node.textContent?.trim() === SSR_CONTENT_INTEGRITY_MARKER) {
|
|
35342
|
-
hydrationMarker = node;
|
|
35343
|
-
break;
|
|
35344
|
-
}
|
|
35345
|
-
}
|
|
35346
|
-
if (!hydrationMarker) {
|
|
35347
|
-
throw new RuntimeError(-507 /* RuntimeErrorCode.MISSING_SSR_CONTENT_INTEGRITY_MARKER */, typeof ngDevMode !== 'undefined' && ngDevMode &&
|
|
35348
|
-
'Angular hydration logic detected that HTML content of this page was modified after it ' +
|
|
35349
|
-
'was produced during server side rendering. Make sure that there are no optimizations ' +
|
|
35350
|
-
'that remove comment nodes from HTML enabled on your CDN. Angular hydration ' +
|
|
35351
|
-
'relies on HTML produced by the server, including whitespaces and comment nodes.');
|
|
35352
|
-
}
|
|
35353
|
-
}
|
|
35354
|
-
|
|
35355
35793
|
/**
|
|
35356
35794
|
* A collection that tracks all serialized views (`ngh` DOM annotations)
|
|
35357
35795
|
* to avoid duplication. An attempt to add a duplicate view results in the
|
|
@@ -35462,6 +35900,8 @@ function annotateLContainerForHydration(lContainer, context) {
|
|
|
35462
35900
|
* @param doc A reference to the current Document instance.
|
|
35463
35901
|
*/
|
|
35464
35902
|
function annotateForHydration(appRef, doc) {
|
|
35903
|
+
const injector = appRef.injector;
|
|
35904
|
+
const isI18nHydrationEnabledVal = isI18nHydrationEnabled(injector);
|
|
35465
35905
|
const serializedViewCollection = new SerializedViewCollection();
|
|
35466
35906
|
const corruptedTextNodes = new Map();
|
|
35467
35907
|
const viewRefs = appRef._views;
|
|
@@ -35473,6 +35913,8 @@ function annotateForHydration(appRef, doc) {
|
|
|
35473
35913
|
const context = {
|
|
35474
35914
|
serializedViewCollection,
|
|
35475
35915
|
corruptedTextNodes,
|
|
35916
|
+
isI18nHydrationEnabled: isI18nHydrationEnabledVal,
|
|
35917
|
+
i18nChildren: new Map(),
|
|
35476
35918
|
};
|
|
35477
35919
|
if (isLContainer(lNode)) {
|
|
35478
35920
|
annotateLContainerForHydration(lNode, context);
|
|
@@ -35489,7 +35931,7 @@ function annotateForHydration(appRef, doc) {
|
|
|
35489
35931
|
// hydration logic was setup and enabled correctly. Otherwise, if a client
|
|
35490
35932
|
// hydration doesn't find a key in the transfer state - an error is produced.
|
|
35491
35933
|
const serializedViews = serializedViewCollection.getAll();
|
|
35492
|
-
const transferState =
|
|
35934
|
+
const transferState = injector.get(TransferState);
|
|
35493
35935
|
transferState.set(NGH_DATA_KEY, serializedViews);
|
|
35494
35936
|
}
|
|
35495
35937
|
/**
|
|
@@ -35570,10 +36012,10 @@ function serializeLContainer(lContainer, context) {
|
|
|
35570
36012
|
* needs to take to locate a node) and stores it in the `NODES` section of the
|
|
35571
36013
|
* current serialized view.
|
|
35572
36014
|
*/
|
|
35573
|
-
function appendSerializedNodePath(ngh, tNode, lView) {
|
|
36015
|
+
function appendSerializedNodePath(ngh, tNode, lView, excludedParentNodes) {
|
|
35574
36016
|
const noOffsetIndex = tNode.index - HEADER_OFFSET;
|
|
35575
36017
|
ngh[NODES] ??= {};
|
|
35576
|
-
ngh[NODES][noOffsetIndex] = calcPathForNode(tNode, lView);
|
|
36018
|
+
ngh[NODES][noOffsetIndex] = calcPathForNode(tNode, lView, excludedParentNodes);
|
|
35577
36019
|
}
|
|
35578
36020
|
/**
|
|
35579
36021
|
* Helper function to append information about a disconnected node.
|
|
@@ -35599,10 +36041,19 @@ function appendDisconnectedNodeIndex(ngh, tNode) {
|
|
|
35599
36041
|
function serializeLView(lView, context) {
|
|
35600
36042
|
const ngh = {};
|
|
35601
36043
|
const tView = lView[TVIEW];
|
|
36044
|
+
const i18nChildren = getOrComputeI18nChildren(tView, context);
|
|
35602
36045
|
// Iterate over DOM element references in an LView.
|
|
35603
36046
|
for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
|
|
35604
36047
|
const tNode = tView.data[i];
|
|
35605
36048
|
const noOffsetIndex = i - HEADER_OFFSET;
|
|
36049
|
+
// Attempt to serialize any i18n data for the given slot. We do this first, as i18n
|
|
36050
|
+
// has its own process for serialization.
|
|
36051
|
+
const i18nData = trySerializeI18nBlock(lView, i, context);
|
|
36052
|
+
if (i18nData) {
|
|
36053
|
+
ngh[I18N_DATA] ??= {};
|
|
36054
|
+
ngh[I18N_DATA][noOffsetIndex] = i18nData;
|
|
36055
|
+
continue;
|
|
36056
|
+
}
|
|
35606
36057
|
// Skip processing of a given slot in the following cases:
|
|
35607
36058
|
// - Local refs (e.g. <div #localRef>) take up an extra slot in LViews
|
|
35608
36059
|
// to store the same element. In this case, there is no information in
|
|
@@ -35612,6 +36063,12 @@ function serializeLView(lView, context) {
|
|
|
35612
36063
|
if (!isTNodeShape(tNode)) {
|
|
35613
36064
|
continue;
|
|
35614
36065
|
}
|
|
36066
|
+
// Skip any nodes that are in an i18n block but are considered detached (i.e. not
|
|
36067
|
+
// present in the template). These nodes are disconnected from the DOM tree, and
|
|
36068
|
+
// so we don't want to serialize any information about them.
|
|
36069
|
+
if (isDetachedByI18n(tNode)) {
|
|
36070
|
+
continue;
|
|
36071
|
+
}
|
|
35615
36072
|
// Check if a native node that represents a given TNode is disconnected from the DOM tree.
|
|
35616
36073
|
// Such nodes must be excluded from the hydration (since the hydration won't be able to
|
|
35617
36074
|
// find them), so the TNode ids are collected and used at runtime to skip the hydration.
|
|
@@ -35643,7 +36100,7 @@ function serializeLView(lView, context) {
|
|
|
35643
36100
|
appendDisconnectedNodeIndex(ngh, projectionHeadTNode);
|
|
35644
36101
|
}
|
|
35645
36102
|
else {
|
|
35646
|
-
appendSerializedNodePath(ngh, projectionHeadTNode, lView);
|
|
36103
|
+
appendSerializedNodePath(ngh, projectionHeadTNode, lView, i18nChildren);
|
|
35647
36104
|
}
|
|
35648
36105
|
}
|
|
35649
36106
|
}
|
|
@@ -35660,7 +36117,7 @@ function serializeLView(lView, context) {
|
|
|
35660
36117
|
}
|
|
35661
36118
|
}
|
|
35662
36119
|
}
|
|
35663
|
-
conditionallyAnnotateNodePath(ngh, tNode, lView);
|
|
36120
|
+
conditionallyAnnotateNodePath(ngh, tNode, lView, i18nChildren);
|
|
35664
36121
|
if (isLContainer(lView[i])) {
|
|
35665
36122
|
// Serialize information about a template.
|
|
35666
36123
|
const embeddedTView = tNode.tView;
|
|
@@ -35710,47 +36167,13 @@ function serializeLView(lView, context) {
|
|
|
35710
36167
|
}
|
|
35711
36168
|
if (nextTNode && !isInSkipHydrationBlock(nextTNode)) {
|
|
35712
36169
|
// Handle a tNode after the `<ng-content>` slot.
|
|
35713
|
-
appendSerializedNodePath(ngh, nextTNode, lView);
|
|
36170
|
+
appendSerializedNodePath(ngh, nextTNode, lView, i18nChildren);
|
|
35714
36171
|
}
|
|
35715
36172
|
}
|
|
35716
36173
|
else {
|
|
35717
|
-
// Handle cases where text nodes can be lost after DOM serialization:
|
|
35718
|
-
// 1. When there is an *empty text node* in DOM: in this case, this
|
|
35719
|
-
// node would not make it into the serialized string and as a result,
|
|
35720
|
-
// this node wouldn't be created in a browser. This would result in
|
|
35721
|
-
// a mismatch during the hydration, where the runtime logic would expect
|
|
35722
|
-
// a text node to be present in live DOM, but no text node would exist.
|
|
35723
|
-
// Example: `<span>{{ name }}</span>` when the `name` is an empty string.
|
|
35724
|
-
// This would result in `<span></span>` string after serialization and
|
|
35725
|
-
// in a browser only the `span` element would be created. To resolve that,
|
|
35726
|
-
// an extra comment node is appended in place of an empty text node and
|
|
35727
|
-
// that special comment node is replaced with an empty text node *before*
|
|
35728
|
-
// hydration.
|
|
35729
|
-
// 2. When there are 2 consecutive text nodes present in the DOM.
|
|
35730
|
-
// Example: `<div>Hello <ng-container *ngIf="true">world</ng-container></div>`.
|
|
35731
|
-
// In this scenario, the live DOM would look like this:
|
|
35732
|
-
// <div>#text('Hello ') #text('world') #comment('container')</div>
|
|
35733
|
-
// Serialized string would look like this: `<div>Hello world<!--container--></div>`.
|
|
35734
|
-
// The live DOM in a browser after that would be:
|
|
35735
|
-
// <div>#text('Hello world') #comment('container')</div>
|
|
35736
|
-
// Notice how 2 text nodes are now "merged" into one. This would cause hydration
|
|
35737
|
-
// logic to fail, since it'd expect 2 text nodes being present, not one.
|
|
35738
|
-
// To fix this, we insert a special comment node in between those text nodes, so
|
|
35739
|
-
// serialized representation is: `<div>Hello <!--ngtns-->world<!--container--></div>`.
|
|
35740
|
-
// This forces browser to create 2 text nodes separated by a comment node.
|
|
35741
|
-
// Before running a hydration process, this special comment node is removed, so the
|
|
35742
|
-
// live DOM has exactly the same state as it was before serialization.
|
|
35743
36174
|
if (tNode.type & 1 /* TNodeType.Text */) {
|
|
35744
36175
|
const rNode = unwrapRNode(lView[i]);
|
|
35745
|
-
|
|
35746
|
-
// contents is empty. Otherwise, such text node would be present on
|
|
35747
|
-
// the client after server-side rendering and no special handling needed.
|
|
35748
|
-
if (rNode.textContent === '') {
|
|
35749
|
-
context.corruptedTextNodes.set(rNode, "ngetn" /* TextNodeMarker.EmptyNode */);
|
|
35750
|
-
}
|
|
35751
|
-
else if (rNode.nextSibling?.nodeType === Node.TEXT_NODE) {
|
|
35752
|
-
context.corruptedTextNodes.set(rNode, "ngtns" /* TextNodeMarker.Separator */);
|
|
35753
|
-
}
|
|
36176
|
+
processTextNodeBeforeSerialization(context, rNode);
|
|
35754
36177
|
}
|
|
35755
36178
|
}
|
|
35756
36179
|
}
|
|
@@ -35763,17 +36186,17 @@ function serializeLView(lView, context) {
|
|
|
35763
36186
|
* 1. If `tNode.projectionNext` is different from `tNode.next` - it means that
|
|
35764
36187
|
* the next `tNode` after projection is different from the one in the original
|
|
35765
36188
|
* template. Since hydration relies on `tNode.next`, this serialized info
|
|
35766
|
-
*
|
|
36189
|
+
* is required to help runtime code find the node at the correct location.
|
|
35767
36190
|
* 2. In certain content projection-based use-cases, it's possible that only
|
|
35768
36191
|
* a content of a projected element is rendered. In this case, content nodes
|
|
35769
36192
|
* require an extra annotation, since runtime logic can't rely on parent-child
|
|
35770
36193
|
* connection to identify the location of a node.
|
|
35771
36194
|
*/
|
|
35772
|
-
function conditionallyAnnotateNodePath(ngh, tNode, lView) {
|
|
36195
|
+
function conditionallyAnnotateNodePath(ngh, tNode, lView, excludedParentNodes) {
|
|
35773
36196
|
// Handle case #1 described above.
|
|
35774
36197
|
if (tNode.projectionNext && tNode.projectionNext !== tNode.next &&
|
|
35775
36198
|
!isInSkipHydrationBlock(tNode.projectionNext)) {
|
|
35776
|
-
appendSerializedNodePath(ngh, tNode.projectionNext, lView);
|
|
36199
|
+
appendSerializedNodePath(ngh, tNode.projectionNext, lView, excludedParentNodes);
|
|
35777
36200
|
}
|
|
35778
36201
|
// Handle case #2 described above.
|
|
35779
36202
|
// Note: we only do that for the first node (i.e. when `tNode.prev === null`),
|
|
@@ -35781,7 +36204,7 @@ function conditionallyAnnotateNodePath(ngh, tNode, lView) {
|
|
|
35781
36204
|
// annotation is needed.
|
|
35782
36205
|
if (tNode.prev === null && tNode.parent !== null && isDisconnectedNode(tNode.parent, lView) &&
|
|
35783
36206
|
!isDisconnectedNode(tNode, lView)) {
|
|
35784
|
-
appendSerializedNodePath(ngh, tNode, lView);
|
|
36207
|
+
appendSerializedNodePath(ngh, tNode, lView, excludedParentNodes);
|
|
35785
36208
|
}
|
|
35786
36209
|
}
|
|
35787
36210
|
/**
|
|
@@ -35857,6 +36280,248 @@ function isContentProjectedNode(tNode) {
|
|
|
35857
36280
|
return false;
|
|
35858
36281
|
}
|
|
35859
36282
|
|
|
36283
|
+
/**
|
|
36284
|
+
* Indicates whether the hydration-related code was added,
|
|
36285
|
+
* prevents adding it multiple times.
|
|
36286
|
+
*/
|
|
36287
|
+
let isHydrationSupportEnabled = false;
|
|
36288
|
+
/**
|
|
36289
|
+
* Indicates whether the i18n-related code was added,
|
|
36290
|
+
* prevents adding it multiple times.
|
|
36291
|
+
*
|
|
36292
|
+
* Note: This merely controls whether the code is loaded,
|
|
36293
|
+
* while `setIsI18nHydrationSupportEnabled` determines
|
|
36294
|
+
* whether i18n blocks are serialized or hydrated.
|
|
36295
|
+
*/
|
|
36296
|
+
let isI18nHydrationRuntimeSupportEnabled = false;
|
|
36297
|
+
/**
|
|
36298
|
+
* Defines a period of time that Angular waits for the `ApplicationRef.isStable` to emit `true`.
|
|
36299
|
+
* If there was no event with the `true` value during this time, Angular reports a warning.
|
|
36300
|
+
*/
|
|
36301
|
+
const APPLICATION_IS_STABLE_TIMEOUT = 10_000;
|
|
36302
|
+
/**
|
|
36303
|
+
* Brings the necessary hydration code in tree-shakable manner.
|
|
36304
|
+
* The code is only present when the `provideClientHydration` is
|
|
36305
|
+
* invoked. Otherwise, this code is tree-shaken away during the
|
|
36306
|
+
* build optimization step.
|
|
36307
|
+
*
|
|
36308
|
+
* This technique allows us to swap implementations of methods so
|
|
36309
|
+
* tree shaking works appropriately when hydration is disabled or
|
|
36310
|
+
* enabled. It brings in the appropriate version of the method that
|
|
36311
|
+
* supports hydration only when enabled.
|
|
36312
|
+
*/
|
|
36313
|
+
function enableHydrationRuntimeSupport() {
|
|
36314
|
+
if (!isHydrationSupportEnabled) {
|
|
36315
|
+
isHydrationSupportEnabled = true;
|
|
36316
|
+
enableRetrieveHydrationInfoImpl();
|
|
36317
|
+
enableLocateOrCreateElementNodeImpl();
|
|
36318
|
+
enableLocateOrCreateTextNodeImpl();
|
|
36319
|
+
enableLocateOrCreateElementContainerNodeImpl();
|
|
36320
|
+
enableLocateOrCreateContainerAnchorImpl();
|
|
36321
|
+
enableLocateOrCreateContainerRefImpl();
|
|
36322
|
+
enableFindMatchingDehydratedViewImpl();
|
|
36323
|
+
enableApplyRootElementTransformImpl();
|
|
36324
|
+
}
|
|
36325
|
+
}
|
|
36326
|
+
/**
|
|
36327
|
+
* Brings the necessary i18n hydration code in tree-shakable manner.
|
|
36328
|
+
* Similar to `enableHydrationRuntimeSupport`, the code is only
|
|
36329
|
+
* present when `withI18nSupport` is invoked.
|
|
36330
|
+
*/
|
|
36331
|
+
function enableI18nHydrationRuntimeSupport() {
|
|
36332
|
+
if (!isI18nHydrationRuntimeSupportEnabled) {
|
|
36333
|
+
isI18nHydrationRuntimeSupportEnabled = true;
|
|
36334
|
+
enableLocateOrCreateI18nNodeImpl();
|
|
36335
|
+
enablePrepareI18nBlockForHydrationImpl();
|
|
36336
|
+
enableClaimDehydratedIcuCaseImpl();
|
|
36337
|
+
}
|
|
36338
|
+
}
|
|
36339
|
+
/**
|
|
36340
|
+
* Outputs a message with hydration stats into a console.
|
|
36341
|
+
*/
|
|
36342
|
+
function printHydrationStats(injector) {
|
|
36343
|
+
const console = injector.get(Console);
|
|
36344
|
+
const message = `Angular hydrated ${ngDevMode.hydratedComponents} component(s) ` +
|
|
36345
|
+
`and ${ngDevMode.hydratedNodes} node(s), ` +
|
|
36346
|
+
`${ngDevMode.componentsSkippedHydration} component(s) were skipped. ` +
|
|
36347
|
+
`Learn more at https://angular.io/guide/hydration.`;
|
|
36348
|
+
// tslint:disable-next-line:no-console
|
|
36349
|
+
console.log(message);
|
|
36350
|
+
}
|
|
36351
|
+
/**
|
|
36352
|
+
* Returns a Promise that is resolved when an application becomes stable.
|
|
36353
|
+
*/
|
|
36354
|
+
function whenStableWithTimeout(appRef, injector) {
|
|
36355
|
+
const whenStablePromise = whenStable(appRef);
|
|
36356
|
+
if (typeof ngDevMode !== 'undefined' && ngDevMode) {
|
|
36357
|
+
const timeoutTime = APPLICATION_IS_STABLE_TIMEOUT;
|
|
36358
|
+
const console = injector.get(Console);
|
|
36359
|
+
const ngZone = injector.get(NgZone);
|
|
36360
|
+
// The following call should not and does not prevent the app to become stable
|
|
36361
|
+
// We cannot use RxJS timer here because the app would remain unstable.
|
|
36362
|
+
// This also avoids an extra change detection cycle.
|
|
36363
|
+
const timeoutId = ngZone.runOutsideAngular(() => {
|
|
36364
|
+
return setTimeout(() => logWarningOnStableTimedout(timeoutTime, console), timeoutTime);
|
|
36365
|
+
});
|
|
36366
|
+
whenStablePromise.finally(() => clearTimeout(timeoutId));
|
|
36367
|
+
}
|
|
36368
|
+
return whenStablePromise;
|
|
36369
|
+
}
|
|
36370
|
+
/**
|
|
36371
|
+
* Returns a set of providers required to setup hydration support
|
|
36372
|
+
* for an application that is server side rendered. This function is
|
|
36373
|
+
* included into the `provideClientHydration` public API function from
|
|
36374
|
+
* the `platform-browser` package.
|
|
36375
|
+
*
|
|
36376
|
+
* The function sets up an internal flag that would be recognized during
|
|
36377
|
+
* the server side rendering time as well, so there is no need to
|
|
36378
|
+
* configure or change anything in NgUniversal to enable the feature.
|
|
36379
|
+
*/
|
|
36380
|
+
function withDomHydration() {
|
|
36381
|
+
return makeEnvironmentProviders([
|
|
36382
|
+
{
|
|
36383
|
+
provide: IS_HYDRATION_DOM_REUSE_ENABLED,
|
|
36384
|
+
useFactory: () => {
|
|
36385
|
+
let isEnabled = true;
|
|
36386
|
+
if (isPlatformBrowser()) {
|
|
36387
|
+
// On the client, verify that the server response contains
|
|
36388
|
+
// hydration annotations. Otherwise, keep hydration disabled.
|
|
36389
|
+
const transferState = inject(TransferState, { optional: true });
|
|
36390
|
+
isEnabled = !!transferState?.get(NGH_DATA_KEY, null);
|
|
36391
|
+
if (!isEnabled && (typeof ngDevMode !== 'undefined' && ngDevMode)) {
|
|
36392
|
+
const console = inject(Console);
|
|
36393
|
+
const message = formatRuntimeError(-505 /* RuntimeErrorCode.MISSING_HYDRATION_ANNOTATIONS */, 'Angular hydration was requested on the client, but there was no ' +
|
|
36394
|
+
'serialized information present in the server response, ' +
|
|
36395
|
+
'thus hydration was not enabled. ' +
|
|
36396
|
+
'Make sure the `provideClientHydration()` is included into the list ' +
|
|
36397
|
+
'of providers in the server part of the application configuration.');
|
|
36398
|
+
// tslint:disable-next-line:no-console
|
|
36399
|
+
console.warn(message);
|
|
36400
|
+
}
|
|
36401
|
+
}
|
|
36402
|
+
if (isEnabled) {
|
|
36403
|
+
performanceMarkFeature('NgHydration');
|
|
36404
|
+
}
|
|
36405
|
+
return isEnabled;
|
|
36406
|
+
},
|
|
36407
|
+
},
|
|
36408
|
+
{
|
|
36409
|
+
provide: ENVIRONMENT_INITIALIZER,
|
|
36410
|
+
useValue: () => {
|
|
36411
|
+
// i18n support is enabled by calling withI18nSupport(), but there's
|
|
36412
|
+
// no way to turn it off (e.g. for tests), so we turn it off by default.
|
|
36413
|
+
setIsI18nHydrationSupportEnabled(false);
|
|
36414
|
+
// Since this function is used across both server and client,
|
|
36415
|
+
// make sure that the runtime code is only added when invoked
|
|
36416
|
+
// on the client. Moving forward, the `isPlatformBrowser` check should
|
|
36417
|
+
// be replaced with a tree-shakable alternative (e.g. `isServer`
|
|
36418
|
+
// flag).
|
|
36419
|
+
if (isPlatformBrowser() && inject(IS_HYDRATION_DOM_REUSE_ENABLED)) {
|
|
36420
|
+
verifySsrContentsIntegrity();
|
|
36421
|
+
enableHydrationRuntimeSupport();
|
|
36422
|
+
}
|
|
36423
|
+
},
|
|
36424
|
+
multi: true,
|
|
36425
|
+
},
|
|
36426
|
+
{
|
|
36427
|
+
provide: PRESERVE_HOST_CONTENT,
|
|
36428
|
+
useFactory: () => {
|
|
36429
|
+
// Preserve host element content only in a browser
|
|
36430
|
+
// environment and when hydration is configured properly.
|
|
36431
|
+
// On a server, an application is rendered from scratch,
|
|
36432
|
+
// so the host content needs to be empty.
|
|
36433
|
+
return isPlatformBrowser() && inject(IS_HYDRATION_DOM_REUSE_ENABLED);
|
|
36434
|
+
}
|
|
36435
|
+
},
|
|
36436
|
+
{
|
|
36437
|
+
provide: APP_BOOTSTRAP_LISTENER,
|
|
36438
|
+
useFactory: () => {
|
|
36439
|
+
if (isPlatformBrowser() && inject(IS_HYDRATION_DOM_REUSE_ENABLED)) {
|
|
36440
|
+
const appRef = inject(ApplicationRef);
|
|
36441
|
+
const injector = inject(Injector);
|
|
36442
|
+
return () => {
|
|
36443
|
+
// Wait until an app becomes stable and cleanup all views that
|
|
36444
|
+
// were not claimed during the application bootstrap process.
|
|
36445
|
+
// The timing is similar to when we start the serialization process
|
|
36446
|
+
// on the server.
|
|
36447
|
+
//
|
|
36448
|
+
// Note: the cleanup task *MUST* be scheduled within the Angular zone
|
|
36449
|
+
// to ensure that change detection is properly run afterward.
|
|
36450
|
+
whenStableWithTimeout(appRef, injector).then(() => {
|
|
36451
|
+
NgZone.assertInAngularZone();
|
|
36452
|
+
cleanupDehydratedViews(appRef);
|
|
36453
|
+
if (typeof ngDevMode !== 'undefined' && ngDevMode) {
|
|
36454
|
+
printHydrationStats(injector);
|
|
36455
|
+
}
|
|
36456
|
+
});
|
|
36457
|
+
};
|
|
36458
|
+
}
|
|
36459
|
+
return () => { }; // noop
|
|
36460
|
+
},
|
|
36461
|
+
multi: true,
|
|
36462
|
+
}
|
|
36463
|
+
]);
|
|
36464
|
+
}
|
|
36465
|
+
/**
|
|
36466
|
+
* Returns a set of providers required to setup support for i18n hydration.
|
|
36467
|
+
* Requires hydration to be enabled separately.
|
|
36468
|
+
*/
|
|
36469
|
+
function withI18nSupport() {
|
|
36470
|
+
return [
|
|
36471
|
+
{
|
|
36472
|
+
provide: IS_I18N_HYDRATION_ENABLED,
|
|
36473
|
+
useValue: true,
|
|
36474
|
+
},
|
|
36475
|
+
{
|
|
36476
|
+
provide: ENVIRONMENT_INITIALIZER,
|
|
36477
|
+
useValue: () => {
|
|
36478
|
+
enableI18nHydrationRuntimeSupport();
|
|
36479
|
+
setIsI18nHydrationSupportEnabled(true);
|
|
36480
|
+
performanceMarkFeature('NgI18nHydration');
|
|
36481
|
+
},
|
|
36482
|
+
multi: true,
|
|
36483
|
+
},
|
|
36484
|
+
];
|
|
36485
|
+
}
|
|
36486
|
+
/**
|
|
36487
|
+
*
|
|
36488
|
+
* @param time The time in ms until the stable timedout warning message is logged
|
|
36489
|
+
*/
|
|
36490
|
+
function logWarningOnStableTimedout(time, console) {
|
|
36491
|
+
const message = `Angular hydration expected the ApplicationRef.isStable() to emit \`true\`, but it ` +
|
|
36492
|
+
`didn't happen within ${time}ms. Angular hydration logic depends on the application becoming stable ` +
|
|
36493
|
+
`as a signal to complete hydration process.`;
|
|
36494
|
+
console.warn(formatRuntimeError(-506 /* RuntimeErrorCode.HYDRATION_STABLE_TIMEDOUT */, message));
|
|
36495
|
+
}
|
|
36496
|
+
/**
|
|
36497
|
+
* Verifies whether the DOM contains a special marker added during SSR time to make sure
|
|
36498
|
+
* there is no SSR'ed contents transformations happen after SSR is completed. Typically that
|
|
36499
|
+
* happens either by CDN or during the build process as an optimization to remove comment nodes.
|
|
36500
|
+
* Hydration process requires comment nodes produced by Angular to locate correct DOM segments.
|
|
36501
|
+
* When this special marker is *not* present - throw an error and do not proceed with hydration,
|
|
36502
|
+
* since it will not be able to function correctly.
|
|
36503
|
+
*
|
|
36504
|
+
* Note: this function is invoked only on the client, so it's safe to use DOM APIs.
|
|
36505
|
+
*/
|
|
36506
|
+
function verifySsrContentsIntegrity() {
|
|
36507
|
+
const doc = getDocument();
|
|
36508
|
+
let hydrationMarker;
|
|
36509
|
+
for (const node of doc.body.childNodes) {
|
|
36510
|
+
if (node.nodeType === Node.COMMENT_NODE &&
|
|
36511
|
+
node.textContent?.trim() === SSR_CONTENT_INTEGRITY_MARKER) {
|
|
36512
|
+
hydrationMarker = node;
|
|
36513
|
+
break;
|
|
36514
|
+
}
|
|
36515
|
+
}
|
|
36516
|
+
if (!hydrationMarker) {
|
|
36517
|
+
throw new RuntimeError(-507 /* RuntimeErrorCode.MISSING_SSR_CONTENT_INTEGRITY_MARKER */, typeof ngDevMode !== 'undefined' && ngDevMode &&
|
|
36518
|
+
'Angular hydration logic detected that HTML content of this page was modified after it ' +
|
|
36519
|
+
'was produced during server side rendering. Make sure that there are no optimizations ' +
|
|
36520
|
+
'that remove comment nodes from HTML enabled on your CDN. Angular hydration ' +
|
|
36521
|
+
'relies on HTML produced by the server, including whitespaces and comment nodes.');
|
|
36522
|
+
}
|
|
36523
|
+
}
|
|
36524
|
+
|
|
35860
36525
|
/**
|
|
35861
36526
|
* Queue a state update to be performed asynchronously.
|
|
35862
36527
|
*
|
|
@@ -35938,6 +36603,17 @@ function ɵɵngDeclareDirective(decl) {
|
|
|
35938
36603
|
function ɵɵngDeclareClassMetadata(decl) {
|
|
35939
36604
|
setClassMetadata(decl.type, decl.decorators, decl.ctorParameters ?? null, decl.propDecorators ?? null);
|
|
35940
36605
|
}
|
|
36606
|
+
/**
|
|
36607
|
+
* Evaluates the class metadata of a component that contains deferred blocks.
|
|
36608
|
+
*
|
|
36609
|
+
* @codeGenApi
|
|
36610
|
+
*/
|
|
36611
|
+
function ɵɵngDeclareClassMetadataAsync(decl) {
|
|
36612
|
+
setClassMetadataAsync(decl.type, decl.resolveDeferredDeps, (...types) => {
|
|
36613
|
+
const meta = decl.resolveMetadata(...types);
|
|
36614
|
+
setClassMetadata(decl.type, meta.decorators, meta.ctorParameters, meta.propDecorators);
|
|
36615
|
+
});
|
|
36616
|
+
}
|
|
35941
36617
|
/**
|
|
35942
36618
|
* Compiles a partial component declaration object into a full component definition object.
|
|
35943
36619
|
*
|
|
@@ -36392,5 +37068,5 @@ if (typeof ngDevMode !== 'undefined' && ngDevMode) {
|
|
|
36392
37068
|
* Generated bundle index. Do not edit.
|
|
36393
37069
|
*/
|
|
36394
37070
|
|
|
36395
|
-
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, HostAttributeToken, HostBinding, HostListener, INJECTOR$1 as 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, OutputEmitterRef, 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, assertNotInReactiveContext, assertPlatform, booleanAttribute, computed, contentChild, contentChildren, createComponent, createEnvironmentInjector, createNgModule, createNgModuleRef, createPlatform, createPlatformFactory, defineInjectable, destroyPlatform, effect, enableProdMode, forwardRef, getDebugNode, getModuleFactory, getNgModuleById, getPlatform, importProvidersFrom, inject, input, isDevMode, isSignal, isStandalone, makeEnvironmentProviders, makeStateKey, mergeApplicationConfig, model, numberAttribute, output, platformCore, provideZoneChangeDetection, reflectComponentType, resolveForwardRef, runInInjectionContext, setTestabilityGetter, signal, untracked, viewChild, viewChildren, ALLOW_MULTIPLE_PLATFORMS as ɵALLOW_MULTIPLE_PLATFORMS, AfterRenderEventManager as ɵAfterRenderEventManager, CONTAINER_HEADER_OFFSET as ɵCONTAINER_HEADER_OFFSET, ChangeDetectionScheduler as ɵChangeDetectionScheduler, 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, EffectScheduler as ɵEffectScheduler, IMAGE_CONFIG as ɵIMAGE_CONFIG, IMAGE_CONFIG_DEFAULTS as ɵIMAGE_CONFIG_DEFAULTS, INJECTOR_SCOPE as ɵINJECTOR_SCOPE, ɵINPUT_SIGNAL_BRAND_WRITE_TYPE, IS_HYDRATION_DOM_REUSE_ENABLED as ɵIS_HYDRATION_DOM_REUSE_ENABLED, 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, PendingTasks as ɵPendingTasks, 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, _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, detectChangesInViewIfRequired as ɵdetectChangesInViewIfRequired, devModeEqual as ɵdevModeEqual, findLocaleData as ɵfindLocaleData, flushModuleScopingQueueAsMuchAsPossible as ɵflushModuleScopingQueueAsMuchAsPossible, formatRuntimeError as ɵformatRuntimeError, generateStandaloneInDeclarationsError as ɵgenerateStandaloneInDeclarationsError, getAsyncClassMetadataFn as ɵgetAsyncClassMetadataFn, getDebugNode as ɵgetDebugNode, getDeferBlocks as ɵgetDeferBlocks, getDirectives as ɵgetDirectives, getEnsureDirtyViewsAreAlwaysReachable as ɵgetEnsureDirtyViewsAreAlwaysReachable, getHostElement as ɵgetHostElement, getInjectableDef as ɵgetInjectableDef, getLContext as ɵgetLContext, getLocaleCurrencyCode as ɵgetLocaleCurrencyCode, getLocalePluralCase as ɵgetLocalePluralCase, getOutputDestroyRef as ɵgetOutputDestroyRef, getSanitizationBypassType as ɵgetSanitizationBypassType, ɵgetUnknownElementStrictMode, ɵgetUnknownPropertyStrictMode, _global as ɵglobal, injectChangeDetectorRef as ɵinjectChangeDetectorRef, internalAfterNextRender as ɵinternalAfterNextRender, 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, performanceMarkFeature as ɵperformanceMarkFeature, provideZonelessChangeDetection as ɵprovideZonelessChangeDetection, queueStateUpdate as ɵqueueStateUpdate, readHydrationInfo as ɵreadHydrationInfo, registerLocaleData as ɵregisterLocaleData, renderDeferBlockState as ɵrenderDeferBlockState, resetCompiledComponents as ɵresetCompiledComponents, resetJitOptions as ɵresetJitOptions, resolveComponentResources as ɵresolveComponentResources, restoreComponentResolutionQueue as ɵrestoreComponentResolutionQueue, setAllowDuplicateNgModuleIdsForTest as ɵsetAllowDuplicateNgModuleIdsForTest, setAlternateWeakRefImpl as ɵsetAlternateWeakRefImpl, ɵsetClassDebugInfo, setClassMetadata as ɵsetClassMetadata, setClassMetadataAsync as ɵsetClassMetadataAsync, setCurrentInjector as ɵsetCurrentInjector, setDocument as ɵsetDocument, setEnsureDirtyViewsAreAlwaysReachable as ɵsetEnsureDirtyViewsAreAlwaysReachable, setInjectorProfilerContext as ɵsetInjectorProfilerContext, setLocaleId as ɵsetLocaleId, ɵsetUnknownElementStrictMode, ɵsetUnknownPropertyStrictMode, store as ɵstore, stringify as ɵstringify, transitiveScopesFor as ɵtransitiveScopesFor, triggerResourceLoading as ɵtriggerResourceLoading, truncateMiddle as ɵtruncateMiddle, unregisterAllLocaleData as ɵunregisterLocaleData, unwrapSafeValue as ɵunwrapSafeValue, ɵunwrapWritableSignal, whenStable as ɵwhenStable, withDomHydration as ɵwithDomHydration, withI18nHydration as ɵwithI18nHydration, ɵɵCopyDefinitionFeature, FactoryTarget as ɵɵFactoryTarget, ɵɵHostDirectivesFeature, ɵɵInheritDefinitionFeature, InputFlags as ɵɵInputFlags, ɵɵ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, ɵɵcontentQuerySignal, ɵɵdefer, ɵɵdeferEnableTimerScheduling, ɵɵ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, ɵɵqueryAdvance, ɵɵ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, ɵɵtwoWayBindingSet, ɵɵtwoWayListener, ɵɵtwoWayProperty, ɵɵvalidateIframeAttribute, ɵɵviewQuery, ɵɵviewQuerySignal };
|
|
37071
|
+
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, HostAttributeToken, HostBinding, HostListener, INJECTOR$1 as 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, OutputEmitterRef, 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, assertNotInReactiveContext, assertPlatform, booleanAttribute, computed, contentChild, contentChildren, createComponent, createEnvironmentInjector, createNgModule, createNgModuleRef, createPlatform, createPlatformFactory, defineInjectable, destroyPlatform, effect, enableProdMode, forwardRef, getDebugNode, getModuleFactory, getNgModuleById, getPlatform, importProvidersFrom, inject, input, isDevMode, isSignal, isStandalone, makeEnvironmentProviders, makeStateKey, mergeApplicationConfig, model, numberAttribute, output, platformCore, provideZoneChangeDetection, reflectComponentType, resolveForwardRef, runInInjectionContext, setTestabilityGetter, signal, untracked, viewChild, viewChildren, ALLOW_MULTIPLE_PLATFORMS as ɵALLOW_MULTIPLE_PLATFORMS, AfterRenderEventManager as ɵAfterRenderEventManager, CONTAINER_HEADER_OFFSET as ɵCONTAINER_HEADER_OFFSET, ChangeDetectionScheduler as ɵChangeDetectionScheduler, 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, EffectScheduler as ɵEffectScheduler, IMAGE_CONFIG as ɵIMAGE_CONFIG, IMAGE_CONFIG_DEFAULTS as ɵIMAGE_CONFIG_DEFAULTS, INJECTOR_SCOPE as ɵINJECTOR_SCOPE, ɵINPUT_SIGNAL_BRAND_WRITE_TYPE, IS_HYDRATION_DOM_REUSE_ENABLED as ɵIS_HYDRATION_DOM_REUSE_ENABLED, 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, PendingTasks as ɵPendingTasks, ReflectionCapabilities as ɵReflectionCapabilities, ComponentFactory as ɵRender3ComponentFactory, ComponentRef as ɵRender3ComponentRef, NgModuleRef as ɵRender3NgModuleRef, RuntimeError as ɵRuntimeError, SSR_CONTENT_INTEGRITY_MARKER as ɵSSR_CONTENT_INTEGRITY_MARKER, SchedulingMode as ɵSchedulingMode, 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, ZONELESS_ENABLED as ɵZONELESS_ENABLED, _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, detectChangesInViewIfRequired as ɵdetectChangesInViewIfRequired, devModeEqual as ɵdevModeEqual, findLocaleData as ɵfindLocaleData, flushModuleScopingQueueAsMuchAsPossible as ɵflushModuleScopingQueueAsMuchAsPossible, formatRuntimeError as ɵformatRuntimeError, generateStandaloneInDeclarationsError as ɵgenerateStandaloneInDeclarationsError, getAsyncClassMetadataFn as ɵgetAsyncClassMetadataFn, getDebugNode as ɵgetDebugNode, getDeferBlocks as ɵgetDeferBlocks, getDirectives as ɵgetDirectives, getHostElement as ɵgetHostElement, getInjectableDef as ɵgetInjectableDef, getLContext as ɵgetLContext, getLocaleCurrencyCode as ɵgetLocaleCurrencyCode, getLocalePluralCase as ɵgetLocalePluralCase, getOutputDestroyRef as ɵgetOutputDestroyRef, getSanitizationBypassType as ɵgetSanitizationBypassType, ɵgetUnknownElementStrictMode, ɵgetUnknownPropertyStrictMode, _global as ɵglobal, injectChangeDetectorRef as ɵinjectChangeDetectorRef, internalAfterNextRender as ɵinternalAfterNextRender, 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, performanceMarkFeature as ɵperformanceMarkFeature, provideZonelessChangeDetection as ɵprovideZonelessChangeDetection, queueStateUpdate as ɵqueueStateUpdate, readHydrationInfo as ɵreadHydrationInfo, registerLocaleData as ɵregisterLocaleData, renderDeferBlockState as ɵrenderDeferBlockState, resetCompiledComponents as ɵresetCompiledComponents, resetJitOptions as ɵresetJitOptions, resolveComponentResources as ɵresolveComponentResources, restoreComponentResolutionQueue as ɵrestoreComponentResolutionQueue, setAllowDuplicateNgModuleIdsForTest as ɵsetAllowDuplicateNgModuleIdsForTest, setAlternateWeakRefImpl as ɵsetAlternateWeakRefImpl, ɵsetClassDebugInfo, 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, truncateMiddle as ɵtruncateMiddle, unregisterAllLocaleData as ɵunregisterLocaleData, unwrapSafeValue as ɵunwrapSafeValue, ɵunwrapWritableSignal, whenStable as ɵwhenStable, withDomHydration as ɵwithDomHydration, withI18nSupport as ɵwithI18nSupport, ɵɵCopyDefinitionFeature, FactoryTarget as ɵɵFactoryTarget, ɵɵHostDirectivesFeature, ɵɵInheritDefinitionFeature, InputFlags as ɵɵInputFlags, ɵɵ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, ɵɵcontentQuerySignal, ɵɵdefer, ɵɵdeferEnableTimerScheduling, ɵɵ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, ɵɵngDeclareClassMetadataAsync, ɵɵ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, ɵɵqueryAdvance, ɵɵ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, ɵɵtwoWayBindingSet, ɵɵtwoWayListener, ɵɵtwoWayProperty, ɵɵvalidateIframeAttribute, ɵɵviewQuery, ɵɵviewQuerySignal };
|
|
36396
37072
|
//# sourceMappingURL=core.mjs.map
|