@angular/core 19.1.5 → 19.2.0-next.1
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/fesm2022/core.mjs +690 -649
- package/fesm2022/core.mjs.map +1 -1
- package/fesm2022/primitives/event-dispatch.mjs +1 -1
- package/fesm2022/primitives/signals.mjs +1 -1
- package/fesm2022/rxjs-interop.mjs +28 -23
- package/fesm2022/rxjs-interop.mjs.map +1 -1
- package/fesm2022/testing.mjs +4 -4
- package/index.d.ts +82 -25
- package/package.json +1 -1
- package/primitives/event-dispatch/index.d.ts +1 -1
- package/primitives/signals/index.d.ts +1 -1
- package/rxjs-interop/index.d.ts +14 -4
- package/schematics/bundles/{apply_import_manager-57619f3c.js → apply_import_manager-5ea49df9.js} +3 -3
- package/schematics/bundles/{checker-ce908760.js → checker-78667e44.js} +391 -99
- package/schematics/bundles/cleanup-unused-imports.js +5 -5
- package/schematics/bundles/{compiler_host-33b24174.js → compiler_host-b22de7db.js} +2 -2
- package/schematics/bundles/control-flow-migration.js +3 -3
- package/schematics/bundles/explicit-standalone-flag.js +3 -3
- package/schematics/bundles/imports-31a38653.js +1 -1
- package/schematics/bundles/{index-d6e51169.js → index-3e744c38.js} +4 -4
- package/schematics/bundles/{index-59a1204f.js → index-de135c2f.js} +4 -4
- package/schematics/bundles/inject-migration.js +3 -3
- package/schematics/bundles/leading_space-6e7a8ec6.js +1 -1
- package/schematics/bundles/{migrate_ts_type_references-2c37e645.js → migrate_ts_type_references-60e2a469.js} +5 -5
- package/schematics/bundles/nodes-88c2157f.js +1 -1
- package/schematics/bundles/output-migration.js +5 -5
- package/schematics/bundles/pending-tasks.js +3 -3
- package/schematics/bundles/{program-5512058c.js → program-b0d98952.js} +26 -100
- package/schematics/bundles/project_tsconfig_paths-6c9cde78.js +1 -1
- package/schematics/bundles/provide-initializer.js +3 -3
- package/schematics/bundles/route-lazy-loading.js +3 -3
- package/schematics/bundles/signal-input-migration.js +6 -6
- package/schematics/bundles/signal-queries-migration.js +6 -6
- package/schematics/bundles/signals.js +6 -6
- package/schematics/bundles/standalone-migration.js +5 -5
- package/testing/index.d.ts +1 -1
package/fesm2022/core.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v19.1
|
|
2
|
+
* @license Angular v19.2.0-next.1
|
|
3
3
|
* (c) 2010-2024 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -3067,7 +3067,7 @@ function assertProjectionSlots(lView, errMessage) {
|
|
|
3067
3067
|
function assertParentView(lView, errMessage) {
|
|
3068
3068
|
assertDefined(lView, errMessage || "Component views should always have a parent view (component's host view)");
|
|
3069
3069
|
}
|
|
3070
|
-
function assertNoDuplicateDirectives
|
|
3070
|
+
function assertNoDuplicateDirectives(directives) {
|
|
3071
3071
|
// The array needs at least two elements in order to have duplicates.
|
|
3072
3072
|
if (directives.length < 2) {
|
|
3073
3073
|
return;
|
|
@@ -3244,7 +3244,7 @@ const setProfiler = (profiler) => {
|
|
|
3244
3244
|
* execution context
|
|
3245
3245
|
* @returns
|
|
3246
3246
|
*/
|
|
3247
|
-
const profiler = function (event, instance, hookOrListener) {
|
|
3247
|
+
const profiler = function (event, instance = null, hookOrListener) {
|
|
3248
3248
|
if (profilerCallback != null /* both `null` and `undefined` */) {
|
|
3249
3249
|
profilerCallback(event, instance, hookOrListener);
|
|
3250
3250
|
}
|
|
@@ -4060,7 +4060,7 @@ function ɵɵnamespaceHTML() {
|
|
|
4060
4060
|
function namespaceHTMLInternal() {
|
|
4061
4061
|
instructionState.lFrame.currentNamespace = null;
|
|
4062
4062
|
}
|
|
4063
|
-
function getNamespace() {
|
|
4063
|
+
function getNamespace$1() {
|
|
4064
4064
|
return instructionState.lFrame.currentNamespace;
|
|
4065
4065
|
}
|
|
4066
4066
|
let _wasLastNodeCreated = true;
|
|
@@ -8756,6 +8756,10 @@ class AfterRenderImpl {
|
|
|
8756
8756
|
* might be scheduled.
|
|
8757
8757
|
*/
|
|
8758
8758
|
execute() {
|
|
8759
|
+
const hasSequencesToExecute = this.sequences.size > 0;
|
|
8760
|
+
if (hasSequencesToExecute) {
|
|
8761
|
+
profiler(16 /* ProfilerEvent.AfterRenderHooksStart */);
|
|
8762
|
+
}
|
|
8759
8763
|
this.executing = true;
|
|
8760
8764
|
for (const phase of AFTER_RENDER_PHASES) {
|
|
8761
8765
|
for (const sequence of this.sequences) {
|
|
@@ -8763,7 +8767,11 @@ class AfterRenderImpl {
|
|
|
8763
8767
|
continue;
|
|
8764
8768
|
}
|
|
8765
8769
|
try {
|
|
8766
|
-
sequence.pipelinedValue = this.ngZone.runOutsideAngular(() => this.maybeTrace(() =>
|
|
8770
|
+
sequence.pipelinedValue = this.ngZone.runOutsideAngular(() => this.maybeTrace(() => {
|
|
8771
|
+
const hookFn = sequence.hooks[phase];
|
|
8772
|
+
const value = hookFn(sequence.pipelinedValue);
|
|
8773
|
+
return value;
|
|
8774
|
+
}, sequence.snapshot));
|
|
8767
8775
|
}
|
|
8768
8776
|
catch (err) {
|
|
8769
8777
|
sequence.erroredOrDestroyed = true;
|
|
@@ -8789,6 +8797,9 @@ class AfterRenderImpl {
|
|
|
8789
8797
|
this.scheduler.notify(8 /* NotificationSource.DeferredRenderHook */);
|
|
8790
8798
|
}
|
|
8791
8799
|
this.deferredRegistrations.clear();
|
|
8800
|
+
if (hasSequencesToExecute) {
|
|
8801
|
+
profiler(17 /* ProfilerEvent.AfterRenderHooksEnd */);
|
|
8802
|
+
}
|
|
8792
8803
|
}
|
|
8793
8804
|
register(sequence) {
|
|
8794
8805
|
if (!this.executing) {
|
|
@@ -11637,6 +11648,14 @@ function getExpressionChangedErrorDetails(lView, bindingIndex, oldValue, newValu
|
|
|
11637
11648
|
return { propName: undefined, oldValue, newValue };
|
|
11638
11649
|
}
|
|
11639
11650
|
|
|
11651
|
+
/** Flags describing an input for a directive. */
|
|
11652
|
+
var InputFlags;
|
|
11653
|
+
(function (InputFlags) {
|
|
11654
|
+
InputFlags[InputFlags["None"] = 0] = "None";
|
|
11655
|
+
InputFlags[InputFlags["SignalBased"] = 1] = "SignalBased";
|
|
11656
|
+
InputFlags[InputFlags["HasDecoratorInputTransform"] = 2] = "HasDecoratorInputTransform";
|
|
11657
|
+
})(InputFlags || (InputFlags = {}));
|
|
11658
|
+
|
|
11640
11659
|
/**
|
|
11641
11660
|
* Returns an index of `classToSearch` in `className` taking token boundaries into account.
|
|
11642
11661
|
*
|
|
@@ -12246,13 +12265,38 @@ function selectIndexInternal(tView, lView, index, checkNoChangesMode) {
|
|
|
12246
12265
|
setSelectedIndex(index);
|
|
12247
12266
|
}
|
|
12248
12267
|
|
|
12249
|
-
|
|
12250
|
-
|
|
12251
|
-
|
|
12252
|
-
|
|
12253
|
-
|
|
12254
|
-
|
|
12255
|
-
|
|
12268
|
+
function ɵɵdirectiveInject(token, flags = InjectFlags.Default) {
|
|
12269
|
+
const lView = getLView();
|
|
12270
|
+
// Fall back to inject() if view hasn't been created. This situation can happen in tests
|
|
12271
|
+
// if inject utilities are used before bootstrapping.
|
|
12272
|
+
if (lView === null) {
|
|
12273
|
+
// Verify that we will not get into infinite loop.
|
|
12274
|
+
ngDevMode && assertInjectImplementationNotEqual(ɵɵdirectiveInject);
|
|
12275
|
+
return ɵɵinject(token, flags);
|
|
12276
|
+
}
|
|
12277
|
+
const tNode = getCurrentTNode();
|
|
12278
|
+
const value = getOrCreateInjectable(tNode, lView, resolveForwardRef(token), flags);
|
|
12279
|
+
ngDevMode && emitInjectEvent(token, value, flags);
|
|
12280
|
+
return value;
|
|
12281
|
+
}
|
|
12282
|
+
/**
|
|
12283
|
+
* Throws an error indicating that a factory function could not be generated by the compiler for a
|
|
12284
|
+
* particular class.
|
|
12285
|
+
*
|
|
12286
|
+
* This instruction allows the actual error message to be optimized away when ngDevMode is turned
|
|
12287
|
+
* off, saving bytes of generated code while still providing a good experience in dev mode.
|
|
12288
|
+
*
|
|
12289
|
+
* The name of the class is not mentioned here, but will be in the generated factory function name
|
|
12290
|
+
* and thus in the stack trace.
|
|
12291
|
+
*
|
|
12292
|
+
* @codeGenApi
|
|
12293
|
+
*/
|
|
12294
|
+
function ɵɵinvalidFactory() {
|
|
12295
|
+
const msg = ngDevMode
|
|
12296
|
+
? `This constructor was not compatible with Dependency Injection.`
|
|
12297
|
+
: 'invalid';
|
|
12298
|
+
throw new Error(msg);
|
|
12299
|
+
}
|
|
12256
12300
|
|
|
12257
12301
|
function writeToDirectiveInput(def, instance, publicName, privateName, flags, value) {
|
|
12258
12302
|
const prevConsumer = setActiveConsumer$1(null);
|
|
@@ -12320,6 +12364,34 @@ function createLView(parentLView, tView, context, flags, host, tHostNode, enviro
|
|
|
12320
12364
|
tView.type == 2 /* TViewType.Embedded */ ? parentLView[DECLARATION_COMPONENT_VIEW] : lView;
|
|
12321
12365
|
return lView;
|
|
12322
12366
|
}
|
|
12367
|
+
/**
|
|
12368
|
+
* When elements are created dynamically after a view blueprint is created (e.g. through
|
|
12369
|
+
* i18nApply()), we need to adjust the blueprint for future
|
|
12370
|
+
* template passes.
|
|
12371
|
+
*
|
|
12372
|
+
* @param tView `TView` associated with `LView`
|
|
12373
|
+
* @param lView The `LView` containing the blueprint to adjust
|
|
12374
|
+
* @param numSlotsToAlloc The number of slots to alloc in the LView, should be >0
|
|
12375
|
+
* @param initialValue Initial value to store in blueprint
|
|
12376
|
+
*/
|
|
12377
|
+
function allocExpando(tView, lView, numSlotsToAlloc, initialValue) {
|
|
12378
|
+
if (numSlotsToAlloc === 0)
|
|
12379
|
+
return -1;
|
|
12380
|
+
if (ngDevMode) {
|
|
12381
|
+
assertFirstCreatePass(tView);
|
|
12382
|
+
assertSame(tView, lView[TVIEW], '`LView` must be associated with `TView`!');
|
|
12383
|
+
assertEqual(tView.data.length, lView.length, 'Expecting LView to be same size as TView');
|
|
12384
|
+
assertEqual(tView.data.length, tView.blueprint.length, 'Expecting Blueprint to be same size as TView');
|
|
12385
|
+
assertFirstUpdatePass(tView);
|
|
12386
|
+
}
|
|
12387
|
+
const allocIdx = lView.length;
|
|
12388
|
+
for (let i = 0; i < numSlotsToAlloc; i++) {
|
|
12389
|
+
lView.push(initialValue);
|
|
12390
|
+
tView.blueprint.push(initialValue);
|
|
12391
|
+
tView.data.push(null);
|
|
12392
|
+
}
|
|
12393
|
+
return allocIdx;
|
|
12394
|
+
}
|
|
12323
12395
|
function executeTemplate(tView, lView, templateFn, rf, context) {
|
|
12324
12396
|
const prevSelectedIndex = getSelectedIndex();
|
|
12325
12397
|
const isUpdatePhase = rf & 2 /* RenderFlags.Update */;
|
|
@@ -12532,6 +12604,106 @@ function applyRootElementTransformImpl(rootElement) {
|
|
|
12532
12604
|
function enableApplyRootElementTransformImpl() {
|
|
12533
12605
|
_applyRootElementTransformImpl = applyRootElementTransformImpl;
|
|
12534
12606
|
}
|
|
12607
|
+
function captureNodeBindings(mode, aliasMap, directiveIndex, bindingsResult, hostDirectiveAliasMap) {
|
|
12608
|
+
for (let publicName in aliasMap) {
|
|
12609
|
+
if (!aliasMap.hasOwnProperty(publicName)) {
|
|
12610
|
+
continue;
|
|
12611
|
+
}
|
|
12612
|
+
const value = aliasMap[publicName];
|
|
12613
|
+
if (value === undefined) {
|
|
12614
|
+
continue;
|
|
12615
|
+
}
|
|
12616
|
+
bindingsResult ??= {};
|
|
12617
|
+
let internalName;
|
|
12618
|
+
let inputFlags = InputFlags.None;
|
|
12619
|
+
// For inputs, the value might be an array capturing additional
|
|
12620
|
+
// input flags.
|
|
12621
|
+
if (Array.isArray(value)) {
|
|
12622
|
+
internalName = value[0];
|
|
12623
|
+
inputFlags = value[1];
|
|
12624
|
+
}
|
|
12625
|
+
else {
|
|
12626
|
+
internalName = value;
|
|
12627
|
+
}
|
|
12628
|
+
// If there are no host directive mappings, we want to remap using the alias map from the
|
|
12629
|
+
// definition itself. If there is an alias map, it has two functions:
|
|
12630
|
+
// 1. It serves as an allowlist of bindings that are exposed by the host directives. Only the
|
|
12631
|
+
// ones inside the host directive map will be exposed on the host.
|
|
12632
|
+
// 2. The public name of the property is aliased using the host directive alias map, rather
|
|
12633
|
+
// than the alias map from the definition.
|
|
12634
|
+
let finalPublicName = publicName;
|
|
12635
|
+
if (hostDirectiveAliasMap !== null) {
|
|
12636
|
+
// If there is no mapping, it's not part of the allowlist and this input/output
|
|
12637
|
+
// is not captured and should be ignored.
|
|
12638
|
+
if (!hostDirectiveAliasMap.hasOwnProperty(publicName)) {
|
|
12639
|
+
continue;
|
|
12640
|
+
}
|
|
12641
|
+
finalPublicName = hostDirectiveAliasMap[publicName];
|
|
12642
|
+
}
|
|
12643
|
+
if (mode === 0 /* CaptureNodeBindingMode.Inputs */) {
|
|
12644
|
+
addPropertyBinding(bindingsResult, directiveIndex, finalPublicName, internalName, inputFlags);
|
|
12645
|
+
}
|
|
12646
|
+
else {
|
|
12647
|
+
addPropertyBinding(bindingsResult, directiveIndex, finalPublicName, internalName);
|
|
12648
|
+
}
|
|
12649
|
+
}
|
|
12650
|
+
return bindingsResult;
|
|
12651
|
+
}
|
|
12652
|
+
function addPropertyBinding(bindings, directiveIndex, publicName, internalName, inputFlags) {
|
|
12653
|
+
let values;
|
|
12654
|
+
if (bindings.hasOwnProperty(publicName)) {
|
|
12655
|
+
(values = bindings[publicName]).push(directiveIndex, internalName);
|
|
12656
|
+
}
|
|
12657
|
+
else {
|
|
12658
|
+
values = bindings[publicName] = [directiveIndex, internalName];
|
|
12659
|
+
}
|
|
12660
|
+
if (inputFlags !== undefined) {
|
|
12661
|
+
values.push(inputFlags);
|
|
12662
|
+
}
|
|
12663
|
+
}
|
|
12664
|
+
/**
|
|
12665
|
+
* Initializes data structures required to work with directive inputs and outputs.
|
|
12666
|
+
* Initialization is done for all directives matched on a given TNode.
|
|
12667
|
+
*/
|
|
12668
|
+
function initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefinitionMap) {
|
|
12669
|
+
ngDevMode && assertFirstCreatePass(tView);
|
|
12670
|
+
const start = tNode.directiveStart;
|
|
12671
|
+
const end = tNode.directiveEnd;
|
|
12672
|
+
const tViewData = tView.data;
|
|
12673
|
+
const tNodeAttrs = tNode.attrs;
|
|
12674
|
+
const inputsFromAttrs = [];
|
|
12675
|
+
let inputsStore = null;
|
|
12676
|
+
let outputsStore = null;
|
|
12677
|
+
for (let directiveIndex = start; directiveIndex < end; directiveIndex++) {
|
|
12678
|
+
const directiveDef = tViewData[directiveIndex];
|
|
12679
|
+
const aliasData = hostDirectiveDefinitionMap
|
|
12680
|
+
? hostDirectiveDefinitionMap.get(directiveDef)
|
|
12681
|
+
: null;
|
|
12682
|
+
const aliasedInputs = aliasData ? aliasData.inputs : null;
|
|
12683
|
+
const aliasedOutputs = aliasData ? aliasData.outputs : null;
|
|
12684
|
+
inputsStore = captureNodeBindings(0 /* CaptureNodeBindingMode.Inputs */, directiveDef.inputs, directiveIndex, inputsStore, aliasedInputs);
|
|
12685
|
+
outputsStore = captureNodeBindings(1 /* CaptureNodeBindingMode.Outputs */, directiveDef.outputs, directiveIndex, outputsStore, aliasedOutputs);
|
|
12686
|
+
// Do not use unbound attributes as inputs to structural directives, since structural
|
|
12687
|
+
// directive inputs can only be set using microsyntax (e.g. `<div *dir="exp">`).
|
|
12688
|
+
// TODO(FW-1930): microsyntax expressions may also contain unbound/static attributes, which
|
|
12689
|
+
// should be set for inline templates.
|
|
12690
|
+
const initialInputs = inputsStore !== null && tNodeAttrs !== null && !isInlineTemplate(tNode)
|
|
12691
|
+
? generateInitialInputs(inputsStore, directiveIndex, tNodeAttrs)
|
|
12692
|
+
: null;
|
|
12693
|
+
inputsFromAttrs.push(initialInputs);
|
|
12694
|
+
}
|
|
12695
|
+
if (inputsStore !== null) {
|
|
12696
|
+
if (inputsStore.hasOwnProperty('class')) {
|
|
12697
|
+
tNode.flags |= 8 /* TNodeFlags.hasClassInput */;
|
|
12698
|
+
}
|
|
12699
|
+
if (inputsStore.hasOwnProperty('style')) {
|
|
12700
|
+
tNode.flags |= 16 /* TNodeFlags.hasStyleInput */;
|
|
12701
|
+
}
|
|
12702
|
+
}
|
|
12703
|
+
tNode.initialInputs = inputsFromAttrs;
|
|
12704
|
+
tNode.inputs = inputsStore;
|
|
12705
|
+
tNode.outputs = outputsStore;
|
|
12706
|
+
}
|
|
12535
12707
|
/**
|
|
12536
12708
|
* Mapping between attributes names that don't correspond to their element property names.
|
|
12537
12709
|
*
|
|
@@ -12632,6 +12804,126 @@ function setNgReflectProperties(lView, element, type, dataValue, value) {
|
|
|
12632
12804
|
}
|
|
12633
12805
|
}
|
|
12634
12806
|
}
|
|
12807
|
+
/**
|
|
12808
|
+
* Resolve the matched directives on a node.
|
|
12809
|
+
*/
|
|
12810
|
+
function resolveDirectives(tView, lView, tNode, localRefs) {
|
|
12811
|
+
// Please make sure to have explicit type for `exportsMap`. Inferred type triggers bug in
|
|
12812
|
+
// tsickle.
|
|
12813
|
+
ngDevMode && assertFirstCreatePass(tView);
|
|
12814
|
+
if (getBindingsEnabled()) {
|
|
12815
|
+
const exportsMap = localRefs === null ? null : { '': -1 };
|
|
12816
|
+
const matchedDirectiveDefs = findDirectiveDefMatches(tView, tNode);
|
|
12817
|
+
if (matchedDirectiveDefs !== null) {
|
|
12818
|
+
const [directiveDefs, hostDirectiveDefs] = resolveHostDirectives(tView, tNode, matchedDirectiveDefs);
|
|
12819
|
+
initializeDirectives(tView, lView, tNode, directiveDefs, exportsMap, hostDirectiveDefs);
|
|
12820
|
+
}
|
|
12821
|
+
if (exportsMap)
|
|
12822
|
+
cacheMatchingLocalNames(tNode, localRefs, exportsMap);
|
|
12823
|
+
}
|
|
12824
|
+
// Merge the template attrs last so that they have the highest priority.
|
|
12825
|
+
tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, tNode.attrs);
|
|
12826
|
+
}
|
|
12827
|
+
/** Initializes the data structures necessary for a list of directives to be instantiated. */
|
|
12828
|
+
function initializeDirectives(tView, lView, tNode, directives, exportsMap, hostDirectiveDefs) {
|
|
12829
|
+
ngDevMode && assertFirstCreatePass(tView);
|
|
12830
|
+
// Publishes the directive types to DI so they can be injected. Needs to
|
|
12831
|
+
// happen in a separate pass before the TNode flags have been initialized.
|
|
12832
|
+
for (let i = 0; i < directives.length; i++) {
|
|
12833
|
+
diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, directives[i].type);
|
|
12834
|
+
}
|
|
12835
|
+
initTNodeFlags(tNode, tView.data.length, directives.length);
|
|
12836
|
+
// When the same token is provided by several directives on the same node, some rules apply in
|
|
12837
|
+
// the viewEngine:
|
|
12838
|
+
// - viewProviders have priority over providers
|
|
12839
|
+
// - the last directive in NgModule.declarations has priority over the previous one
|
|
12840
|
+
// So to match these rules, the order in which providers are added in the arrays is very
|
|
12841
|
+
// important.
|
|
12842
|
+
for (let i = 0; i < directives.length; i++) {
|
|
12843
|
+
const def = directives[i];
|
|
12844
|
+
if (def.providersResolver)
|
|
12845
|
+
def.providersResolver(def);
|
|
12846
|
+
}
|
|
12847
|
+
let preOrderHooksFound = false;
|
|
12848
|
+
let preOrderCheckHooksFound = false;
|
|
12849
|
+
let directiveIdx = allocExpando(tView, lView, directives.length, null);
|
|
12850
|
+
ngDevMode &&
|
|
12851
|
+
assertSame(directiveIdx, tNode.directiveStart, 'TNode.directiveStart should point to just allocated space');
|
|
12852
|
+
for (let i = 0; i < directives.length; i++) {
|
|
12853
|
+
const def = directives[i];
|
|
12854
|
+
// Merge the attrs in the order of matches. This assumes that the first directive is the
|
|
12855
|
+
// component itself, so that the component has the least priority.
|
|
12856
|
+
tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
|
|
12857
|
+
configureViewWithDirective(tView, tNode, lView, directiveIdx, def);
|
|
12858
|
+
saveNameToExportMap(directiveIdx, def, exportsMap);
|
|
12859
|
+
if (def.contentQueries !== null)
|
|
12860
|
+
tNode.flags |= 4 /* TNodeFlags.hasContentQuery */;
|
|
12861
|
+
if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0)
|
|
12862
|
+
tNode.flags |= 64 /* TNodeFlags.hasHostBindings */;
|
|
12863
|
+
const lifeCycleHooks = def.type.prototype;
|
|
12864
|
+
// Only push a node index into the preOrderHooks array if this is the first
|
|
12865
|
+
// pre-order hook found on this node.
|
|
12866
|
+
if (!preOrderHooksFound &&
|
|
12867
|
+
(lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngOnInit || lifeCycleHooks.ngDoCheck)) {
|
|
12868
|
+
// We will push the actual hook function into this array later during dir instantiation.
|
|
12869
|
+
// We cannot do it now because we must ensure hooks are registered in the same
|
|
12870
|
+
// order that directives are created (i.e. injection order).
|
|
12871
|
+
(tView.preOrderHooks ??= []).push(tNode.index);
|
|
12872
|
+
preOrderHooksFound = true;
|
|
12873
|
+
}
|
|
12874
|
+
if (!preOrderCheckHooksFound && (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngDoCheck)) {
|
|
12875
|
+
(tView.preOrderCheckHooks ??= []).push(tNode.index);
|
|
12876
|
+
preOrderCheckHooksFound = true;
|
|
12877
|
+
}
|
|
12878
|
+
directiveIdx++;
|
|
12879
|
+
}
|
|
12880
|
+
initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefs);
|
|
12881
|
+
}
|
|
12882
|
+
/**
|
|
12883
|
+
* Add `hostBindings` to the `TView.hostBindingOpCodes`.
|
|
12884
|
+
*
|
|
12885
|
+
* @param tView `TView` to which the `hostBindings` should be added.
|
|
12886
|
+
* @param tNode `TNode` the element which contains the directive
|
|
12887
|
+
* @param directiveIdx Directive index in view.
|
|
12888
|
+
* @param directiveVarsIdx Where will the directive's vars be stored
|
|
12889
|
+
* @param def `ComponentDef`/`DirectiveDef`, which contains the `hostVars`/`hostBindings` to add.
|
|
12890
|
+
*/
|
|
12891
|
+
function registerHostBindingOpCodes(tView, tNode, directiveIdx, directiveVarsIdx, def) {
|
|
12892
|
+
ngDevMode && assertFirstCreatePass(tView);
|
|
12893
|
+
const hostBindings = def.hostBindings;
|
|
12894
|
+
if (hostBindings) {
|
|
12895
|
+
let hostBindingOpCodes = tView.hostBindingOpCodes;
|
|
12896
|
+
if (hostBindingOpCodes === null) {
|
|
12897
|
+
hostBindingOpCodes = tView.hostBindingOpCodes = [];
|
|
12898
|
+
}
|
|
12899
|
+
const elementIndx = ~tNode.index;
|
|
12900
|
+
if (lastSelectedElementIdx(hostBindingOpCodes) != elementIndx) {
|
|
12901
|
+
// Conditionally add select element so that we are more efficient in execution.
|
|
12902
|
+
// NOTE: this is strictly not necessary and it trades code size for runtime perf.
|
|
12903
|
+
// (We could just always add it.)
|
|
12904
|
+
hostBindingOpCodes.push(elementIndx);
|
|
12905
|
+
}
|
|
12906
|
+
hostBindingOpCodes.push(directiveIdx, directiveVarsIdx, hostBindings);
|
|
12907
|
+
}
|
|
12908
|
+
}
|
|
12909
|
+
/**
|
|
12910
|
+
* Returns the last selected element index in the `HostBindingOpCodes`
|
|
12911
|
+
*
|
|
12912
|
+
* For perf reasons we don't need to update the selected element index in `HostBindingOpCodes` only
|
|
12913
|
+
* if it changes. This method returns the last index (or '0' if not found.)
|
|
12914
|
+
*
|
|
12915
|
+
* Selected element index are only the ones which are negative.
|
|
12916
|
+
*/
|
|
12917
|
+
function lastSelectedElementIdx(hostBindingOpCodes) {
|
|
12918
|
+
let i = hostBindingOpCodes.length;
|
|
12919
|
+
while (i > 0) {
|
|
12920
|
+
const value = hostBindingOpCodes[--i];
|
|
12921
|
+
if (typeof value === 'number' && value < 0) {
|
|
12922
|
+
return value;
|
|
12923
|
+
}
|
|
12924
|
+
}
|
|
12925
|
+
return 0;
|
|
12926
|
+
}
|
|
12635
12927
|
/**
|
|
12636
12928
|
* Instantiate all the directives that were previously resolved on the current node.
|
|
12637
12929
|
*/
|
|
@@ -12725,6 +13017,121 @@ function findDirectiveDefMatches(tView, tNode) {
|
|
|
12725
13017
|
}
|
|
12726
13018
|
return matches;
|
|
12727
13019
|
}
|
|
13020
|
+
function resolveHostDirectives(tView, tNode, matches) {
|
|
13021
|
+
const allDirectiveDefs = [];
|
|
13022
|
+
let hostDirectiveDefs = null;
|
|
13023
|
+
for (const def of matches) {
|
|
13024
|
+
if (def.findHostDirectiveDefs !== null) {
|
|
13025
|
+
// TODO(pk): probably could return matches instead of taking in an array to fill in?
|
|
13026
|
+
hostDirectiveDefs ??= new Map();
|
|
13027
|
+
// Components are inserted at the front of the matches array so that their lifecycle
|
|
13028
|
+
// hooks run before any directive lifecycle hooks. This appears to be for ViewEngine
|
|
13029
|
+
// compatibility. This logic doesn't make sense with host directives, because it
|
|
13030
|
+
// would allow the host directives to undo any overrides the host may have made.
|
|
13031
|
+
// To handle this case, the host directives of components are inserted at the beginning
|
|
13032
|
+
// of the array, followed by the component. As such, the insertion order is as follows:
|
|
13033
|
+
// 1. Host directives belonging to the selector-matched component.
|
|
13034
|
+
// 2. Selector-matched component.
|
|
13035
|
+
// 3. Host directives belonging to selector-matched directives.
|
|
13036
|
+
// 4. Selector-matched directives.
|
|
13037
|
+
def.findHostDirectiveDefs(def, allDirectiveDefs, hostDirectiveDefs);
|
|
13038
|
+
}
|
|
13039
|
+
if (isComponentDef(def)) {
|
|
13040
|
+
allDirectiveDefs.push(def);
|
|
13041
|
+
markAsComponentHost(tView, tNode, allDirectiveDefs.length - 1);
|
|
13042
|
+
}
|
|
13043
|
+
}
|
|
13044
|
+
if (isComponentHost(tNode)) {
|
|
13045
|
+
allDirectiveDefs.push(...matches.slice(1));
|
|
13046
|
+
}
|
|
13047
|
+
else {
|
|
13048
|
+
allDirectiveDefs.push(...matches);
|
|
13049
|
+
}
|
|
13050
|
+
if (ngDevMode) {
|
|
13051
|
+
assertNoDuplicateDirectives(allDirectiveDefs);
|
|
13052
|
+
}
|
|
13053
|
+
return [allDirectiveDefs, hostDirectiveDefs];
|
|
13054
|
+
}
|
|
13055
|
+
/**
|
|
13056
|
+
* Marks a given TNode as a component's host. This consists of:
|
|
13057
|
+
* - setting the component offset on the TNode.
|
|
13058
|
+
* - storing index of component's host element so it will be queued for view refresh during CD.
|
|
13059
|
+
*/
|
|
13060
|
+
function markAsComponentHost(tView, hostTNode, componentOffset) {
|
|
13061
|
+
ngDevMode && assertFirstCreatePass(tView);
|
|
13062
|
+
ngDevMode && assertGreaterThan(componentOffset, -1, 'componentOffset must be great than -1');
|
|
13063
|
+
hostTNode.componentOffset = componentOffset;
|
|
13064
|
+
(tView.components ??= []).push(hostTNode.index);
|
|
13065
|
+
}
|
|
13066
|
+
/** Caches local names and their matching directive indices for query and template lookups. */
|
|
13067
|
+
function cacheMatchingLocalNames(tNode, localRefs, exportsMap) {
|
|
13068
|
+
if (localRefs) {
|
|
13069
|
+
const localNames = (tNode.localNames = []);
|
|
13070
|
+
// Local names must be stored in tNode in the same order that localRefs are defined
|
|
13071
|
+
// in the template to ensure the data is loaded in the same slots as their refs
|
|
13072
|
+
// in the template (for template queries).
|
|
13073
|
+
for (let i = 0; i < localRefs.length; i += 2) {
|
|
13074
|
+
const index = exportsMap[localRefs[i + 1]];
|
|
13075
|
+
if (index == null)
|
|
13076
|
+
throw new RuntimeError(-301 /* RuntimeErrorCode.EXPORT_NOT_FOUND */, ngDevMode && `Export of name '${localRefs[i + 1]}' not found!`);
|
|
13077
|
+
localNames.push(localRefs[i], index);
|
|
13078
|
+
}
|
|
13079
|
+
}
|
|
13080
|
+
}
|
|
13081
|
+
/**
|
|
13082
|
+
* Builds up an export map as directives are created, so local refs can be quickly mapped
|
|
13083
|
+
* to their directive instances.
|
|
13084
|
+
*/
|
|
13085
|
+
function saveNameToExportMap(directiveIdx, def, exportsMap) {
|
|
13086
|
+
if (exportsMap) {
|
|
13087
|
+
if (def.exportAs) {
|
|
13088
|
+
for (let i = 0; i < def.exportAs.length; i++) {
|
|
13089
|
+
exportsMap[def.exportAs[i]] = directiveIdx;
|
|
13090
|
+
}
|
|
13091
|
+
}
|
|
13092
|
+
if (isComponentDef(def))
|
|
13093
|
+
exportsMap[''] = directiveIdx;
|
|
13094
|
+
}
|
|
13095
|
+
}
|
|
13096
|
+
/**
|
|
13097
|
+
* Initializes the flags on the current node, setting all indices to the initial index,
|
|
13098
|
+
* the directive count to 0, and adding the isComponent flag.
|
|
13099
|
+
* @param index the initial index
|
|
13100
|
+
*/
|
|
13101
|
+
function initTNodeFlags(tNode, index, numberOfDirectives) {
|
|
13102
|
+
ngDevMode &&
|
|
13103
|
+
assertNotEqual(numberOfDirectives, tNode.directiveEnd - tNode.directiveStart, 'Reached the max number of directives');
|
|
13104
|
+
tNode.flags |= 1 /* TNodeFlags.isDirectiveHost */;
|
|
13105
|
+
// When the first directive is created on a node, save the index
|
|
13106
|
+
tNode.directiveStart = index;
|
|
13107
|
+
tNode.directiveEnd = index + numberOfDirectives;
|
|
13108
|
+
tNode.providerIndexes = index;
|
|
13109
|
+
}
|
|
13110
|
+
/**
|
|
13111
|
+
* Setup directive for instantiation.
|
|
13112
|
+
*
|
|
13113
|
+
* We need to create a `NodeInjectorFactory` which is then inserted in both the `Blueprint` as well
|
|
13114
|
+
* as `LView`. `TView` gets the `DirectiveDef`.
|
|
13115
|
+
*
|
|
13116
|
+
* @param tView `TView`
|
|
13117
|
+
* @param tNode `TNode`
|
|
13118
|
+
* @param lView `LView`
|
|
13119
|
+
* @param directiveIndex Index where the directive will be stored in the Expando.
|
|
13120
|
+
* @param def `DirectiveDef`
|
|
13121
|
+
*/
|
|
13122
|
+
function configureViewWithDirective(tView, tNode, lView, directiveIndex, def) {
|
|
13123
|
+
ngDevMode &&
|
|
13124
|
+
assertGreaterThanOrEqual(directiveIndex, HEADER_OFFSET, 'Must be in Expando section');
|
|
13125
|
+
tView.data[directiveIndex] = def;
|
|
13126
|
+
const directiveFactory = def.factory || (def.factory = getFactoryDef(def.type, true));
|
|
13127
|
+
// Even though `directiveFactory` will already be using `ɵɵdirectiveInject` in its generated code,
|
|
13128
|
+
// we also want to support `inject()` directly from the directive constructor context so we set
|
|
13129
|
+
// `ɵɵdirectiveInject` as the inject implementation here too.
|
|
13130
|
+
const nodeInjectorFactory = new NodeInjectorFactory(directiveFactory, isComponentDef(def), ɵɵdirectiveInject);
|
|
13131
|
+
tView.blueprint[directiveIndex] = nodeInjectorFactory;
|
|
13132
|
+
lView[directiveIndex] = nodeInjectorFactory;
|
|
13133
|
+
registerHostBindingOpCodes(tView, tNode, directiveIndex, allocExpando(tView, lView, def.hostVars, NO_CHANGE), def);
|
|
13134
|
+
}
|
|
12728
13135
|
/**
|
|
12729
13136
|
* Gets the initial set of LView flags based on the component definition that the LView represents.
|
|
12730
13137
|
* @param def Component definition from which to determine the flags.
|
|
@@ -12796,6 +13203,58 @@ function setInputsFromAttrs(lView, directiveIndex, instance, def, tNode, initial
|
|
|
12796
13203
|
}
|
|
12797
13204
|
}
|
|
12798
13205
|
}
|
|
13206
|
+
/**
|
|
13207
|
+
* Generates initialInputData for a node and stores it in the template's static storage
|
|
13208
|
+
* so subsequent template invocations don't have to recalculate it.
|
|
13209
|
+
*
|
|
13210
|
+
* initialInputData is an array containing values that need to be set as input properties
|
|
13211
|
+
* for directives on this node, but only once on creation. We need this array to support
|
|
13212
|
+
* the case where you set an @Input property of a directive using attribute-like syntax.
|
|
13213
|
+
* e.g. if you have a `name` @Input, you can set it once like this:
|
|
13214
|
+
*
|
|
13215
|
+
* <my-component name="Bess"></my-component>
|
|
13216
|
+
*
|
|
13217
|
+
* @param inputs Input alias map that was generated from the directive def inputs.
|
|
13218
|
+
* @param directiveIndex Index of the directive that is currently being processed.
|
|
13219
|
+
* @param attrs Static attrs on this node.
|
|
13220
|
+
*/
|
|
13221
|
+
function generateInitialInputs(inputs, directiveIndex, attrs) {
|
|
13222
|
+
let inputsToStore = null;
|
|
13223
|
+
let i = 0;
|
|
13224
|
+
while (i < attrs.length) {
|
|
13225
|
+
const attrName = attrs[i];
|
|
13226
|
+
if (attrName === 0 /* AttributeMarker.NamespaceURI */) {
|
|
13227
|
+
// We do not allow inputs on namespaced attributes.
|
|
13228
|
+
i += 4;
|
|
13229
|
+
continue;
|
|
13230
|
+
}
|
|
13231
|
+
else if (attrName === 5 /* AttributeMarker.ProjectAs */) {
|
|
13232
|
+
// Skip over the `ngProjectAs` value.
|
|
13233
|
+
i += 2;
|
|
13234
|
+
continue;
|
|
13235
|
+
}
|
|
13236
|
+
// If we hit any other attribute markers, we're done anyway. None of those are valid inputs.
|
|
13237
|
+
if (typeof attrName === 'number')
|
|
13238
|
+
break;
|
|
13239
|
+
if (inputs.hasOwnProperty(attrName)) {
|
|
13240
|
+
if (inputsToStore === null)
|
|
13241
|
+
inputsToStore = [];
|
|
13242
|
+
// Find the input's public name from the input store. Note that we can be found easier
|
|
13243
|
+
// through the directive def, but we want to do it using the inputs store so that it can
|
|
13244
|
+
// account for host directive aliases.
|
|
13245
|
+
const inputConfig = inputs[attrName];
|
|
13246
|
+
for (let j = 0; j < inputConfig.length; j += 3) {
|
|
13247
|
+
if (inputConfig[j] === directiveIndex) {
|
|
13248
|
+
inputsToStore.push(attrName, inputConfig[j + 1], inputConfig[j + 2], attrs[i + 1]);
|
|
13249
|
+
// A directive can't have multiple inputs with the same name so we can break here.
|
|
13250
|
+
break;
|
|
13251
|
+
}
|
|
13252
|
+
}
|
|
13253
|
+
}
|
|
13254
|
+
i += 2;
|
|
13255
|
+
}
|
|
13256
|
+
return inputsToStore;
|
|
13257
|
+
}
|
|
12799
13258
|
//////////////////////////
|
|
12800
13259
|
//// ViewContainer & View
|
|
12801
13260
|
//////////////////////////
|
|
@@ -12946,7 +13405,9 @@ function renderComponent(hostLView, componentHostIdx) {
|
|
|
12946
13405
|
if (hostRNode !== null && componentView[HYDRATION] === null) {
|
|
12947
13406
|
componentView[HYDRATION] = retrieveHydrationInfo(hostRNode, componentView[INJECTOR]);
|
|
12948
13407
|
}
|
|
13408
|
+
profiler(18 /* ProfilerEvent.ComponentStart */);
|
|
12949
13409
|
renderView(componentTView, componentView, componentView[CONTEXT]);
|
|
13410
|
+
profiler(19 /* ProfilerEvent.ComponentEnd */, componentView[CONTEXT]);
|
|
12950
13411
|
}
|
|
12951
13412
|
/**
|
|
12952
13413
|
* Syncs an LView instance with its blueprint if they have gotten out of sync.
|
|
@@ -14487,8 +14948,10 @@ function markTransplantedViewsForRefresh(lView) {
|
|
|
14487
14948
|
*/
|
|
14488
14949
|
function detectChangesInComponent(hostLView, componentHostIdx, mode) {
|
|
14489
14950
|
ngDevMode && assertEqual(isCreationMode(hostLView), false, 'Should be run in update mode');
|
|
14951
|
+
profiler(18 /* ProfilerEvent.ComponentStart */);
|
|
14490
14952
|
const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
|
|
14491
14953
|
detectChangesInViewIfAttached(componentView, mode);
|
|
14954
|
+
profiler(19 /* ProfilerEvent.ComponentEnd */, componentView[CONTEXT]);
|
|
14492
14955
|
}
|
|
14493
14956
|
/**
|
|
14494
14957
|
* Visits a view as part of change detection traversal.
|
|
@@ -17330,492 +17793,6 @@ function computeStaticStyling(tNode, attrs, writeToHost) {
|
|
|
17330
17793
|
writeToHost ? (tNode.classes = classes) : (tNode.classesWithoutHost = classes);
|
|
17331
17794
|
}
|
|
17332
17795
|
|
|
17333
|
-
function ɵɵdirectiveInject(token, flags = InjectFlags.Default) {
|
|
17334
|
-
const lView = getLView();
|
|
17335
|
-
// Fall back to inject() if view hasn't been created. This situation can happen in tests
|
|
17336
|
-
// if inject utilities are used before bootstrapping.
|
|
17337
|
-
if (lView === null) {
|
|
17338
|
-
// Verify that we will not get into infinite loop.
|
|
17339
|
-
ngDevMode && assertInjectImplementationNotEqual(ɵɵdirectiveInject);
|
|
17340
|
-
return ɵɵinject(token, flags);
|
|
17341
|
-
}
|
|
17342
|
-
const tNode = getCurrentTNode();
|
|
17343
|
-
const value = getOrCreateInjectable(tNode, lView, resolveForwardRef(token), flags);
|
|
17344
|
-
ngDevMode && emitInjectEvent(token, value, flags);
|
|
17345
|
-
return value;
|
|
17346
|
-
}
|
|
17347
|
-
/**
|
|
17348
|
-
* Throws an error indicating that a factory function could not be generated by the compiler for a
|
|
17349
|
-
* particular class.
|
|
17350
|
-
*
|
|
17351
|
-
* This instruction allows the actual error message to be optimized away when ngDevMode is turned
|
|
17352
|
-
* off, saving bytes of generated code while still providing a good experience in dev mode.
|
|
17353
|
-
*
|
|
17354
|
-
* The name of the class is not mentioned here, but will be in the generated factory function name
|
|
17355
|
-
* and thus in the stack trace.
|
|
17356
|
-
*
|
|
17357
|
-
* @codeGenApi
|
|
17358
|
-
*/
|
|
17359
|
-
function ɵɵinvalidFactory() {
|
|
17360
|
-
const msg = ngDevMode
|
|
17361
|
-
? `This constructor was not compatible with Dependency Injection.`
|
|
17362
|
-
: 'invalid';
|
|
17363
|
-
throw new Error(msg);
|
|
17364
|
-
}
|
|
17365
|
-
|
|
17366
|
-
/**
|
|
17367
|
-
* When elements are created dynamically after a view blueprint is created (e.g. through
|
|
17368
|
-
* i18nApply()), we need to adjust the blueprint for future template passes.
|
|
17369
|
-
*
|
|
17370
|
-
* @param tView `TView` associated with `LView`
|
|
17371
|
-
* @param lView The `LView` containing the blueprint to adjust
|
|
17372
|
-
* @param numSlotsToAlloc The number of slots to alloc in the LView, should be >0
|
|
17373
|
-
* @param initialValue Initial value to store in blueprint
|
|
17374
|
-
*/
|
|
17375
|
-
function allocExpando(tView, lView, numSlotsToAlloc, initialValue) {
|
|
17376
|
-
if (numSlotsToAlloc === 0)
|
|
17377
|
-
return -1;
|
|
17378
|
-
if (ngDevMode) {
|
|
17379
|
-
assertFirstCreatePass(tView);
|
|
17380
|
-
assertSame(tView, lView[TVIEW], '`LView` must be associated with `TView`!');
|
|
17381
|
-
assertEqual(tView.data.length, lView.length, 'Expecting LView to be same size as TView');
|
|
17382
|
-
assertEqual(tView.data.length, tView.blueprint.length, 'Expecting Blueprint to be same size as TView');
|
|
17383
|
-
assertFirstUpdatePass(tView);
|
|
17384
|
-
}
|
|
17385
|
-
const allocIdx = lView.length;
|
|
17386
|
-
for (let i = 0; i < numSlotsToAlloc; i++) {
|
|
17387
|
-
lView.push(initialValue);
|
|
17388
|
-
tView.blueprint.push(initialValue);
|
|
17389
|
-
tView.data.push(null);
|
|
17390
|
-
}
|
|
17391
|
-
return allocIdx;
|
|
17392
|
-
}
|
|
17393
|
-
|
|
17394
|
-
/**
|
|
17395
|
-
* Resolve the matched directives on a node.
|
|
17396
|
-
*/
|
|
17397
|
-
function resolveDirectives(tView, lView, tNode, localRefs, directiveMatcher) {
|
|
17398
|
-
// Please make sure to have explicit type for `exportsMap`. Inferred type triggers bug in
|
|
17399
|
-
// tsickle.
|
|
17400
|
-
ngDevMode && assertFirstCreatePass(tView);
|
|
17401
|
-
const exportsMap = localRefs === null ? null : { '': -1 };
|
|
17402
|
-
const matchedDirectiveDefs = directiveMatcher(tView, tNode);
|
|
17403
|
-
if (matchedDirectiveDefs !== null) {
|
|
17404
|
-
const [directiveDefs, hostDirectiveDefs] = resolveHostDirectives(tView, tNode, matchedDirectiveDefs);
|
|
17405
|
-
initializeDirectives(tView, lView, tNode, directiveDefs, exportsMap, hostDirectiveDefs);
|
|
17406
|
-
}
|
|
17407
|
-
if (exportsMap !== null && localRefs !== null) {
|
|
17408
|
-
cacheMatchingLocalNames(tNode, localRefs, exportsMap);
|
|
17409
|
-
}
|
|
17410
|
-
}
|
|
17411
|
-
/** Caches local names and their matching directive indices for query and template lookups. */
|
|
17412
|
-
function cacheMatchingLocalNames(tNode, localRefs, exportsMap) {
|
|
17413
|
-
const localNames = (tNode.localNames = []);
|
|
17414
|
-
// Local names must be stored in tNode in the same order that localRefs are defined
|
|
17415
|
-
// in the template to ensure the data is loaded in the same slots as their refs
|
|
17416
|
-
// in the template (for template queries).
|
|
17417
|
-
for (let i = 0; i < localRefs.length; i += 2) {
|
|
17418
|
-
const index = exportsMap[localRefs[i + 1]];
|
|
17419
|
-
if (index == null)
|
|
17420
|
-
throw new RuntimeError(-301 /* RuntimeErrorCode.EXPORT_NOT_FOUND */, ngDevMode && `Export of name '${localRefs[i + 1]}' not found!`);
|
|
17421
|
-
localNames.push(localRefs[i], index);
|
|
17422
|
-
}
|
|
17423
|
-
}
|
|
17424
|
-
function resolveHostDirectives(tView, tNode, matches) {
|
|
17425
|
-
const allDirectiveDefs = [];
|
|
17426
|
-
let hostDirectiveDefs = null;
|
|
17427
|
-
for (const def of matches) {
|
|
17428
|
-
if (def.findHostDirectiveDefs !== null) {
|
|
17429
|
-
// TODO(pk): probably could return matches instead of taking in an array to fill in?
|
|
17430
|
-
hostDirectiveDefs ??= new Map();
|
|
17431
|
-
// Components are inserted at the front of the matches array so that their lifecycle
|
|
17432
|
-
// hooks run before any directive lifecycle hooks. This appears to be for ViewEngine
|
|
17433
|
-
// compatibility. This logic doesn't make sense with host directives, because it
|
|
17434
|
-
// would allow the host directives to undo any overrides the host may have made.
|
|
17435
|
-
// To handle this case, the host directives of components are inserted at the beginning
|
|
17436
|
-
// of the array, followed by the component. As such, the insertion order is as follows:
|
|
17437
|
-
// 1. Host directives belonging to the selector-matched component.
|
|
17438
|
-
// 2. Selector-matched component.
|
|
17439
|
-
// 3. Host directives belonging to selector-matched directives.
|
|
17440
|
-
// 4. Selector-matched directives.
|
|
17441
|
-
def.findHostDirectiveDefs(def, allDirectiveDefs, hostDirectiveDefs);
|
|
17442
|
-
}
|
|
17443
|
-
if (isComponentDef(def)) {
|
|
17444
|
-
allDirectiveDefs.push(def);
|
|
17445
|
-
markAsComponentHost(tView, tNode, allDirectiveDefs.length - 1);
|
|
17446
|
-
}
|
|
17447
|
-
}
|
|
17448
|
-
if (isComponentHost(tNode)) {
|
|
17449
|
-
allDirectiveDefs.push(...matches.slice(1));
|
|
17450
|
-
}
|
|
17451
|
-
else {
|
|
17452
|
-
allDirectiveDefs.push(...matches);
|
|
17453
|
-
}
|
|
17454
|
-
if (ngDevMode) {
|
|
17455
|
-
assertNoDuplicateDirectives(allDirectiveDefs);
|
|
17456
|
-
}
|
|
17457
|
-
return [allDirectiveDefs, hostDirectiveDefs];
|
|
17458
|
-
}
|
|
17459
|
-
/**
|
|
17460
|
-
* Marks a given TNode as a component's host. This consists of:
|
|
17461
|
-
* - setting the component offset on the TNode.
|
|
17462
|
-
* - storing index of component's host element so it will be queued for view refresh during CD.
|
|
17463
|
-
*/
|
|
17464
|
-
function markAsComponentHost(tView, hostTNode, componentOffset) {
|
|
17465
|
-
ngDevMode && assertFirstCreatePass(tView);
|
|
17466
|
-
ngDevMode && assertGreaterThan(componentOffset, -1, 'componentOffset must be great than -1');
|
|
17467
|
-
hostTNode.componentOffset = componentOffset;
|
|
17468
|
-
(tView.components ??= []).push(hostTNode.index);
|
|
17469
|
-
}
|
|
17470
|
-
/** Initializes the data structures necessary for a list of directives to be instantiated. */
|
|
17471
|
-
function initializeDirectives(tView, lView, tNode, directives, exportsMap, hostDirectiveDefs) {
|
|
17472
|
-
ngDevMode && assertFirstCreatePass(tView);
|
|
17473
|
-
// Publishes the directive types to DI so they can be injected. Needs to
|
|
17474
|
-
// happen in a separate pass before the TNode flags have been initialized.
|
|
17475
|
-
for (let i = 0; i < directives.length; i++) {
|
|
17476
|
-
diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, directives[i].type);
|
|
17477
|
-
}
|
|
17478
|
-
initTNodeFlags(tNode, tView.data.length, directives.length);
|
|
17479
|
-
// When the same token is provided by several directives on the same node, some rules apply in
|
|
17480
|
-
// the viewEngine:
|
|
17481
|
-
// - viewProviders have priority over providers
|
|
17482
|
-
// - the last directive in NgModule.declarations has priority over the previous one
|
|
17483
|
-
// So to match these rules, the order in which providers are added in the arrays is very
|
|
17484
|
-
// important.
|
|
17485
|
-
for (let i = 0; i < directives.length; i++) {
|
|
17486
|
-
const def = directives[i];
|
|
17487
|
-
if (def.providersResolver)
|
|
17488
|
-
def.providersResolver(def);
|
|
17489
|
-
}
|
|
17490
|
-
let preOrderHooksFound = false;
|
|
17491
|
-
let preOrderCheckHooksFound = false;
|
|
17492
|
-
let directiveIdx = allocExpando(tView, lView, directives.length, null);
|
|
17493
|
-
ngDevMode &&
|
|
17494
|
-
assertSame(directiveIdx, tNode.directiveStart, 'TNode.directiveStart should point to just allocated space');
|
|
17495
|
-
for (let i = 0; i < directives.length; i++) {
|
|
17496
|
-
const def = directives[i];
|
|
17497
|
-
// Merge the attrs in the order of matches. This assumes that the first directive is the
|
|
17498
|
-
// component itself, so that the component has the least priority.
|
|
17499
|
-
tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
|
|
17500
|
-
configureViewWithDirective(tView, tNode, lView, directiveIdx, def);
|
|
17501
|
-
saveNameToExportMap(directiveIdx, def, exportsMap);
|
|
17502
|
-
if (def.contentQueries !== null)
|
|
17503
|
-
tNode.flags |= 4 /* TNodeFlags.hasContentQuery */;
|
|
17504
|
-
if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0)
|
|
17505
|
-
tNode.flags |= 64 /* TNodeFlags.hasHostBindings */;
|
|
17506
|
-
const lifeCycleHooks = def.type.prototype;
|
|
17507
|
-
// Only push a node index into the preOrderHooks array if this is the first
|
|
17508
|
-
// pre-order hook found on this node.
|
|
17509
|
-
if (!preOrderHooksFound &&
|
|
17510
|
-
(lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngOnInit || lifeCycleHooks.ngDoCheck)) {
|
|
17511
|
-
// We will push the actual hook function into this array later during dir instantiation.
|
|
17512
|
-
// We cannot do it now because we must ensure hooks are registered in the same
|
|
17513
|
-
// order that directives are created (i.e. injection order).
|
|
17514
|
-
(tView.preOrderHooks ??= []).push(tNode.index);
|
|
17515
|
-
preOrderHooksFound = true;
|
|
17516
|
-
}
|
|
17517
|
-
if (!preOrderCheckHooksFound && (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngDoCheck)) {
|
|
17518
|
-
(tView.preOrderCheckHooks ??= []).push(tNode.index);
|
|
17519
|
-
preOrderCheckHooksFound = true;
|
|
17520
|
-
}
|
|
17521
|
-
directiveIdx++;
|
|
17522
|
-
}
|
|
17523
|
-
initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefs);
|
|
17524
|
-
}
|
|
17525
|
-
/**
|
|
17526
|
-
* Initializes data structures required to work with directive inputs and outputs.
|
|
17527
|
-
* Initialization is done for all directives matched on a given TNode.
|
|
17528
|
-
*/
|
|
17529
|
-
function initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefinitionMap) {
|
|
17530
|
-
ngDevMode && assertFirstCreatePass(tView);
|
|
17531
|
-
const start = tNode.directiveStart;
|
|
17532
|
-
const end = tNode.directiveEnd;
|
|
17533
|
-
const tViewData = tView.data;
|
|
17534
|
-
const tNodeAttrs = tNode.attrs;
|
|
17535
|
-
const inputsFromAttrs = [];
|
|
17536
|
-
let inputsStore = null;
|
|
17537
|
-
let outputsStore = null;
|
|
17538
|
-
for (let directiveIndex = start; directiveIndex < end; directiveIndex++) {
|
|
17539
|
-
const directiveDef = tViewData[directiveIndex];
|
|
17540
|
-
const aliasData = hostDirectiveDefinitionMap
|
|
17541
|
-
? hostDirectiveDefinitionMap.get(directiveDef)
|
|
17542
|
-
: null;
|
|
17543
|
-
const aliasedInputs = aliasData ? aliasData.inputs : null;
|
|
17544
|
-
const aliasedOutputs = aliasData ? aliasData.outputs : null;
|
|
17545
|
-
inputsStore = captureNodeBindings(0 /* CaptureNodeBindingMode.Inputs */, directiveDef.inputs, directiveIndex, inputsStore, aliasedInputs);
|
|
17546
|
-
outputsStore = captureNodeBindings(1 /* CaptureNodeBindingMode.Outputs */, directiveDef.outputs, directiveIndex, outputsStore, aliasedOutputs);
|
|
17547
|
-
// Do not use unbound attributes as inputs to structural directives, since structural
|
|
17548
|
-
// directive inputs can only be set using microsyntax (e.g. `<div *dir="exp">`).
|
|
17549
|
-
const initialInputs = inputsStore !== null && tNodeAttrs !== null && !isInlineTemplate(tNode)
|
|
17550
|
-
? generateInitialInputs(inputsStore, directiveIndex, tNodeAttrs)
|
|
17551
|
-
: null;
|
|
17552
|
-
inputsFromAttrs.push(initialInputs);
|
|
17553
|
-
}
|
|
17554
|
-
if (inputsStore !== null) {
|
|
17555
|
-
if (inputsStore.hasOwnProperty('class')) {
|
|
17556
|
-
tNode.flags |= 8 /* TNodeFlags.hasClassInput */;
|
|
17557
|
-
}
|
|
17558
|
-
if (inputsStore.hasOwnProperty('style')) {
|
|
17559
|
-
tNode.flags |= 16 /* TNodeFlags.hasStyleInput */;
|
|
17560
|
-
}
|
|
17561
|
-
}
|
|
17562
|
-
tNode.initialInputs = inputsFromAttrs;
|
|
17563
|
-
tNode.inputs = inputsStore;
|
|
17564
|
-
tNode.outputs = outputsStore;
|
|
17565
|
-
}
|
|
17566
|
-
function captureNodeBindings(mode, aliasMap, directiveIndex, bindingsResult, hostDirectiveAliasMap) {
|
|
17567
|
-
for (let publicName in aliasMap) {
|
|
17568
|
-
if (!aliasMap.hasOwnProperty(publicName)) {
|
|
17569
|
-
continue;
|
|
17570
|
-
}
|
|
17571
|
-
const value = aliasMap[publicName];
|
|
17572
|
-
if (value === undefined) {
|
|
17573
|
-
continue;
|
|
17574
|
-
}
|
|
17575
|
-
bindingsResult ??= {};
|
|
17576
|
-
let internalName;
|
|
17577
|
-
let inputFlags = InputFlags.None;
|
|
17578
|
-
// For inputs, the value might be an array capturing additional
|
|
17579
|
-
// input flags.
|
|
17580
|
-
if (Array.isArray(value)) {
|
|
17581
|
-
internalName = value[0];
|
|
17582
|
-
inputFlags = value[1];
|
|
17583
|
-
}
|
|
17584
|
-
else {
|
|
17585
|
-
internalName = value;
|
|
17586
|
-
}
|
|
17587
|
-
// If there are no host directive mappings, we want to remap using the alias map from the
|
|
17588
|
-
// definition itself. If there is an alias map, it has two functions:
|
|
17589
|
-
// 1. It serves as an allowlist of bindings that are exposed by the host directives. Only the
|
|
17590
|
-
// ones inside the host directive map will be exposed on the host.
|
|
17591
|
-
// 2. The public name of the property is aliased using the host directive alias map, rather
|
|
17592
|
-
// than the alias map from the definition.
|
|
17593
|
-
let finalPublicName = publicName;
|
|
17594
|
-
if (hostDirectiveAliasMap !== null) {
|
|
17595
|
-
// If there is no mapping, it's not part of the allowlist and this input/output
|
|
17596
|
-
// is not captured and should be ignored.
|
|
17597
|
-
if (!hostDirectiveAliasMap.hasOwnProperty(publicName)) {
|
|
17598
|
-
continue;
|
|
17599
|
-
}
|
|
17600
|
-
finalPublicName = hostDirectiveAliasMap[publicName];
|
|
17601
|
-
}
|
|
17602
|
-
if (mode === 0 /* CaptureNodeBindingMode.Inputs */) {
|
|
17603
|
-
addPropertyBinding(bindingsResult, directiveIndex, finalPublicName, internalName, inputFlags);
|
|
17604
|
-
}
|
|
17605
|
-
else {
|
|
17606
|
-
addPropertyBinding(bindingsResult, directiveIndex, finalPublicName, internalName);
|
|
17607
|
-
}
|
|
17608
|
-
}
|
|
17609
|
-
return bindingsResult;
|
|
17610
|
-
}
|
|
17611
|
-
function addPropertyBinding(bindings, directiveIndex, publicName, internalName, inputFlags) {
|
|
17612
|
-
let values;
|
|
17613
|
-
if (bindings.hasOwnProperty(publicName)) {
|
|
17614
|
-
(values = bindings[publicName]).push(directiveIndex, internalName);
|
|
17615
|
-
}
|
|
17616
|
-
else {
|
|
17617
|
-
values = bindings[publicName] = [directiveIndex, internalName];
|
|
17618
|
-
}
|
|
17619
|
-
if (inputFlags !== undefined) {
|
|
17620
|
-
values.push(inputFlags);
|
|
17621
|
-
}
|
|
17622
|
-
}
|
|
17623
|
-
/**
|
|
17624
|
-
* Generates initialInputData for a node and stores it in the template's static storage
|
|
17625
|
-
* so subsequent template invocations don't have to recalculate it.
|
|
17626
|
-
*
|
|
17627
|
-
* initialInputData is an array containing values that need to be set as input properties
|
|
17628
|
-
* for directives on this node, but only once on creation. We need this array to support
|
|
17629
|
-
* the case where you set an @Input property of a directive using attribute-like syntax.
|
|
17630
|
-
* e.g. if you have a `name` @Input, you can set it once like this:
|
|
17631
|
-
*
|
|
17632
|
-
* <my-component name="Bess"></my-component>
|
|
17633
|
-
*
|
|
17634
|
-
* @param inputs Input alias map that was generated from the directive def inputs.
|
|
17635
|
-
* @param directiveIndex Index of the directive that is currently being processed.
|
|
17636
|
-
* @param attrs Static attrs on this node.
|
|
17637
|
-
*/
|
|
17638
|
-
function generateInitialInputs(inputs, directiveIndex, attrs) {
|
|
17639
|
-
let inputsToStore = null;
|
|
17640
|
-
let i = 0;
|
|
17641
|
-
while (i < attrs.length) {
|
|
17642
|
-
const attrName = attrs[i];
|
|
17643
|
-
if (attrName === 0 /* AttributeMarker.NamespaceURI */) {
|
|
17644
|
-
// We do not allow inputs on namespaced attributes.
|
|
17645
|
-
i += 4;
|
|
17646
|
-
continue;
|
|
17647
|
-
}
|
|
17648
|
-
else if (attrName === 5 /* AttributeMarker.ProjectAs */) {
|
|
17649
|
-
// Skip over the `ngProjectAs` value.
|
|
17650
|
-
i += 2;
|
|
17651
|
-
continue;
|
|
17652
|
-
}
|
|
17653
|
-
// If we hit any other attribute markers, we're done anyway. None of those are valid inputs.
|
|
17654
|
-
if (typeof attrName === 'number')
|
|
17655
|
-
break;
|
|
17656
|
-
if (inputs.hasOwnProperty(attrName)) {
|
|
17657
|
-
if (inputsToStore === null)
|
|
17658
|
-
inputsToStore = [];
|
|
17659
|
-
// Find the input's public name from the input store. Note that we can be found easier
|
|
17660
|
-
// through the directive def, but we want to do it using the inputs store so that it can
|
|
17661
|
-
// account for host directive aliases.
|
|
17662
|
-
const inputConfig = inputs[attrName];
|
|
17663
|
-
for (let j = 0; j < inputConfig.length; j += 3) {
|
|
17664
|
-
if (inputConfig[j] === directiveIndex) {
|
|
17665
|
-
inputsToStore.push(attrName, inputConfig[j + 1], inputConfig[j + 2], attrs[i + 1]);
|
|
17666
|
-
// A directive can't have multiple inputs with the same name so we can break here.
|
|
17667
|
-
break;
|
|
17668
|
-
}
|
|
17669
|
-
}
|
|
17670
|
-
}
|
|
17671
|
-
i += 2;
|
|
17672
|
-
}
|
|
17673
|
-
return inputsToStore;
|
|
17674
|
-
}
|
|
17675
|
-
/**
|
|
17676
|
-
* Setup directive for instantiation.
|
|
17677
|
-
*
|
|
17678
|
-
* We need to create a `NodeInjectorFactory` which is then inserted in both the `Blueprint` as well
|
|
17679
|
-
* as `LView`. `TView` gets the `DirectiveDef`.
|
|
17680
|
-
*
|
|
17681
|
-
* @param tView `TView`
|
|
17682
|
-
* @param tNode `TNode`
|
|
17683
|
-
* @param lView `LView`
|
|
17684
|
-
* @param directiveIndex Index where the directive will be stored in the Expando.
|
|
17685
|
-
* @param def `DirectiveDef`
|
|
17686
|
-
*/
|
|
17687
|
-
function configureViewWithDirective(tView, tNode, lView, directiveIndex, def) {
|
|
17688
|
-
ngDevMode &&
|
|
17689
|
-
assertGreaterThanOrEqual(directiveIndex, HEADER_OFFSET, 'Must be in Expando section');
|
|
17690
|
-
tView.data[directiveIndex] = def;
|
|
17691
|
-
const directiveFactory = def.factory || (def.factory = getFactoryDef(def.type, true));
|
|
17692
|
-
// Even though `directiveFactory` will already be using `ɵɵdirectiveInject` in its generated code,
|
|
17693
|
-
// we also want to support `inject()` directly from the directive constructor context so we set
|
|
17694
|
-
// `ɵɵdirectiveInject` as the inject implementation here too.
|
|
17695
|
-
const nodeInjectorFactory = new NodeInjectorFactory(directiveFactory, isComponentDef(def), ɵɵdirectiveInject);
|
|
17696
|
-
tView.blueprint[directiveIndex] = nodeInjectorFactory;
|
|
17697
|
-
lView[directiveIndex] = nodeInjectorFactory;
|
|
17698
|
-
registerHostBindingOpCodes(tView, tNode, directiveIndex, allocExpando(tView, lView, def.hostVars, NO_CHANGE), def);
|
|
17699
|
-
}
|
|
17700
|
-
/**
|
|
17701
|
-
* Add `hostBindings` to the `TView.hostBindingOpCodes`.
|
|
17702
|
-
*
|
|
17703
|
-
* @param tView `TView` to which the `hostBindings` should be added.
|
|
17704
|
-
* @param tNode `TNode` the element which contains the directive
|
|
17705
|
-
* @param directiveIdx Directive index in view.
|
|
17706
|
-
* @param directiveVarsIdx Where will the directive's vars be stored
|
|
17707
|
-
* @param def `ComponentDef`/`DirectiveDef`, which contains the `hostVars`/`hostBindings` to add.
|
|
17708
|
-
*/
|
|
17709
|
-
function registerHostBindingOpCodes(tView, tNode, directiveIdx, directiveVarsIdx, def) {
|
|
17710
|
-
ngDevMode && assertFirstCreatePass(tView);
|
|
17711
|
-
const hostBindings = def.hostBindings;
|
|
17712
|
-
if (hostBindings) {
|
|
17713
|
-
let hostBindingOpCodes = tView.hostBindingOpCodes;
|
|
17714
|
-
if (hostBindingOpCodes === null) {
|
|
17715
|
-
hostBindingOpCodes = tView.hostBindingOpCodes = [];
|
|
17716
|
-
}
|
|
17717
|
-
const elementIndx = ~tNode.index;
|
|
17718
|
-
if (lastSelectedElementIdx(hostBindingOpCodes) != elementIndx) {
|
|
17719
|
-
// Conditionally add select element so that we are more efficient in execution.
|
|
17720
|
-
// NOTE: this is strictly not necessary and it trades code size for runtime perf.
|
|
17721
|
-
// (We could just always add it.)
|
|
17722
|
-
hostBindingOpCodes.push(elementIndx);
|
|
17723
|
-
}
|
|
17724
|
-
hostBindingOpCodes.push(directiveIdx, directiveVarsIdx, hostBindings);
|
|
17725
|
-
}
|
|
17726
|
-
}
|
|
17727
|
-
/**
|
|
17728
|
-
* Returns the last selected element index in the `HostBindingOpCodes`
|
|
17729
|
-
*
|
|
17730
|
-
* For perf reasons we don't need to update the selected element index in `HostBindingOpCodes` only
|
|
17731
|
-
* if it changes. This method returns the last index (or '0' if not found.)
|
|
17732
|
-
*
|
|
17733
|
-
* Selected element index are only the ones which are negative.
|
|
17734
|
-
*/
|
|
17735
|
-
function lastSelectedElementIdx(hostBindingOpCodes) {
|
|
17736
|
-
let i = hostBindingOpCodes.length;
|
|
17737
|
-
while (i > 0) {
|
|
17738
|
-
const value = hostBindingOpCodes[--i];
|
|
17739
|
-
if (typeof value === 'number' && value < 0) {
|
|
17740
|
-
return value;
|
|
17741
|
-
}
|
|
17742
|
-
}
|
|
17743
|
-
return 0;
|
|
17744
|
-
}
|
|
17745
|
-
/**
|
|
17746
|
-
* Builds up an export map as directives are created, so local refs can be quickly mapped
|
|
17747
|
-
* to their directive instances.
|
|
17748
|
-
*/
|
|
17749
|
-
function saveNameToExportMap(directiveIdx, def, exportsMap) {
|
|
17750
|
-
if (exportsMap) {
|
|
17751
|
-
if (def.exportAs) {
|
|
17752
|
-
for (let i = 0; i < def.exportAs.length; i++) {
|
|
17753
|
-
exportsMap[def.exportAs[i]] = directiveIdx;
|
|
17754
|
-
}
|
|
17755
|
-
}
|
|
17756
|
-
if (isComponentDef(def))
|
|
17757
|
-
exportsMap[''] = directiveIdx;
|
|
17758
|
-
}
|
|
17759
|
-
}
|
|
17760
|
-
/**
|
|
17761
|
-
* Initializes the flags on the current node, setting all indices to the initial index,
|
|
17762
|
-
* the directive count to 0, and adding the isComponent flag.
|
|
17763
|
-
* @param index the initial index
|
|
17764
|
-
*/
|
|
17765
|
-
function initTNodeFlags(tNode, index, numberOfDirectives) {
|
|
17766
|
-
ngDevMode &&
|
|
17767
|
-
assertNotEqual(numberOfDirectives, tNode.directiveEnd - tNode.directiveStart, 'Reached the max number of directives');
|
|
17768
|
-
tNode.flags |= 1 /* TNodeFlags.isDirectiveHost */;
|
|
17769
|
-
// When the first directive is created on a node, save the index
|
|
17770
|
-
tNode.directiveStart = index;
|
|
17771
|
-
tNode.directiveEnd = index + numberOfDirectives;
|
|
17772
|
-
tNode.providerIndexes = index;
|
|
17773
|
-
}
|
|
17774
|
-
function assertNoDuplicateDirectives(directives) {
|
|
17775
|
-
// The array needs at least two elements in order to have duplicates.
|
|
17776
|
-
if (directives.length < 2) {
|
|
17777
|
-
return;
|
|
17778
|
-
}
|
|
17779
|
-
const seenDirectives = new Set();
|
|
17780
|
-
for (const current of directives) {
|
|
17781
|
-
if (seenDirectives.has(current)) {
|
|
17782
|
-
throw new RuntimeError(309 /* RuntimeErrorCode.DUPLICATE_DIRECTIVE */, `Directive ${current.type.name} matches multiple times on the same element. ` +
|
|
17783
|
-
`Directives can only match an element once.`);
|
|
17784
|
-
}
|
|
17785
|
-
seenDirectives.add(current);
|
|
17786
|
-
}
|
|
17787
|
-
}
|
|
17788
|
-
|
|
17789
|
-
function elementStartFirstCreatePass(index, tView, lView, name, directiveMatcher, bindingsEnabled, attrsIndex, localRefsIndex) {
|
|
17790
|
-
ngDevMode && assertFirstCreatePass(tView);
|
|
17791
|
-
ngDevMode && ngDevMode.firstCreatePass++;
|
|
17792
|
-
const tViewConsts = tView.consts;
|
|
17793
|
-
const attrs = getConstant(tViewConsts, attrsIndex);
|
|
17794
|
-
const tNode = getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, name, attrs);
|
|
17795
|
-
if (bindingsEnabled) {
|
|
17796
|
-
resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex), directiveMatcher);
|
|
17797
|
-
}
|
|
17798
|
-
// Merge the template attrs last so that they have the highest priority.
|
|
17799
|
-
tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, tNode.attrs);
|
|
17800
|
-
if (tNode.attrs !== null) {
|
|
17801
|
-
computeStaticStyling(tNode, tNode.attrs, false);
|
|
17802
|
-
}
|
|
17803
|
-
if (tNode.mergedAttrs !== null) {
|
|
17804
|
-
computeStaticStyling(tNode, tNode.mergedAttrs, true);
|
|
17805
|
-
}
|
|
17806
|
-
if (tView.queries !== null) {
|
|
17807
|
-
tView.queries.elementStart(tView, tNode);
|
|
17808
|
-
}
|
|
17809
|
-
return tNode;
|
|
17810
|
-
}
|
|
17811
|
-
function elementEndFirstCreatePass(tView, tNode) {
|
|
17812
|
-
ngDevMode && assertFirstCreatePass(tView);
|
|
17813
|
-
registerPostOrderHooks(tView, tNode);
|
|
17814
|
-
if (isContentQueryHost(tNode)) {
|
|
17815
|
-
tView.queries.elementEnd(tNode);
|
|
17816
|
-
}
|
|
17817
|
-
}
|
|
17818
|
-
|
|
17819
17796
|
class ComponentFactoryResolver extends ComponentFactoryResolver$1 {
|
|
17820
17797
|
ngModule;
|
|
17821
17798
|
/**
|
|
@@ -17860,51 +17837,9 @@ function toRefArray(map, isInputMap) {
|
|
|
17860
17837
|
}
|
|
17861
17838
|
return array;
|
|
17862
17839
|
}
|
|
17863
|
-
function
|
|
17864
|
-
|
|
17865
|
-
|
|
17866
|
-
componentDef.debugInfo?.forbidOrphanRendering) {
|
|
17867
|
-
if (depsTracker.isOrphanComponent(componentDef.type)) {
|
|
17868
|
-
throw new RuntimeError(981 /* RuntimeErrorCode.RUNTIME_DEPS_ORPHAN_COMPONENT */, `Orphan component found! Trying to render the component ${debugStringifyTypeForError(componentDef.type)} without first loading the NgModule that declares it. It is recommended to make this component standalone in order to avoid this error. If this is not possible now, import the component's NgModule in the appropriate NgModule, or the standalone component in which you are trying to render this component. If this is a lazy import, load the NgModule lazily as well and use its module injector.`);
|
|
17869
|
-
}
|
|
17870
|
-
}
|
|
17871
|
-
}
|
|
17872
|
-
function createRootViewInjector(componentDef, environmentInjector, injector) {
|
|
17873
|
-
let realEnvironmentInjector = environmentInjector instanceof EnvironmentInjector
|
|
17874
|
-
? environmentInjector
|
|
17875
|
-
: environmentInjector?.injector;
|
|
17876
|
-
if (realEnvironmentInjector && componentDef.getStandaloneInjector !== null) {
|
|
17877
|
-
realEnvironmentInjector =
|
|
17878
|
-
componentDef.getStandaloneInjector(realEnvironmentInjector) || realEnvironmentInjector;
|
|
17879
|
-
}
|
|
17880
|
-
const rootViewInjector = realEnvironmentInjector
|
|
17881
|
-
? new ChainedInjector(injector, realEnvironmentInjector)
|
|
17882
|
-
: injector;
|
|
17883
|
-
return rootViewInjector;
|
|
17884
|
-
}
|
|
17885
|
-
function createRootLViewEnvironment(rootLViewInjector) {
|
|
17886
|
-
const rendererFactory = rootLViewInjector.get(RendererFactory2, null);
|
|
17887
|
-
if (rendererFactory === null) {
|
|
17888
|
-
throw new RuntimeError(407 /* RuntimeErrorCode.RENDERER_NOT_FOUND */, ngDevMode &&
|
|
17889
|
-
'Angular was not able to inject a renderer (RendererFactory2). ' +
|
|
17890
|
-
'Likely this is due to a broken DI hierarchy. ' +
|
|
17891
|
-
'Make sure that any injector used to create this component has a correct parent.');
|
|
17892
|
-
}
|
|
17893
|
-
const sanitizer = rootLViewInjector.get(Sanitizer, null);
|
|
17894
|
-
const changeDetectionScheduler = rootLViewInjector.get(ChangeDetectionScheduler, null);
|
|
17895
|
-
return {
|
|
17896
|
-
rendererFactory,
|
|
17897
|
-
sanitizer,
|
|
17898
|
-
changeDetectionScheduler,
|
|
17899
|
-
};
|
|
17900
|
-
}
|
|
17901
|
-
function createHostElement(componentDef, render) {
|
|
17902
|
-
// Determine a tag name used for creating host elements when this component is created
|
|
17903
|
-
// dynamically. Default to 'div' if this component did not specify any tag name in its
|
|
17904
|
-
// selector.
|
|
17905
|
-
const tagName = (componentDef.selectors[0][0] || 'div').toLowerCase();
|
|
17906
|
-
const namespace = tagName === 'svg' ? SVG_NAMESPACE : tagName === 'math' ? MATH_ML_NAMESPACE : null;
|
|
17907
|
-
return createElementNode(render, tagName, namespace);
|
|
17840
|
+
function getNamespace(elementName) {
|
|
17841
|
+
const name = elementName.toLowerCase();
|
|
17842
|
+
return name === 'svg' ? SVG_NAMESPACE : name === 'math' ? MATH_ML_NAMESPACE : null;
|
|
17908
17843
|
}
|
|
17909
17844
|
/**
|
|
17910
17845
|
* ComponentFactory interface implementation.
|
|
@@ -17946,24 +17881,66 @@ class ComponentFactory extends ComponentFactory$1 {
|
|
|
17946
17881
|
this.isBoundToModule = !!ngModule;
|
|
17947
17882
|
}
|
|
17948
17883
|
create(injector, projectableNodes, rootSelectorOrNode, environmentInjector) {
|
|
17884
|
+
profiler(22 /* ProfilerEvent.DynamicComponentStart */);
|
|
17949
17885
|
const prevConsumer = setActiveConsumer$1(null);
|
|
17950
17886
|
try {
|
|
17951
|
-
|
|
17952
|
-
ngDevMode &&
|
|
17953
|
-
|
|
17954
|
-
|
|
17955
|
-
|
|
17956
|
-
|
|
17887
|
+
// Check if the component is orphan
|
|
17888
|
+
if (ngDevMode &&
|
|
17889
|
+
(typeof ngJitMode === 'undefined' || ngJitMode) &&
|
|
17890
|
+
this.componentDef.debugInfo?.forbidOrphanRendering) {
|
|
17891
|
+
if (depsTracker.isOrphanComponent(this.componentType)) {
|
|
17892
|
+
throw new RuntimeError(981 /* RuntimeErrorCode.RUNTIME_DEPS_ORPHAN_COMPONENT */, `Orphan component found! Trying to render the component ${debugStringifyTypeForError(this.componentType)} without first loading the NgModule that declares it. It is recommended to make this component standalone in order to avoid this error. If this is not possible now, import the component's NgModule in the appropriate NgModule, or the standalone component in which you are trying to render this component. If this is a lazy import, load the NgModule lazily as well and use its module injector.`);
|
|
17893
|
+
}
|
|
17894
|
+
}
|
|
17895
|
+
environmentInjector = environmentInjector || this.ngModule;
|
|
17896
|
+
let realEnvironmentInjector = environmentInjector instanceof EnvironmentInjector
|
|
17897
|
+
? environmentInjector
|
|
17898
|
+
: environmentInjector?.injector;
|
|
17899
|
+
if (realEnvironmentInjector && this.componentDef.getStandaloneInjector !== null) {
|
|
17900
|
+
realEnvironmentInjector =
|
|
17901
|
+
this.componentDef.getStandaloneInjector(realEnvironmentInjector) ||
|
|
17902
|
+
realEnvironmentInjector;
|
|
17903
|
+
}
|
|
17904
|
+
const rootViewInjector = realEnvironmentInjector
|
|
17905
|
+
? new ChainedInjector(injector, realEnvironmentInjector)
|
|
17906
|
+
: injector;
|
|
17907
|
+
const rendererFactory = rootViewInjector.get(RendererFactory2, null);
|
|
17908
|
+
if (rendererFactory === null) {
|
|
17909
|
+
throw new RuntimeError(407 /* RuntimeErrorCode.RENDERER_NOT_FOUND */, ngDevMode &&
|
|
17910
|
+
'Angular was not able to inject a renderer (RendererFactory2). ' +
|
|
17911
|
+
'Likely this is due to a broken DI hierarchy. ' +
|
|
17912
|
+
'Make sure that any injector used to create this component has a correct parent.');
|
|
17913
|
+
}
|
|
17914
|
+
const sanitizer = rootViewInjector.get(Sanitizer, null);
|
|
17915
|
+
const changeDetectionScheduler = rootViewInjector.get(ChangeDetectionScheduler, null);
|
|
17916
|
+
const environment = {
|
|
17917
|
+
rendererFactory,
|
|
17918
|
+
sanitizer,
|
|
17919
|
+
changeDetectionScheduler,
|
|
17920
|
+
};
|
|
17921
|
+
const hostRenderer = rendererFactory.createRenderer(null, this.componentDef);
|
|
17922
|
+
// Determine a tag name used for creating host elements when this component is created
|
|
17923
|
+
// dynamically. Default to 'div' if this component did not specify any tag name in its
|
|
17924
|
+
// selector.
|
|
17925
|
+
const elementName = this.componentDef.selectors[0][0] || 'div';
|
|
17926
|
+
const hostRNode = rootSelectorOrNode
|
|
17927
|
+
? locateHostElement(hostRenderer, rootSelectorOrNode, this.componentDef.encapsulation, rootViewInjector)
|
|
17928
|
+
: createElementNode(hostRenderer, elementName, getNamespace(elementName));
|
|
17929
|
+
let rootFlags = 512 /* LViewFlags.IsRoot */;
|
|
17930
|
+
if (this.componentDef.signals) {
|
|
17931
|
+
rootFlags |= 4096 /* LViewFlags.SignalView */;
|
|
17932
|
+
}
|
|
17933
|
+
else if (!this.componentDef.onPush) {
|
|
17934
|
+
rootFlags |= 16 /* LViewFlags.CheckAlways */;
|
|
17935
|
+
}
|
|
17936
|
+
let hydrationInfo = null;
|
|
17937
|
+
if (hostRNode !== null) {
|
|
17938
|
+
hydrationInfo = retrieveHydrationInfo(hostRNode, rootViewInjector, true /* isRootView */);
|
|
17939
|
+
}
|
|
17957
17940
|
// Create the root view. Uses empty TView and ContentTemplate.
|
|
17958
|
-
const rootTView = createTView(0 /* TViewType.Root */, null, null, 1, 0, null, null, null, null,
|
|
17959
|
-
const
|
|
17960
|
-
|
|
17961
|
-
const hostRenderer = environment.rendererFactory.createRenderer(null, cmpDef);
|
|
17962
|
-
const hostElement = rootSelectorOrNode
|
|
17963
|
-
? locateHostElement(hostRenderer, rootSelectorOrNode, cmpDef.encapsulation, rootViewInjector)
|
|
17964
|
-
: createHostElement(cmpDef, hostRenderer);
|
|
17965
|
-
const rootLView = createLView(null, rootTView, null, 512 /* LViewFlags.IsRoot */ | getInitialLViewFlagsFromDef(cmpDef), null, null, environment, hostRenderer, rootViewInjector, null, retrieveHydrationInfo(hostElement, rootViewInjector, true /* isRootView */));
|
|
17966
|
-
rootLView[HEADER_OFFSET] = hostElement;
|
|
17941
|
+
const rootTView = createTView(0 /* TViewType.Root */, null, null, 1, 0, null, null, null, null, null, null);
|
|
17942
|
+
const rootLView = createLView(null, rootTView, null, rootFlags, null, null, environment, hostRenderer, rootViewInjector, null, hydrationInfo);
|
|
17943
|
+
rootLView[HEADER_OFFSET] = hostRNode;
|
|
17967
17944
|
// rootView is the parent when bootstrapping
|
|
17968
17945
|
// TODO(misko): it looks like we are entering view here but we don't really need to as
|
|
17969
17946
|
// `renderView` does that. However as the code is written it is needed because
|
|
@@ -17972,22 +17949,37 @@ class ComponentFactory extends ComponentFactory$1 {
|
|
|
17972
17949
|
enterView(rootLView);
|
|
17973
17950
|
let componentView = null;
|
|
17974
17951
|
try {
|
|
17975
|
-
|
|
17976
|
-
|
|
17952
|
+
// If host dom element is created (instead of being provided as part of the dynamic component creation), also apply attributes and classes extracted from component selector.
|
|
17953
|
+
const tAttributes = rootSelectorOrNode
|
|
17954
|
+
? ['ng-version', '19.2.0-next.1']
|
|
17955
|
+
: // Extract attributes and classes from the first selector only to match VE behavior.
|
|
17956
|
+
extractAttrsAndClassesFromSelector(this.componentDef.selectors[0]);
|
|
17957
|
+
// TODO: this logic is shared with the element instruction first create pass
|
|
17958
|
+
const hostTNode = getOrCreateTNode(rootTView, HEADER_OFFSET, 2 /* TNodeType.Element */, '#host', tAttributes);
|
|
17959
|
+
const [directiveDefs, hostDirectiveDefs] = resolveHostDirectives(rootTView, hostTNode, [
|
|
17960
|
+
this.componentDef,
|
|
17961
|
+
]);
|
|
17962
|
+
initializeDirectives(rootTView, rootLView, hostTNode, directiveDefs, {}, hostDirectiveDefs);
|
|
17963
|
+
for (const def of directiveDefs) {
|
|
17964
|
+
hostTNode.mergedAttrs = mergeHostAttrs(hostTNode.mergedAttrs, def.hostAttrs);
|
|
17965
|
+
}
|
|
17966
|
+
hostTNode.mergedAttrs = mergeHostAttrs(hostTNode.mergedAttrs, tAttributes);
|
|
17967
|
+
computeStaticStyling(hostTNode, hostTNode.mergedAttrs, true);
|
|
17977
17968
|
// TODO(crisbeto): in practice `hostRNode` should always be defined, but there are some
|
|
17978
17969
|
// tests where the renderer is mocked out and `undefined` is returned. We should update the
|
|
17979
17970
|
// tests so that this check can be removed.
|
|
17980
|
-
if (
|
|
17981
|
-
setupStaticAttributes(hostRenderer,
|
|
17982
|
-
attachPatchData(
|
|
17971
|
+
if (hostRNode) {
|
|
17972
|
+
setupStaticAttributes(hostRenderer, hostRNode, hostTNode);
|
|
17973
|
+
attachPatchData(hostRNode, rootLView);
|
|
17983
17974
|
}
|
|
17984
|
-
// TODO(pk): this logic is similar to the instruction code where a node can have directives
|
|
17985
|
-
createDirectivesInstances(rootTView, rootLView, hostTNode);
|
|
17986
|
-
executeContentQueries(rootTView, hostTNode, rootLView);
|
|
17987
|
-
elementEndFirstCreatePass(rootTView, hostTNode);
|
|
17988
17975
|
if (projectableNodes !== undefined) {
|
|
17989
17976
|
projectNodes(hostTNode, this.ngContentSelectors, projectableNodes);
|
|
17990
17977
|
}
|
|
17978
|
+
// TODO(pk): this logic is similar to the instruction code where a node can have directives
|
|
17979
|
+
createDirectivesInstances(rootTView, rootLView, hostTNode);
|
|
17980
|
+
executeContentQueries(rootTView, hostTNode, rootLView);
|
|
17981
|
+
// TODO(pk): code / logic duplication with the elementEnd and similar instructions
|
|
17982
|
+
registerPostOrderHooks(rootTView, hostTNode);
|
|
17991
17983
|
componentView = getComponentLViewByIndex(hostTNode.index, rootLView);
|
|
17992
17984
|
// TODO(pk): why do we need this logic?
|
|
17993
17985
|
rootLView[CONTEXT] = componentView[CONTEXT];
|
|
@@ -18003,6 +17995,7 @@ class ComponentFactory extends ComponentFactory$1 {
|
|
|
18003
17995
|
throw e;
|
|
18004
17996
|
}
|
|
18005
17997
|
finally {
|
|
17998
|
+
profiler(23 /* ProfilerEvent.DynamicComponentEnd */);
|
|
18006
17999
|
leaveView();
|
|
18007
18000
|
}
|
|
18008
18001
|
return new ComponentRef(this.componentType, rootLView);
|
|
@@ -20602,11 +20595,7 @@ function templateFirstCreatePass(index, tView, lView, templateFn, decls, vars, t
|
|
|
20602
20595
|
const tViewConsts = tView.consts;
|
|
20603
20596
|
// TODO(pk): refactor getOrCreateTNode to have the "create" only version
|
|
20604
20597
|
const tNode = getOrCreateTNode(tView, index, 4 /* TNodeType.Container */, tagName || null, attrs || null);
|
|
20605
|
-
|
|
20606
|
-
resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex), findDirectiveDefMatches);
|
|
20607
|
-
}
|
|
20608
|
-
// Merge the template attrs last so that they have the highest priority.
|
|
20609
|
-
tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, tNode.attrs);
|
|
20598
|
+
resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex));
|
|
20610
20599
|
registerPostOrderHooks(tView, tNode);
|
|
20611
20600
|
const embeddedTView = (tNode.tView = createTView(2 /* TViewType.Embedded */, tNode, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, tView.schemas, tViewConsts, null /* ssrId */));
|
|
20612
20601
|
if (tView.queries !== null) {
|
|
@@ -21191,6 +21180,7 @@ function findMatchingDehydratedViewForDeferBlock(lContainer, lDetails) {
|
|
|
21191
21180
|
* Applies changes to the DOM to reflect a given state.
|
|
21192
21181
|
*/
|
|
21193
21182
|
function applyDeferBlockState(newState, lDetails, lContainer, tNode, hostLView) {
|
|
21183
|
+
profiler(20 /* ProfilerEvent.DeferBlockStateStart */);
|
|
21194
21184
|
const stateTmplIndex = getTemplateIndexForState(newState, hostLView, tNode);
|
|
21195
21185
|
if (stateTmplIndex !== null) {
|
|
21196
21186
|
lDetails[DEFER_BLOCK_STATE] = newState;
|
|
@@ -21239,6 +21229,7 @@ function applyDeferBlockState(newState, lDetails, lContainer, tNode, hostLView)
|
|
|
21239
21229
|
lDetails[ON_COMPLETE_FNS] = null;
|
|
21240
21230
|
}
|
|
21241
21231
|
}
|
|
21232
|
+
profiler(21 /* ProfilerEvent.DeferBlockStateEnd */);
|
|
21242
21233
|
}
|
|
21243
21234
|
/**
|
|
21244
21235
|
* Extends the `applyDeferBlockState` with timer-based scheduling.
|
|
@@ -23278,6 +23269,24 @@ class NgProbeToken {
|
|
|
23278
23269
|
}
|
|
23279
23270
|
/** Maximum number of times ApplicationRef will refresh all attached views in a single tick. */
|
|
23280
23271
|
const MAXIMUM_REFRESH_RERUNS = 10;
|
|
23272
|
+
function _callAndReportToErrorHandler(errorHandler, ngZone, callback) {
|
|
23273
|
+
try {
|
|
23274
|
+
const result = callback();
|
|
23275
|
+
if (isPromise(result)) {
|
|
23276
|
+
return result.catch((e) => {
|
|
23277
|
+
ngZone.runOutsideAngular(() => errorHandler.handleError(e));
|
|
23278
|
+
// rethrow as the exception handler might not do it
|
|
23279
|
+
throw e;
|
|
23280
|
+
});
|
|
23281
|
+
}
|
|
23282
|
+
return result;
|
|
23283
|
+
}
|
|
23284
|
+
catch (e) {
|
|
23285
|
+
ngZone.runOutsideAngular(() => errorHandler.handleError(e));
|
|
23286
|
+
// rethrow as the exception handler might not do it
|
|
23287
|
+
throw e;
|
|
23288
|
+
}
|
|
23289
|
+
}
|
|
23281
23290
|
function optionsReducer(dst, objs) {
|
|
23282
23291
|
if (Array.isArray(objs)) {
|
|
23283
23292
|
return objs.reduce(optionsReducer, dst);
|
|
@@ -23506,6 +23515,7 @@ class ApplicationRef {
|
|
|
23506
23515
|
* {@example core/ts/platform/platform.ts region='domNode'}
|
|
23507
23516
|
*/
|
|
23508
23517
|
bootstrap(componentOrFactory, rootSelectorOrNode) {
|
|
23518
|
+
profiler(10 /* ProfilerEvent.BootstrapComponentStart */);
|
|
23509
23519
|
(typeof ngDevMode === 'undefined' || ngDevMode) && warnIfDestroyed(this._destroyed);
|
|
23510
23520
|
const isComponentFactory = componentOrFactory instanceof ComponentFactory$1;
|
|
23511
23521
|
const initStatus = this._injector.get(ApplicationInitStatus);
|
|
@@ -23546,6 +23556,7 @@ class ApplicationRef {
|
|
|
23546
23556
|
const _console = this._injector.get(Console);
|
|
23547
23557
|
_console.log(`Angular is running in development mode.`);
|
|
23548
23558
|
}
|
|
23559
|
+
profiler(11 /* ProfilerEvent.BootstrapComponentEnd */, compRef);
|
|
23549
23560
|
return compRef;
|
|
23550
23561
|
}
|
|
23551
23562
|
/**
|
|
@@ -23566,6 +23577,7 @@ class ApplicationRef {
|
|
|
23566
23577
|
}
|
|
23567
23578
|
/** @internal */
|
|
23568
23579
|
_tick = () => {
|
|
23580
|
+
profiler(12 /* ProfilerEvent.ChangeDetectionStart */);
|
|
23569
23581
|
if (this.tracingSnapshot !== null) {
|
|
23570
23582
|
const snapshot = this.tracingSnapshot;
|
|
23571
23583
|
this.tracingSnapshot = null;
|
|
@@ -23598,6 +23610,7 @@ class ApplicationRef {
|
|
|
23598
23610
|
this._runningTick = false;
|
|
23599
23611
|
setActiveConsumer$1(prevConsumer);
|
|
23600
23612
|
this.afterTick.next();
|
|
23613
|
+
profiler(13 /* ProfilerEvent.ChangeDetectionEnd */);
|
|
23601
23614
|
}
|
|
23602
23615
|
};
|
|
23603
23616
|
/**
|
|
@@ -23613,7 +23626,9 @@ class ApplicationRef {
|
|
|
23613
23626
|
this.deferredDirtyFlags = 0 /* ApplicationRefDirtyFlags.None */;
|
|
23614
23627
|
let runs = 0;
|
|
23615
23628
|
while (this.dirtyFlags !== 0 /* ApplicationRefDirtyFlags.None */ && runs++ < MAXIMUM_REFRESH_RERUNS) {
|
|
23629
|
+
profiler(14 /* ProfilerEvent.ChangeDetectionSyncStart */);
|
|
23616
23630
|
this.synchronizeOnce();
|
|
23631
|
+
profiler(15 /* ProfilerEvent.ChangeDetectionSyncEnd */);
|
|
23617
23632
|
}
|
|
23618
23633
|
if ((typeof ngDevMode === 'undefined' || ngDevMode) && runs >= MAXIMUM_REFRESH_RERUNS) {
|
|
23619
23634
|
throw new RuntimeError(103 /* RuntimeErrorCode.INFINITE_CHANGE_DETECTION */, ngDevMode &&
|
|
@@ -27964,6 +27979,24 @@ function getExistingTNode(tView, index) {
|
|
|
27964
27979
|
return tNode;
|
|
27965
27980
|
}
|
|
27966
27981
|
|
|
27982
|
+
function elementStartFirstCreatePass(index, tView, lView, name, attrsIndex, localRefsIndex) {
|
|
27983
|
+
ngDevMode && assertFirstCreatePass(tView);
|
|
27984
|
+
ngDevMode && ngDevMode.firstCreatePass++;
|
|
27985
|
+
const tViewConsts = tView.consts;
|
|
27986
|
+
const attrs = getConstant(tViewConsts, attrsIndex);
|
|
27987
|
+
const tNode = getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, name, attrs);
|
|
27988
|
+
resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex));
|
|
27989
|
+
if (tNode.attrs !== null) {
|
|
27990
|
+
computeStaticStyling(tNode, tNode.attrs, false);
|
|
27991
|
+
}
|
|
27992
|
+
if (tNode.mergedAttrs !== null) {
|
|
27993
|
+
computeStaticStyling(tNode, tNode.mergedAttrs, true);
|
|
27994
|
+
}
|
|
27995
|
+
if (tView.queries !== null) {
|
|
27996
|
+
tView.queries.elementStart(tView, tNode);
|
|
27997
|
+
}
|
|
27998
|
+
return tNode;
|
|
27999
|
+
}
|
|
27967
28000
|
/**
|
|
27968
28001
|
* Create DOM element. The instruction must later be followed by `elementEnd()` call.
|
|
27969
28002
|
*
|
|
@@ -27988,7 +28021,7 @@ function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
|
|
|
27988
28021
|
ngDevMode && assertIndexInRange(lView, adjustedIndex);
|
|
27989
28022
|
const renderer = lView[RENDERER];
|
|
27990
28023
|
const tNode = tView.firstCreatePass
|
|
27991
|
-
? elementStartFirstCreatePass(adjustedIndex, tView, lView, name,
|
|
28024
|
+
? elementStartFirstCreatePass(adjustedIndex, tView, lView, name, attrsIndex, localRefsIndex)
|
|
27992
28025
|
: tView.data[adjustedIndex];
|
|
27993
28026
|
const native = _locateOrCreateElementNode(tView, lView, tNode, renderer, name, index);
|
|
27994
28027
|
lView[adjustedIndex] = native;
|
|
@@ -28044,7 +28077,10 @@ function ɵɵelementEnd() {
|
|
|
28044
28077
|
decreaseElementDepthCount();
|
|
28045
28078
|
const tView = getTView();
|
|
28046
28079
|
if (tView.firstCreatePass) {
|
|
28047
|
-
|
|
28080
|
+
registerPostOrderHooks(tView, currentTNode);
|
|
28081
|
+
if (isContentQueryHost(currentTNode)) {
|
|
28082
|
+
tView.queries.elementEnd(currentTNode);
|
|
28083
|
+
}
|
|
28048
28084
|
}
|
|
28049
28085
|
if (tNode.classesWithoutHost != null && hasClassInput(tNode)) {
|
|
28050
28086
|
setDirectiveInputsWhichShadowsStyling(tView, tNode, getLView(), tNode.classesWithoutHost, true);
|
|
@@ -28072,7 +28108,7 @@ function ɵɵelement(index, name, attrsIndex, localRefsIndex) {
|
|
|
28072
28108
|
}
|
|
28073
28109
|
let _locateOrCreateElementNode = (tView, lView, tNode, renderer, name, index) => {
|
|
28074
28110
|
lastNodeWasCreated(true);
|
|
28075
|
-
return createElementNode(renderer, name, getNamespace());
|
|
28111
|
+
return createElementNode(renderer, name, getNamespace$1());
|
|
28076
28112
|
};
|
|
28077
28113
|
/**
|
|
28078
28114
|
* Enables hydration code path (to lookup existing elements in DOM)
|
|
@@ -28087,7 +28123,7 @@ function locateOrCreateElementNodeImpl(tView, lView, tNode, renderer, name, inde
|
|
|
28087
28123
|
lastNodeWasCreated(isNodeCreationMode);
|
|
28088
28124
|
// Regular creation mode.
|
|
28089
28125
|
if (isNodeCreationMode) {
|
|
28090
|
-
return createElementNode(renderer, name, getNamespace());
|
|
28126
|
+
return createElementNode(renderer, name, getNamespace$1());
|
|
28091
28127
|
}
|
|
28092
28128
|
// Hydration mode, looking up an existing element in DOM.
|
|
28093
28129
|
const native = locateNextRNode(hydrationInfo, tView, lView, tNode);
|
|
@@ -28140,11 +28176,7 @@ function elementContainerStartFirstCreatePass(index, tView, lView, attrsIndex, l
|
|
|
28140
28176
|
computeStaticStyling(tNode, attrs, true);
|
|
28141
28177
|
}
|
|
28142
28178
|
const localRefs = getConstant(tViewConsts, localRefsIndex);
|
|
28143
|
-
|
|
28144
|
-
resolveDirectives(tView, lView, tNode, localRefs, findDirectiveDefMatches);
|
|
28145
|
-
}
|
|
28146
|
-
// Merge the template attrs last so that they have the highest priority.
|
|
28147
|
-
tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, tNode.attrs);
|
|
28179
|
+
resolveDirectives(tView, lView, tNode, localRefs);
|
|
28148
28180
|
if (tView.queries !== null) {
|
|
28149
28181
|
tView.queries.elementStart(tView, tNode);
|
|
28150
28182
|
}
|
|
@@ -34897,7 +34929,7 @@ class Version {
|
|
|
34897
34929
|
/**
|
|
34898
34930
|
* @publicApi
|
|
34899
34931
|
*/
|
|
34900
|
-
const VERSION = new Version('19.1
|
|
34932
|
+
const VERSION = new Version('19.2.0-next.1');
|
|
34901
34933
|
|
|
34902
34934
|
/**
|
|
34903
34935
|
* Combination of NgModuleFactory and ComponentFactories.
|
|
@@ -36046,24 +36078,6 @@ function moduleDoBootstrap(moduleRef, allPlatformModules) {
|
|
|
36046
36078
|
}
|
|
36047
36079
|
allPlatformModules.push(moduleRef);
|
|
36048
36080
|
}
|
|
36049
|
-
function _callAndReportToErrorHandler(errorHandler, ngZone, callback) {
|
|
36050
|
-
try {
|
|
36051
|
-
const result = callback();
|
|
36052
|
-
if (isPromise(result)) {
|
|
36053
|
-
return result.catch((e) => {
|
|
36054
|
-
ngZone.runOutsideAngular(() => errorHandler.handleError(e));
|
|
36055
|
-
// rethrow as the exception handler might not do it
|
|
36056
|
-
throw e;
|
|
36057
|
-
});
|
|
36058
|
-
}
|
|
36059
|
-
return result;
|
|
36060
|
-
}
|
|
36061
|
-
catch (e) {
|
|
36062
|
-
ngZone.runOutsideAngular(() => errorHandler.handleError(e));
|
|
36063
|
-
// rethrow as the exception handler might not do it
|
|
36064
|
-
throw e;
|
|
36065
|
-
}
|
|
36066
|
-
}
|
|
36067
36081
|
|
|
36068
36082
|
/**
|
|
36069
36083
|
* The Angular platform is the entry point for Angular on a web page.
|
|
@@ -38998,6 +39012,7 @@ function setAlternateWeakRefImpl(impl) {
|
|
|
38998
39012
|
* @returns A promise that returns an `ApplicationRef` instance once resolved.
|
|
38999
39013
|
*/
|
|
39000
39014
|
function internalCreateApplication(config) {
|
|
39015
|
+
profiler(8 /* ProfilerEvent.BootstrapApplicationStart */);
|
|
39001
39016
|
try {
|
|
39002
39017
|
const { rootComponent, appProviders, platformProviders } = config;
|
|
39003
39018
|
if ((typeof ngDevMode === 'undefined' || ngDevMode) && rootComponent !== undefined) {
|
|
@@ -39028,6 +39043,9 @@ function internalCreateApplication(config) {
|
|
|
39028
39043
|
catch (e) {
|
|
39029
39044
|
return Promise.reject(e);
|
|
39030
39045
|
}
|
|
39046
|
+
finally {
|
|
39047
|
+
profiler(9 /* ProfilerEvent.BootstrapApplicationEnd */);
|
|
39048
|
+
}
|
|
39031
39049
|
}
|
|
39032
39050
|
|
|
39033
39051
|
/** Apps in which we've enabled event replay.
|
|
@@ -41084,20 +41102,10 @@ var ResourceStatus;
|
|
|
41084
41102
|
ResourceStatus[ResourceStatus["Local"] = 5] = "Local";
|
|
41085
41103
|
})(ResourceStatus || (ResourceStatus = {}));
|
|
41086
41104
|
|
|
41087
|
-
/**
|
|
41088
|
-
* Constructs a `Resource` that projects a reactive request to an asynchronous operation defined by
|
|
41089
|
-
* a loader function, which exposes the result of the loading operation via signals.
|
|
41090
|
-
*
|
|
41091
|
-
* Note that `resource` is intended for _read_ operations, not operations which perform mutations.
|
|
41092
|
-
* `resource` will cancel in-progress loads via the `AbortSignal` when destroyed or when a new
|
|
41093
|
-
* request object becomes available, which could prematurely abort mutations.
|
|
41094
|
-
*
|
|
41095
|
-
* @experimental
|
|
41096
|
-
*/
|
|
41097
41105
|
function resource(options) {
|
|
41098
41106
|
options?.injector || assertInInjectionContext(resource);
|
|
41099
41107
|
const request = (options.request ?? (() => null));
|
|
41100
|
-
return new ResourceImpl(request, options
|
|
41108
|
+
return new ResourceImpl(request, getLoader(options), options.defaultValue, options.equal ? wrapEqualityFn(options.equal) : undefined, options.injector ?? inject(Injector));
|
|
41101
41109
|
}
|
|
41102
41110
|
/**
|
|
41103
41111
|
* Base class which implements `.value` as a `WritableSignal` by delegating `.set` and `.update`.
|
|
@@ -41143,9 +41151,13 @@ class ResourceImpl extends BaseWritableResource {
|
|
|
41143
41151
|
resolvePendingTask = undefined;
|
|
41144
41152
|
destroyed = false;
|
|
41145
41153
|
constructor(request, loaderFn, defaultValue, equal, injector) {
|
|
41154
|
+
super(
|
|
41146
41155
|
// Feed a computed signal for the value to `BaseWritableResource`, which will upgrade it to a
|
|
41147
41156
|
// `WritableSignal` that delegates to `ResourceImpl.set`.
|
|
41148
|
-
|
|
41157
|
+
computed(() => {
|
|
41158
|
+
const stream = this.state()?.stream?.();
|
|
41159
|
+
return stream && isResolved(stream) ? stream.value : this.defaultValue;
|
|
41160
|
+
}, { equal }));
|
|
41149
41161
|
this.loaderFn = loaderFn;
|
|
41150
41162
|
this.defaultValue = defaultValue;
|
|
41151
41163
|
this.equal = equal;
|
|
@@ -41174,13 +41186,10 @@ class ResourceImpl extends BaseWritableResource {
|
|
|
41174
41186
|
status,
|
|
41175
41187
|
// When the state of the resource changes due to the request, remember the previous status
|
|
41176
41188
|
// for the loader to consider.
|
|
41177
|
-
previousStatus: previous?.value
|
|
41189
|
+
previousStatus: computeStatusOfState(previous?.value),
|
|
41178
41190
|
// In `Reloading` state, we keep the previous value if there is one, since the identity of
|
|
41179
41191
|
// the request hasn't changed. Otherwise, we switch back to the default value.
|
|
41180
|
-
|
|
41181
|
-
? previous.value.value
|
|
41182
|
-
: this.defaultValue,
|
|
41183
|
-
error: undefined,
|
|
41192
|
+
stream: previous && status === ResourceStatus.Reloading ? previous.value.stream : undefined,
|
|
41184
41193
|
}),
|
|
41185
41194
|
});
|
|
41186
41195
|
this.effectRef = effect(this.loadEffect.bind(this), {
|
|
@@ -41190,8 +41199,16 @@ class ResourceImpl extends BaseWritableResource {
|
|
|
41190
41199
|
// Cancel any pending request when the resource itself is destroyed.
|
|
41191
41200
|
injector.get(DestroyRef).onDestroy(() => this.destroy());
|
|
41192
41201
|
}
|
|
41193
|
-
status = computed(() =>
|
|
41194
|
-
|
|
41202
|
+
status = computed(() => {
|
|
41203
|
+
if (this.state().status !== ResourceStatus.Resolved) {
|
|
41204
|
+
return this.state().status;
|
|
41205
|
+
}
|
|
41206
|
+
return isResolved(this.state().stream()) ? ResourceStatus.Resolved : ResourceStatus.Error;
|
|
41207
|
+
});
|
|
41208
|
+
error = computed(() => {
|
|
41209
|
+
const stream = this.state().stream?.();
|
|
41210
|
+
return stream && !isResolved(stream) ? stream.error : undefined;
|
|
41211
|
+
});
|
|
41195
41212
|
/**
|
|
41196
41213
|
* Called either directly via `WritableResource.set` or via `.value.set()`.
|
|
41197
41214
|
*/
|
|
@@ -41199,16 +41216,16 @@ class ResourceImpl extends BaseWritableResource {
|
|
|
41199
41216
|
if (this.destroyed) {
|
|
41200
41217
|
return;
|
|
41201
41218
|
}
|
|
41202
|
-
const
|
|
41203
|
-
if (this.
|
|
41219
|
+
const current = untracked(this.value);
|
|
41220
|
+
if (untracked(this.status) === ResourceStatus.Local &&
|
|
41221
|
+
(this.equal ? this.equal(current, value) : current === value)) {
|
|
41204
41222
|
return;
|
|
41205
41223
|
}
|
|
41206
41224
|
// Enter Local state with the user-defined value.
|
|
41207
41225
|
this.state.set({
|
|
41208
41226
|
status: ResourceStatus.Local,
|
|
41209
41227
|
previousStatus: ResourceStatus.Local,
|
|
41210
|
-
value,
|
|
41211
|
-
error: undefined,
|
|
41228
|
+
stream: signal({ value }),
|
|
41212
41229
|
});
|
|
41213
41230
|
// We're departing from whatever state the resource was in previously, so cancel any in-progress
|
|
41214
41231
|
// loading operations.
|
|
@@ -41234,14 +41251,13 @@ class ResourceImpl extends BaseWritableResource {
|
|
|
41234
41251
|
this.state.set({
|
|
41235
41252
|
status: ResourceStatus.Idle,
|
|
41236
41253
|
previousStatus: ResourceStatus.Idle,
|
|
41237
|
-
|
|
41238
|
-
error: undefined,
|
|
41254
|
+
stream: undefined,
|
|
41239
41255
|
});
|
|
41240
41256
|
}
|
|
41241
41257
|
async loadEffect() {
|
|
41242
41258
|
// Capture the previous status before any state transitions. Note that this is `untracked` since
|
|
41243
41259
|
// we do not want the effect to depend on the state of the resource, only on the request.
|
|
41244
|
-
const { status: previousStatus } = untracked(this.state);
|
|
41260
|
+
const { status: currentStatus, previousStatus } = untracked(this.state);
|
|
41245
41261
|
const { request, reload: reloadCounter } = this.extendedRequest();
|
|
41246
41262
|
// Subscribe side-effectfully to `reloadCounter`, although we don't actually care about its
|
|
41247
41263
|
// value. This is used to rerun the effect when `reload()` is triggered.
|
|
@@ -41250,8 +41266,8 @@ class ResourceImpl extends BaseWritableResource {
|
|
|
41250
41266
|
// Nothing to load (and we should already be in a non-loading state).
|
|
41251
41267
|
return;
|
|
41252
41268
|
}
|
|
41253
|
-
else if (
|
|
41254
|
-
|
|
41269
|
+
else if (currentStatus !== ResourceStatus.Loading &&
|
|
41270
|
+
currentStatus !== ResourceStatus.Reloading) {
|
|
41255
41271
|
// We might've transitioned into a loading state, but has since been overwritten (likely via
|
|
41256
41272
|
// `.set`).
|
|
41257
41273
|
// In this case, the resource has nothing to do.
|
|
@@ -41268,49 +41284,45 @@ class ResourceImpl extends BaseWritableResource {
|
|
|
41268
41284
|
// After the loading operation is cancelled, `this.resolvePendingTask` no longer represents this
|
|
41269
41285
|
// particular task, but this `await` may eventually resolve/reject. Thus, when we cancel in
|
|
41270
41286
|
// response to (1) below, we need to cancel the locally saved task.
|
|
41271
|
-
|
|
41287
|
+
let resolvePendingTask = (this.resolvePendingTask =
|
|
41288
|
+
this.pendingTasks.add());
|
|
41272
41289
|
const { signal: abortSignal } = (this.pendingController = new AbortController());
|
|
41273
41290
|
try {
|
|
41274
41291
|
// The actual loading is run through `untracked` - only the request side of `resource` is
|
|
41275
41292
|
// reactive. This avoids any confusion with signals tracking or not tracking depending on
|
|
41276
41293
|
// which side of the `await` they are.
|
|
41277
|
-
const
|
|
41278
|
-
|
|
41279
|
-
|
|
41280
|
-
|
|
41281
|
-
|
|
41282
|
-
|
|
41283
|
-
|
|
41294
|
+
const stream = await untracked(() => {
|
|
41295
|
+
return this.loaderFn({
|
|
41296
|
+
request: request,
|
|
41297
|
+
abortSignal,
|
|
41298
|
+
previous: {
|
|
41299
|
+
status: previousStatus,
|
|
41300
|
+
},
|
|
41301
|
+
});
|
|
41302
|
+
});
|
|
41284
41303
|
if (abortSignal.aborted) {
|
|
41285
|
-
// This load operation was cancelled.
|
|
41286
41304
|
return;
|
|
41287
41305
|
}
|
|
41288
|
-
// Success :)
|
|
41289
41306
|
this.state.set({
|
|
41290
41307
|
status: ResourceStatus.Resolved,
|
|
41291
41308
|
previousStatus: ResourceStatus.Resolved,
|
|
41292
|
-
|
|
41293
|
-
error: undefined,
|
|
41309
|
+
stream,
|
|
41294
41310
|
});
|
|
41295
41311
|
}
|
|
41296
41312
|
catch (err) {
|
|
41297
41313
|
if (abortSignal.aborted) {
|
|
41298
|
-
// This load operation was cancelled.
|
|
41299
41314
|
return;
|
|
41300
41315
|
}
|
|
41301
|
-
// Fail :(
|
|
41302
41316
|
this.state.set({
|
|
41303
|
-
status: ResourceStatus.
|
|
41317
|
+
status: ResourceStatus.Resolved,
|
|
41304
41318
|
previousStatus: ResourceStatus.Error,
|
|
41305
|
-
|
|
41306
|
-
error: err,
|
|
41319
|
+
stream: signal({ error: err }),
|
|
41307
41320
|
});
|
|
41308
41321
|
}
|
|
41309
41322
|
finally {
|
|
41310
|
-
// Resolve the pending task now that
|
|
41311
|
-
resolvePendingTask();
|
|
41312
|
-
|
|
41313
|
-
this.pendingController = undefined;
|
|
41323
|
+
// Resolve the pending task now that the resource has a value.
|
|
41324
|
+
resolvePendingTask?.();
|
|
41325
|
+
resolvePendingTask = undefined;
|
|
41314
41326
|
}
|
|
41315
41327
|
}
|
|
41316
41328
|
abortInProgressLoad() {
|
|
@@ -41327,6 +41339,35 @@ class ResourceImpl extends BaseWritableResource {
|
|
|
41327
41339
|
function wrapEqualityFn(equal) {
|
|
41328
41340
|
return (a, b) => (a === undefined || b === undefined ? a === b : equal(a, b));
|
|
41329
41341
|
}
|
|
41342
|
+
function getLoader(options) {
|
|
41343
|
+
if (isStreamingResourceOptions(options)) {
|
|
41344
|
+
return options.stream;
|
|
41345
|
+
}
|
|
41346
|
+
return async (params) => {
|
|
41347
|
+
try {
|
|
41348
|
+
return signal({ value: await options.loader(params) });
|
|
41349
|
+
}
|
|
41350
|
+
catch (err) {
|
|
41351
|
+
return signal({ error: err });
|
|
41352
|
+
}
|
|
41353
|
+
};
|
|
41354
|
+
}
|
|
41355
|
+
function isStreamingResourceOptions(options) {
|
|
41356
|
+
return !!options.stream;
|
|
41357
|
+
}
|
|
41358
|
+
function computeStatusOfState(state) {
|
|
41359
|
+
switch (state?.status) {
|
|
41360
|
+
case undefined:
|
|
41361
|
+
return ResourceStatus.Idle;
|
|
41362
|
+
case ResourceStatus.Resolved:
|
|
41363
|
+
return isResolved(untracked(state.stream)) ? ResourceStatus.Resolved : ResourceStatus.Error;
|
|
41364
|
+
default:
|
|
41365
|
+
return state.status;
|
|
41366
|
+
}
|
|
41367
|
+
}
|
|
41368
|
+
function isResolved(state) {
|
|
41369
|
+
return state.error === undefined;
|
|
41370
|
+
}
|
|
41330
41371
|
|
|
41331
41372
|
/**
|
|
41332
41373
|
* Creates a `ComponentRef` instance based on provided component type and a set of options.
|