@angular/core 19.2.0-next.1 → 19.2.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/core.mjs +865 -805
- 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 +2 -1
- package/fesm2022/rxjs-interop.mjs.map +1 -1
- package/fesm2022/testing.mjs +5 -5
- package/index.d.ts +41 -12
- 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 +1 -1
- package/schematics/bundles/{apply_import_manager-5ea49df9.js → apply_import_manager-f4d044b2.js} +3 -3
- package/schematics/bundles/{checker-78667e44.js → checker-32db85a6.js} +90 -52
- package/schematics/bundles/cleanup-unused-imports.js +6 -6
- package/schematics/bundles/{compiler_host-b22de7db.js → compiler_host-540e221c.js} +2 -2
- package/schematics/bundles/control-flow-migration.js +10 -3
- package/schematics/bundles/explicit-standalone-flag.js +5 -5
- package/schematics/bundles/{imports-31a38653.js → imports-abe29092.js} +1 -1
- package/schematics/bundles/{index-de135c2f.js → index-7ee8967e.js} +4 -4
- package/schematics/bundles/{index-3e744c38.js → index-d5020c9c.js} +4 -4
- package/schematics/bundles/inject-migration.js +6 -6
- package/schematics/bundles/{leading_space-6e7a8ec6.js → leading_space-d190b83b.js} +1 -1
- package/schematics/bundles/{migrate_ts_type_references-60e2a469.js → migrate_ts_type_references-26986908.js} +6 -6
- package/schematics/bundles/{nodes-88c2157f.js → nodes-a9f0b985.js} +2 -2
- package/schematics/bundles/output-migration.js +6 -6
- package/schematics/bundles/pending-tasks.js +5 -5
- package/schematics/bundles/{program-b0d98952.js → program-507de2f1.js} +124 -36
- package/schematics/bundles/{project_tsconfig_paths-6c9cde78.js → project_tsconfig_paths-e9ccccbf.js} +1 -1
- package/schematics/bundles/provide-initializer.js +5 -5
- package/schematics/bundles/route-lazy-loading.js +4 -4
- package/schematics/bundles/signal-input-migration.js +8 -8
- package/schematics/bundles/signal-queries-migration.js +8 -8
- package/schematics/bundles/signals.js +8 -8
- package/schematics/bundles/standalone-migration.js +8 -8
- package/testing/index.d.ts +1 -1
package/fesm2022/core.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v19.2.0-next.
|
|
2
|
+
* @license Angular v19.2.0-next.3
|
|
3
3
|
* (c) 2010-2024 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -377,13 +377,11 @@ function stringify(token) {
|
|
|
377
377
|
* @returns concatenated string.
|
|
378
378
|
*/
|
|
379
379
|
function concatStringsWithSpace(before, after) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
? before
|
|
386
|
-
: before + ' ' + after;
|
|
380
|
+
if (!before)
|
|
381
|
+
return after || '';
|
|
382
|
+
if (!after)
|
|
383
|
+
return before;
|
|
384
|
+
return `${before} ${after}`;
|
|
387
385
|
}
|
|
388
386
|
/**
|
|
389
387
|
* Ellipses the string in the middle when longer than the max length
|
|
@@ -2902,6 +2900,7 @@ const ON_DESTROY_HOOKS = 21;
|
|
|
2902
2900
|
const EFFECTS_TO_SCHEDULE = 22;
|
|
2903
2901
|
const EFFECTS = 23;
|
|
2904
2902
|
const REACTIVE_TEMPLATE_CONSUMER = 24;
|
|
2903
|
+
const AFTER_RENDER_SEQUENCES_TO_ADD = 25;
|
|
2905
2904
|
/**
|
|
2906
2905
|
* Size of LView's header. Necessary to adjust for it when setting slots.
|
|
2907
2906
|
*
|
|
@@ -2909,7 +2908,7 @@ const REACTIVE_TEMPLATE_CONSUMER = 24;
|
|
|
2909
2908
|
* instruction index into `LView` index. All other indexes should be in the `LView` index space and
|
|
2910
2909
|
* there should be no need to refer to `HEADER_OFFSET` anywhere else.
|
|
2911
2910
|
*/
|
|
2912
|
-
const HEADER_OFFSET =
|
|
2911
|
+
const HEADER_OFFSET = 26;
|
|
2913
2912
|
|
|
2914
2913
|
/**
|
|
2915
2914
|
* Special location which allows easy identification of type. If we have an array which was
|
|
@@ -3067,7 +3066,7 @@ function assertProjectionSlots(lView, errMessage) {
|
|
|
3067
3066
|
function assertParentView(lView, errMessage) {
|
|
3068
3067
|
assertDefined(lView, errMessage || "Component views should always have a parent view (component's host view)");
|
|
3069
3068
|
}
|
|
3070
|
-
function assertNoDuplicateDirectives(directives) {
|
|
3069
|
+
function assertNoDuplicateDirectives$1(directives) {
|
|
3071
3070
|
// The array needs at least two elements in order to have duplicates.
|
|
3072
3071
|
if (directives.length < 2) {
|
|
3073
3072
|
return;
|
|
@@ -3419,7 +3418,7 @@ function requiresRefreshOrTraversal(lView) {
|
|
|
3419
3418
|
* parents above.
|
|
3420
3419
|
*/
|
|
3421
3420
|
function updateAncestorTraversalFlagsOnAttach(lView) {
|
|
3422
|
-
lView[ENVIRONMENT].changeDetectionScheduler?.notify(
|
|
3421
|
+
lView[ENVIRONMENT].changeDetectionScheduler?.notify(8 /* NotificationSource.ViewAttached */);
|
|
3423
3422
|
if (lView[FLAGS] & 64 /* LViewFlags.Dirty */) {
|
|
3424
3423
|
lView[FLAGS] |= 1024 /* LViewFlags.RefreshView */;
|
|
3425
3424
|
}
|
|
@@ -4060,7 +4059,7 @@ function ɵɵnamespaceHTML() {
|
|
|
4060
4059
|
function namespaceHTMLInternal() {
|
|
4061
4060
|
instructionState.lFrame.currentNamespace = null;
|
|
4062
4061
|
}
|
|
4063
|
-
function getNamespace
|
|
4062
|
+
function getNamespace() {
|
|
4064
4063
|
return instructionState.lFrame.currentNamespace;
|
|
4065
4064
|
}
|
|
4066
4065
|
let _wasLastNodeCreated = true;
|
|
@@ -6162,7 +6161,7 @@ class PendingTasks {
|
|
|
6162
6161
|
return;
|
|
6163
6162
|
}
|
|
6164
6163
|
// Notifying the scheduler will hold application stability open until the next tick.
|
|
6165
|
-
this.scheduler.notify(
|
|
6164
|
+
this.scheduler.notify(11 /* NotificationSource.PendingTaskRemoved */);
|
|
6166
6165
|
this.internalPendingTasks.remove(taskId);
|
|
6167
6166
|
};
|
|
6168
6167
|
}
|
|
@@ -8656,6 +8655,23 @@ function assertNotInReactiveContext(debugFn, extraContext) {
|
|
|
8656
8655
|
}
|
|
8657
8656
|
}
|
|
8658
8657
|
|
|
8658
|
+
class ViewContext {
|
|
8659
|
+
view;
|
|
8660
|
+
node;
|
|
8661
|
+
constructor(view, node) {
|
|
8662
|
+
this.view = view;
|
|
8663
|
+
this.node = node;
|
|
8664
|
+
}
|
|
8665
|
+
/**
|
|
8666
|
+
* @internal
|
|
8667
|
+
* @nocollapse
|
|
8668
|
+
*/
|
|
8669
|
+
static __NG_ELEMENT_ID__ = injectViewContext;
|
|
8670
|
+
}
|
|
8671
|
+
function injectViewContext() {
|
|
8672
|
+
return new ViewContext(getLView(), getCurrentTNode());
|
|
8673
|
+
}
|
|
8674
|
+
|
|
8659
8675
|
/**
|
|
8660
8676
|
* The phase to run an `afterRender` or `afterNextRender` callback in.
|
|
8661
8677
|
*
|
|
@@ -8794,7 +8810,7 @@ class AfterRenderImpl {
|
|
|
8794
8810
|
this.sequences.add(sequence);
|
|
8795
8811
|
}
|
|
8796
8812
|
if (this.deferredRegistrations.size > 0) {
|
|
8797
|
-
this.scheduler.notify(
|
|
8813
|
+
this.scheduler.notify(7 /* NotificationSource.RenderHook */);
|
|
8798
8814
|
}
|
|
8799
8815
|
this.deferredRegistrations.clear();
|
|
8800
8816
|
if (hasSequencesToExecute) {
|
|
@@ -8802,16 +8818,27 @@ class AfterRenderImpl {
|
|
|
8802
8818
|
}
|
|
8803
8819
|
}
|
|
8804
8820
|
register(sequence) {
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
//
|
|
8808
|
-
|
|
8809
|
-
|
|
8821
|
+
const { view } = sequence;
|
|
8822
|
+
if (view !== undefined) {
|
|
8823
|
+
// Delay adding it to the manager, add it to the view instead.
|
|
8824
|
+
(view[AFTER_RENDER_SEQUENCES_TO_ADD] ??= []).push(sequence);
|
|
8825
|
+
// Mark the view for traversal to ensure we eventually schedule the afterNextRender.
|
|
8826
|
+
markAncestorsForTraversal(view);
|
|
8827
|
+
view[FLAGS] |= 8192 /* LViewFlags.HasChildViewsToRefresh */;
|
|
8828
|
+
}
|
|
8829
|
+
else if (!this.executing) {
|
|
8830
|
+
this.addSequence(sequence);
|
|
8810
8831
|
}
|
|
8811
8832
|
else {
|
|
8812
8833
|
this.deferredRegistrations.add(sequence);
|
|
8813
8834
|
}
|
|
8814
8835
|
}
|
|
8836
|
+
addSequence(sequence) {
|
|
8837
|
+
this.sequences.add(sequence);
|
|
8838
|
+
// Trigger an `ApplicationRef.tick()` if one is not already pending/running, because we have a
|
|
8839
|
+
// new render hook that needs to run.
|
|
8840
|
+
this.scheduler.notify(7 /* NotificationSource.RenderHook */);
|
|
8841
|
+
}
|
|
8815
8842
|
unregister(sequence) {
|
|
8816
8843
|
if (this.executing && this.sequences.has(sequence)) {
|
|
8817
8844
|
// We can't remove an `AfterRenderSequence` in the middle of iteration.
|
|
@@ -8841,6 +8868,7 @@ class AfterRenderImpl {
|
|
|
8841
8868
|
class AfterRenderSequence {
|
|
8842
8869
|
impl;
|
|
8843
8870
|
hooks;
|
|
8871
|
+
view;
|
|
8844
8872
|
once;
|
|
8845
8873
|
snapshot;
|
|
8846
8874
|
/**
|
|
@@ -8854,9 +8882,10 @@ class AfterRenderSequence {
|
|
|
8854
8882
|
*/
|
|
8855
8883
|
pipelinedValue = undefined;
|
|
8856
8884
|
unregisterOnDestroy;
|
|
8857
|
-
constructor(impl, hooks, once, destroyRef, snapshot = null) {
|
|
8885
|
+
constructor(impl, hooks, view, once, destroyRef, snapshot = null) {
|
|
8858
8886
|
this.impl = impl;
|
|
8859
8887
|
this.hooks = hooks;
|
|
8888
|
+
this.view = view;
|
|
8860
8889
|
this.once = once;
|
|
8861
8890
|
this.snapshot = snapshot;
|
|
8862
8891
|
this.unregisterOnDestroy = destroyRef?.onDestroy(() => this.destroy());
|
|
@@ -8874,6 +8903,10 @@ class AfterRenderSequence {
|
|
|
8874
8903
|
destroy() {
|
|
8875
8904
|
this.impl.unregister(this);
|
|
8876
8905
|
this.unregisterOnDestroy?.();
|
|
8906
|
+
const scheduled = this.view?.[AFTER_RENDER_SEQUENCES_TO_ADD];
|
|
8907
|
+
if (scheduled) {
|
|
8908
|
+
this.view[AFTER_RENDER_SEQUENCES_TO_ADD] = scheduled.filter((s) => s !== this);
|
|
8909
|
+
}
|
|
8877
8910
|
}
|
|
8878
8911
|
}
|
|
8879
8912
|
|
|
@@ -8924,7 +8957,8 @@ function afterRenderImpl(callbackOrSpec, injector, options, once) {
|
|
|
8924
8957
|
const tracing = injector.get(TracingService, null, { optional: true });
|
|
8925
8958
|
const hooks = options?.phase ?? AfterRenderPhase.MixedReadWrite;
|
|
8926
8959
|
const destroyRef = options?.manualCleanup !== true ? injector.get(DestroyRef) : null;
|
|
8927
|
-
const
|
|
8960
|
+
const viewContext = injector.get(ViewContext, null, { optional: true });
|
|
8961
|
+
const sequence = new AfterRenderSequence(manager.impl, getHooks(callbackOrSpec, hooks), viewContext?.view, once, destroyRef, tracing?.snapshot(null));
|
|
8928
8962
|
manager.impl.register(sequence);
|
|
8929
8963
|
return sequence;
|
|
8930
8964
|
}
|
|
@@ -9396,9 +9430,11 @@ function getTriggerElement(triggerLView, triggerIndex) {
|
|
|
9396
9430
|
function registerDomTrigger(initialLView, tNode, triggerIndex, walkUpTimes, registerFn, callback, type) {
|
|
9397
9431
|
const injector = initialLView[INJECTOR];
|
|
9398
9432
|
const zone = injector.get(NgZone);
|
|
9433
|
+
let poll;
|
|
9399
9434
|
function pollDomTrigger() {
|
|
9400
9435
|
// If the initial view was destroyed, we don't need to do anything.
|
|
9401
9436
|
if (isDestroyed(initialLView)) {
|
|
9437
|
+
poll.destroy();
|
|
9402
9438
|
return;
|
|
9403
9439
|
}
|
|
9404
9440
|
const lDetails = getLDeferBlockDetails(initialLView, tNode);
|
|
@@ -9406,14 +9442,16 @@ function registerDomTrigger(initialLView, tNode, triggerIndex, walkUpTimes, regi
|
|
|
9406
9442
|
// If the block was loaded before the trigger was resolved, we don't need to do anything.
|
|
9407
9443
|
if (renderedState !== DeferBlockInternalState.Initial &&
|
|
9408
9444
|
renderedState !== DeferBlockState.Placeholder) {
|
|
9445
|
+
poll.destroy();
|
|
9409
9446
|
return;
|
|
9410
9447
|
}
|
|
9411
9448
|
const triggerLView = getTriggerLView(initialLView, tNode, walkUpTimes);
|
|
9412
9449
|
// Keep polling until we resolve the trigger's LView.
|
|
9413
9450
|
if (!triggerLView) {
|
|
9414
|
-
|
|
9451
|
+
// Keep polling.
|
|
9415
9452
|
return;
|
|
9416
9453
|
}
|
|
9454
|
+
poll.destroy();
|
|
9417
9455
|
// It's possible that the trigger's view was destroyed before we resolved the trigger element.
|
|
9418
9456
|
if (isDestroyed(triggerLView)) {
|
|
9419
9457
|
return;
|
|
@@ -9440,7 +9478,7 @@ function registerDomTrigger(initialLView, tNode, triggerIndex, walkUpTimes, regi
|
|
|
9440
9478
|
storeTriggerCleanupFn(type, lDetails, cleanup);
|
|
9441
9479
|
}
|
|
9442
9480
|
// Begin polling for the trigger.
|
|
9443
|
-
|
|
9481
|
+
poll = afterRender({ read: pollDomTrigger }, { injector });
|
|
9444
9482
|
}
|
|
9445
9483
|
|
|
9446
9484
|
const DEFER_BLOCK_SSR_ID_ATTRIBUTE = 'ngb';
|
|
@@ -11648,14 +11686,6 @@ function getExpressionChangedErrorDetails(lView, bindingIndex, oldValue, newValu
|
|
|
11648
11686
|
return { propName: undefined, oldValue, newValue };
|
|
11649
11687
|
}
|
|
11650
11688
|
|
|
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
|
-
|
|
11659
11689
|
/**
|
|
11660
11690
|
* Returns an index of `classToSearch` in `className` taking token boundaries into account.
|
|
11661
11691
|
*
|
|
@@ -12265,38 +12295,13 @@ function selectIndexInternal(tView, lView, index, checkNoChangesMode) {
|
|
|
12265
12295
|
setSelectedIndex(index);
|
|
12266
12296
|
}
|
|
12267
12297
|
|
|
12268
|
-
|
|
12269
|
-
|
|
12270
|
-
|
|
12271
|
-
|
|
12272
|
-
|
|
12273
|
-
|
|
12274
|
-
|
|
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
|
-
}
|
|
12298
|
+
/** Flags describing an input for a directive. */
|
|
12299
|
+
var InputFlags;
|
|
12300
|
+
(function (InputFlags) {
|
|
12301
|
+
InputFlags[InputFlags["None"] = 0] = "None";
|
|
12302
|
+
InputFlags[InputFlags["SignalBased"] = 1] = "SignalBased";
|
|
12303
|
+
InputFlags[InputFlags["HasDecoratorInputTransform"] = 2] = "HasDecoratorInputTransform";
|
|
12304
|
+
})(InputFlags || (InputFlags = {}));
|
|
12300
12305
|
|
|
12301
12306
|
function writeToDirectiveInput(def, instance, publicName, privateName, flags, value) {
|
|
12302
12307
|
const prevConsumer = setActiveConsumer$1(null);
|
|
@@ -12364,34 +12369,6 @@ function createLView(parentLView, tView, context, flags, host, tHostNode, enviro
|
|
|
12364
12369
|
tView.type == 2 /* TViewType.Embedded */ ? parentLView[DECLARATION_COMPONENT_VIEW] : lView;
|
|
12365
12370
|
return lView;
|
|
12366
12371
|
}
|
|
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
|
-
}
|
|
12395
12372
|
function executeTemplate(tView, lView, templateFn, rf, context) {
|
|
12396
12373
|
const prevSelectedIndex = getSelectedIndex();
|
|
12397
12374
|
const isUpdatePhase = rf & 2 /* RenderFlags.Update */;
|
|
@@ -12604,106 +12581,6 @@ function applyRootElementTransformImpl(rootElement) {
|
|
|
12604
12581
|
function enableApplyRootElementTransformImpl() {
|
|
12605
12582
|
_applyRootElementTransformImpl = applyRootElementTransformImpl;
|
|
12606
12583
|
}
|
|
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
|
-
}
|
|
12707
12584
|
/**
|
|
12708
12585
|
* Mapping between attributes names that don't correspond to their element property names.
|
|
12709
12586
|
*
|
|
@@ -12804,126 +12681,6 @@ function setNgReflectProperties(lView, element, type, dataValue, value) {
|
|
|
12804
12681
|
}
|
|
12805
12682
|
}
|
|
12806
12683
|
}
|
|
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
|
-
}
|
|
12927
12684
|
/**
|
|
12928
12685
|
* Instantiate all the directives that were previously resolved on the current node.
|
|
12929
12686
|
*/
|
|
@@ -13017,121 +12774,6 @@ function findDirectiveDefMatches(tView, tNode) {
|
|
|
13017
12774
|
}
|
|
13018
12775
|
return matches;
|
|
13019
12776
|
}
|
|
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
|
-
}
|
|
13135
12777
|
/**
|
|
13136
12778
|
* Gets the initial set of LView flags based on the component definition that the LView represents.
|
|
13137
12779
|
* @param def Component definition from which to determine the flags.
|
|
@@ -13203,58 +12845,6 @@ function setInputsFromAttrs(lView, directiveIndex, instance, def, tNode, initial
|
|
|
13203
12845
|
}
|
|
13204
12846
|
}
|
|
13205
12847
|
}
|
|
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
|
-
}
|
|
13258
12848
|
//////////////////////////
|
|
13259
12849
|
//// ViewContainer & View
|
|
13260
12850
|
//////////////////////////
|
|
@@ -13653,7 +13243,7 @@ function detachViewFromDOM(tView, lView) {
|
|
|
13653
13243
|
// When we remove a view from the DOM, we need to rerun afterRender hooks
|
|
13654
13244
|
// We don't necessarily needs to run change detection. DOM removal only requires
|
|
13655
13245
|
// change detection if animations are enabled (this notification is handled by animations).
|
|
13656
|
-
lView[ENVIRONMENT].changeDetectionScheduler?.notify(
|
|
13246
|
+
lView[ENVIRONMENT].changeDetectionScheduler?.notify(9 /* NotificationSource.ViewDetachedFromDOM */);
|
|
13657
13247
|
applyView(tView, lView, lView[RENDERER], 2 /* WalkTNodeTreeAction.Detach */, null, null);
|
|
13658
13248
|
}
|
|
13659
13249
|
/**
|
|
@@ -14543,6 +14133,15 @@ function collectNativeNodesInLContainer(lContainer, result) {
|
|
|
14543
14133
|
}
|
|
14544
14134
|
}
|
|
14545
14135
|
|
|
14136
|
+
function addAfterRenderSequencesForView(lView) {
|
|
14137
|
+
if (lView[AFTER_RENDER_SEQUENCES_TO_ADD] !== null) {
|
|
14138
|
+
for (const sequence of lView[AFTER_RENDER_SEQUENCES_TO_ADD]) {
|
|
14139
|
+
sequence.impl.addSequence(sequence);
|
|
14140
|
+
}
|
|
14141
|
+
lView[AFTER_RENDER_SEQUENCES_TO_ADD].length = 0;
|
|
14142
|
+
}
|
|
14143
|
+
}
|
|
14144
|
+
|
|
14546
14145
|
let freeConsumers = [];
|
|
14547
14146
|
/**
|
|
14548
14147
|
* Create a new template consumer pointing at the specified LView.
|
|
@@ -14888,6 +14487,7 @@ function refreshView(tView, lView, templateFn, context) {
|
|
|
14888
14487
|
// no changes cycle, the component would be not be dirty for the next update pass. This would
|
|
14889
14488
|
// be different in production mode where the component dirty state is not reset.
|
|
14890
14489
|
if (!isInCheckNoChangesPass) {
|
|
14490
|
+
addAfterRenderSequencesForView(lView);
|
|
14891
14491
|
lView[FLAGS] &= ~(64 /* LViewFlags.Dirty */ | 8 /* LViewFlags.FirstLViewPass */);
|
|
14892
14492
|
}
|
|
14893
14493
|
}
|
|
@@ -15005,12 +14605,17 @@ function detectChangesInView(lView, mode) {
|
|
|
15005
14605
|
refreshView(tView, lView, tView.template, lView[CONTEXT]);
|
|
15006
14606
|
}
|
|
15007
14607
|
else if (flags & 8192 /* LViewFlags.HasChildViewsToRefresh */) {
|
|
15008
|
-
|
|
14608
|
+
if (!isInCheckNoChangesPass) {
|
|
14609
|
+
runEffectsInView(lView);
|
|
14610
|
+
}
|
|
15009
14611
|
detectChangesInEmbeddedViews(lView, 1 /* ChangeDetectionMode.Targeted */);
|
|
15010
14612
|
const components = tView.components;
|
|
15011
14613
|
if (components !== null) {
|
|
15012
14614
|
detectChangesInChildComponents(lView, components, 1 /* ChangeDetectionMode.Targeted */);
|
|
15013
14615
|
}
|
|
14616
|
+
if (!isInCheckNoChangesPass) {
|
|
14617
|
+
addAfterRenderSequencesForView(lView);
|
|
14618
|
+
}
|
|
15014
14619
|
}
|
|
15015
14620
|
}
|
|
15016
14621
|
/** Refreshes child components in the current view (update mode). */
|
|
@@ -17793,6 +17398,492 @@ function computeStaticStyling(tNode, attrs, writeToHost) {
|
|
|
17793
17398
|
writeToHost ? (tNode.classes = classes) : (tNode.classesWithoutHost = classes);
|
|
17794
17399
|
}
|
|
17795
17400
|
|
|
17401
|
+
function ɵɵdirectiveInject(token, flags = InjectFlags.Default) {
|
|
17402
|
+
const lView = getLView();
|
|
17403
|
+
// Fall back to inject() if view hasn't been created. This situation can happen in tests
|
|
17404
|
+
// if inject utilities are used before bootstrapping.
|
|
17405
|
+
if (lView === null) {
|
|
17406
|
+
// Verify that we will not get into infinite loop.
|
|
17407
|
+
ngDevMode && assertInjectImplementationNotEqual(ɵɵdirectiveInject);
|
|
17408
|
+
return ɵɵinject(token, flags);
|
|
17409
|
+
}
|
|
17410
|
+
const tNode = getCurrentTNode();
|
|
17411
|
+
const value = getOrCreateInjectable(tNode, lView, resolveForwardRef(token), flags);
|
|
17412
|
+
ngDevMode && emitInjectEvent(token, value, flags);
|
|
17413
|
+
return value;
|
|
17414
|
+
}
|
|
17415
|
+
/**
|
|
17416
|
+
* Throws an error indicating that a factory function could not be generated by the compiler for a
|
|
17417
|
+
* particular class.
|
|
17418
|
+
*
|
|
17419
|
+
* This instruction allows the actual error message to be optimized away when ngDevMode is turned
|
|
17420
|
+
* off, saving bytes of generated code while still providing a good experience in dev mode.
|
|
17421
|
+
*
|
|
17422
|
+
* The name of the class is not mentioned here, but will be in the generated factory function name
|
|
17423
|
+
* and thus in the stack trace.
|
|
17424
|
+
*
|
|
17425
|
+
* @codeGenApi
|
|
17426
|
+
*/
|
|
17427
|
+
function ɵɵinvalidFactory() {
|
|
17428
|
+
const msg = ngDevMode
|
|
17429
|
+
? `This constructor was not compatible with Dependency Injection.`
|
|
17430
|
+
: 'invalid';
|
|
17431
|
+
throw new Error(msg);
|
|
17432
|
+
}
|
|
17433
|
+
|
|
17434
|
+
/**
|
|
17435
|
+
* When elements are created dynamically after a view blueprint is created (e.g. through
|
|
17436
|
+
* i18nApply()), we need to adjust the blueprint for future template passes.
|
|
17437
|
+
*
|
|
17438
|
+
* @param tView `TView` associated with `LView`
|
|
17439
|
+
* @param lView The `LView` containing the blueprint to adjust
|
|
17440
|
+
* @param numSlotsToAlloc The number of slots to alloc in the LView, should be >0
|
|
17441
|
+
* @param initialValue Initial value to store in blueprint
|
|
17442
|
+
*/
|
|
17443
|
+
function allocExpando(tView, lView, numSlotsToAlloc, initialValue) {
|
|
17444
|
+
if (numSlotsToAlloc === 0)
|
|
17445
|
+
return -1;
|
|
17446
|
+
if (ngDevMode) {
|
|
17447
|
+
assertFirstCreatePass(tView);
|
|
17448
|
+
assertSame(tView, lView[TVIEW], '`LView` must be associated with `TView`!');
|
|
17449
|
+
assertEqual(tView.data.length, lView.length, 'Expecting LView to be same size as TView');
|
|
17450
|
+
assertEqual(tView.data.length, tView.blueprint.length, 'Expecting Blueprint to be same size as TView');
|
|
17451
|
+
assertFirstUpdatePass(tView);
|
|
17452
|
+
}
|
|
17453
|
+
const allocIdx = lView.length;
|
|
17454
|
+
for (let i = 0; i < numSlotsToAlloc; i++) {
|
|
17455
|
+
lView.push(initialValue);
|
|
17456
|
+
tView.blueprint.push(initialValue);
|
|
17457
|
+
tView.data.push(null);
|
|
17458
|
+
}
|
|
17459
|
+
return allocIdx;
|
|
17460
|
+
}
|
|
17461
|
+
|
|
17462
|
+
/**
|
|
17463
|
+
* Resolve the matched directives on a node.
|
|
17464
|
+
*/
|
|
17465
|
+
function resolveDirectives(tView, lView, tNode, localRefs, directiveMatcher) {
|
|
17466
|
+
// Please make sure to have explicit type for `exportsMap`. Inferred type triggers bug in
|
|
17467
|
+
// tsickle.
|
|
17468
|
+
ngDevMode && assertFirstCreatePass(tView);
|
|
17469
|
+
const exportsMap = localRefs === null ? null : { '': -1 };
|
|
17470
|
+
const matchedDirectiveDefs = directiveMatcher(tView, tNode);
|
|
17471
|
+
if (matchedDirectiveDefs !== null) {
|
|
17472
|
+
const [directiveDefs, hostDirectiveDefs] = resolveHostDirectives(tView, tNode, matchedDirectiveDefs);
|
|
17473
|
+
initializeDirectives(tView, lView, tNode, directiveDefs, exportsMap, hostDirectiveDefs);
|
|
17474
|
+
}
|
|
17475
|
+
if (exportsMap !== null && localRefs !== null) {
|
|
17476
|
+
cacheMatchingLocalNames(tNode, localRefs, exportsMap);
|
|
17477
|
+
}
|
|
17478
|
+
}
|
|
17479
|
+
/** Caches local names and their matching directive indices for query and template lookups. */
|
|
17480
|
+
function cacheMatchingLocalNames(tNode, localRefs, exportsMap) {
|
|
17481
|
+
const localNames = (tNode.localNames = []);
|
|
17482
|
+
// Local names must be stored in tNode in the same order that localRefs are defined
|
|
17483
|
+
// in the template to ensure the data is loaded in the same slots as their refs
|
|
17484
|
+
// in the template (for template queries).
|
|
17485
|
+
for (let i = 0; i < localRefs.length; i += 2) {
|
|
17486
|
+
const index = exportsMap[localRefs[i + 1]];
|
|
17487
|
+
if (index == null)
|
|
17488
|
+
throw new RuntimeError(-301 /* RuntimeErrorCode.EXPORT_NOT_FOUND */, ngDevMode && `Export of name '${localRefs[i + 1]}' not found!`);
|
|
17489
|
+
localNames.push(localRefs[i], index);
|
|
17490
|
+
}
|
|
17491
|
+
}
|
|
17492
|
+
function resolveHostDirectives(tView, tNode, matches) {
|
|
17493
|
+
const allDirectiveDefs = [];
|
|
17494
|
+
let hostDirectiveDefs = null;
|
|
17495
|
+
for (const def of matches) {
|
|
17496
|
+
if (def.findHostDirectiveDefs !== null) {
|
|
17497
|
+
// TODO(pk): probably could return matches instead of taking in an array to fill in?
|
|
17498
|
+
hostDirectiveDefs ??= new Map();
|
|
17499
|
+
// Components are inserted at the front of the matches array so that their lifecycle
|
|
17500
|
+
// hooks run before any directive lifecycle hooks. This appears to be for ViewEngine
|
|
17501
|
+
// compatibility. This logic doesn't make sense with host directives, because it
|
|
17502
|
+
// would allow the host directives to undo any overrides the host may have made.
|
|
17503
|
+
// To handle this case, the host directives of components are inserted at the beginning
|
|
17504
|
+
// of the array, followed by the component. As such, the insertion order is as follows:
|
|
17505
|
+
// 1. Host directives belonging to the selector-matched component.
|
|
17506
|
+
// 2. Selector-matched component.
|
|
17507
|
+
// 3. Host directives belonging to selector-matched directives.
|
|
17508
|
+
// 4. Selector-matched directives.
|
|
17509
|
+
def.findHostDirectiveDefs(def, allDirectiveDefs, hostDirectiveDefs);
|
|
17510
|
+
}
|
|
17511
|
+
if (isComponentDef(def)) {
|
|
17512
|
+
allDirectiveDefs.push(def);
|
|
17513
|
+
markAsComponentHost(tView, tNode, allDirectiveDefs.length - 1);
|
|
17514
|
+
}
|
|
17515
|
+
}
|
|
17516
|
+
if (isComponentHost(tNode)) {
|
|
17517
|
+
allDirectiveDefs.push(...matches.slice(1));
|
|
17518
|
+
}
|
|
17519
|
+
else {
|
|
17520
|
+
allDirectiveDefs.push(...matches);
|
|
17521
|
+
}
|
|
17522
|
+
if (ngDevMode) {
|
|
17523
|
+
assertNoDuplicateDirectives(allDirectiveDefs);
|
|
17524
|
+
}
|
|
17525
|
+
return [allDirectiveDefs, hostDirectiveDefs];
|
|
17526
|
+
}
|
|
17527
|
+
/**
|
|
17528
|
+
* Marks a given TNode as a component's host. This consists of:
|
|
17529
|
+
* - setting the component offset on the TNode.
|
|
17530
|
+
* - storing index of component's host element so it will be queued for view refresh during CD.
|
|
17531
|
+
*/
|
|
17532
|
+
function markAsComponentHost(tView, hostTNode, componentOffset) {
|
|
17533
|
+
ngDevMode && assertFirstCreatePass(tView);
|
|
17534
|
+
ngDevMode && assertGreaterThan(componentOffset, -1, 'componentOffset must be great than -1');
|
|
17535
|
+
hostTNode.componentOffset = componentOffset;
|
|
17536
|
+
(tView.components ??= []).push(hostTNode.index);
|
|
17537
|
+
}
|
|
17538
|
+
/** Initializes the data structures necessary for a list of directives to be instantiated. */
|
|
17539
|
+
function initializeDirectives(tView, lView, tNode, directives, exportsMap, hostDirectiveDefs) {
|
|
17540
|
+
ngDevMode && assertFirstCreatePass(tView);
|
|
17541
|
+
// Publishes the directive types to DI so they can be injected. Needs to
|
|
17542
|
+
// happen in a separate pass before the TNode flags have been initialized.
|
|
17543
|
+
for (let i = 0; i < directives.length; i++) {
|
|
17544
|
+
diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, directives[i].type);
|
|
17545
|
+
}
|
|
17546
|
+
initTNodeFlags(tNode, tView.data.length, directives.length);
|
|
17547
|
+
// When the same token is provided by several directives on the same node, some rules apply in
|
|
17548
|
+
// the viewEngine:
|
|
17549
|
+
// - viewProviders have priority over providers
|
|
17550
|
+
// - the last directive in NgModule.declarations has priority over the previous one
|
|
17551
|
+
// So to match these rules, the order in which providers are added in the arrays is very
|
|
17552
|
+
// important.
|
|
17553
|
+
for (let i = 0; i < directives.length; i++) {
|
|
17554
|
+
const def = directives[i];
|
|
17555
|
+
if (def.providersResolver)
|
|
17556
|
+
def.providersResolver(def);
|
|
17557
|
+
}
|
|
17558
|
+
let preOrderHooksFound = false;
|
|
17559
|
+
let preOrderCheckHooksFound = false;
|
|
17560
|
+
let directiveIdx = allocExpando(tView, lView, directives.length, null);
|
|
17561
|
+
ngDevMode &&
|
|
17562
|
+
assertSame(directiveIdx, tNode.directiveStart, 'TNode.directiveStart should point to just allocated space');
|
|
17563
|
+
for (let i = 0; i < directives.length; i++) {
|
|
17564
|
+
const def = directives[i];
|
|
17565
|
+
// Merge the attrs in the order of matches. This assumes that the first directive is the
|
|
17566
|
+
// component itself, so that the component has the least priority.
|
|
17567
|
+
tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
|
|
17568
|
+
configureViewWithDirective(tView, tNode, lView, directiveIdx, def);
|
|
17569
|
+
saveNameToExportMap(directiveIdx, def, exportsMap);
|
|
17570
|
+
if (def.contentQueries !== null)
|
|
17571
|
+
tNode.flags |= 4 /* TNodeFlags.hasContentQuery */;
|
|
17572
|
+
if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0)
|
|
17573
|
+
tNode.flags |= 64 /* TNodeFlags.hasHostBindings */;
|
|
17574
|
+
const lifeCycleHooks = def.type.prototype;
|
|
17575
|
+
// Only push a node index into the preOrderHooks array if this is the first
|
|
17576
|
+
// pre-order hook found on this node.
|
|
17577
|
+
if (!preOrderHooksFound &&
|
|
17578
|
+
(lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngOnInit || lifeCycleHooks.ngDoCheck)) {
|
|
17579
|
+
// We will push the actual hook function into this array later during dir instantiation.
|
|
17580
|
+
// We cannot do it now because we must ensure hooks are registered in the same
|
|
17581
|
+
// order that directives are created (i.e. injection order).
|
|
17582
|
+
(tView.preOrderHooks ??= []).push(tNode.index);
|
|
17583
|
+
preOrderHooksFound = true;
|
|
17584
|
+
}
|
|
17585
|
+
if (!preOrderCheckHooksFound && (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngDoCheck)) {
|
|
17586
|
+
(tView.preOrderCheckHooks ??= []).push(tNode.index);
|
|
17587
|
+
preOrderCheckHooksFound = true;
|
|
17588
|
+
}
|
|
17589
|
+
directiveIdx++;
|
|
17590
|
+
}
|
|
17591
|
+
initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefs);
|
|
17592
|
+
}
|
|
17593
|
+
/**
|
|
17594
|
+
* Initializes data structures required to work with directive inputs and outputs.
|
|
17595
|
+
* Initialization is done for all directives matched on a given TNode.
|
|
17596
|
+
*/
|
|
17597
|
+
function initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefinitionMap) {
|
|
17598
|
+
ngDevMode && assertFirstCreatePass(tView);
|
|
17599
|
+
const start = tNode.directiveStart;
|
|
17600
|
+
const end = tNode.directiveEnd;
|
|
17601
|
+
const tViewData = tView.data;
|
|
17602
|
+
const tNodeAttrs = tNode.attrs;
|
|
17603
|
+
const inputsFromAttrs = [];
|
|
17604
|
+
let inputsStore = null;
|
|
17605
|
+
let outputsStore = null;
|
|
17606
|
+
for (let directiveIndex = start; directiveIndex < end; directiveIndex++) {
|
|
17607
|
+
const directiveDef = tViewData[directiveIndex];
|
|
17608
|
+
const aliasData = hostDirectiveDefinitionMap
|
|
17609
|
+
? hostDirectiveDefinitionMap.get(directiveDef)
|
|
17610
|
+
: null;
|
|
17611
|
+
const aliasedInputs = aliasData ? aliasData.inputs : null;
|
|
17612
|
+
const aliasedOutputs = aliasData ? aliasData.outputs : null;
|
|
17613
|
+
inputsStore = captureNodeBindings(0 /* CaptureNodeBindingMode.Inputs */, directiveDef.inputs, directiveIndex, inputsStore, aliasedInputs);
|
|
17614
|
+
outputsStore = captureNodeBindings(1 /* CaptureNodeBindingMode.Outputs */, directiveDef.outputs, directiveIndex, outputsStore, aliasedOutputs);
|
|
17615
|
+
// Do not use unbound attributes as inputs to structural directives, since structural
|
|
17616
|
+
// directive inputs can only be set using microsyntax (e.g. `<div *dir="exp">`).
|
|
17617
|
+
const initialInputs = inputsStore !== null && tNodeAttrs !== null && !isInlineTemplate(tNode)
|
|
17618
|
+
? generateInitialInputs(inputsStore, directiveIndex, tNodeAttrs)
|
|
17619
|
+
: null;
|
|
17620
|
+
inputsFromAttrs.push(initialInputs);
|
|
17621
|
+
}
|
|
17622
|
+
if (inputsStore !== null) {
|
|
17623
|
+
if (inputsStore.hasOwnProperty('class')) {
|
|
17624
|
+
tNode.flags |= 8 /* TNodeFlags.hasClassInput */;
|
|
17625
|
+
}
|
|
17626
|
+
if (inputsStore.hasOwnProperty('style')) {
|
|
17627
|
+
tNode.flags |= 16 /* TNodeFlags.hasStyleInput */;
|
|
17628
|
+
}
|
|
17629
|
+
}
|
|
17630
|
+
tNode.initialInputs = inputsFromAttrs;
|
|
17631
|
+
tNode.inputs = inputsStore;
|
|
17632
|
+
tNode.outputs = outputsStore;
|
|
17633
|
+
}
|
|
17634
|
+
function captureNodeBindings(mode, aliasMap, directiveIndex, bindingsResult, hostDirectiveAliasMap) {
|
|
17635
|
+
for (let publicName in aliasMap) {
|
|
17636
|
+
if (!aliasMap.hasOwnProperty(publicName)) {
|
|
17637
|
+
continue;
|
|
17638
|
+
}
|
|
17639
|
+
const value = aliasMap[publicName];
|
|
17640
|
+
if (value === undefined) {
|
|
17641
|
+
continue;
|
|
17642
|
+
}
|
|
17643
|
+
bindingsResult ??= {};
|
|
17644
|
+
let internalName;
|
|
17645
|
+
let inputFlags = InputFlags.None;
|
|
17646
|
+
// For inputs, the value might be an array capturing additional
|
|
17647
|
+
// input flags.
|
|
17648
|
+
if (Array.isArray(value)) {
|
|
17649
|
+
internalName = value[0];
|
|
17650
|
+
inputFlags = value[1];
|
|
17651
|
+
}
|
|
17652
|
+
else {
|
|
17653
|
+
internalName = value;
|
|
17654
|
+
}
|
|
17655
|
+
// If there are no host directive mappings, we want to remap using the alias map from the
|
|
17656
|
+
// definition itself. If there is an alias map, it has two functions:
|
|
17657
|
+
// 1. It serves as an allowlist of bindings that are exposed by the host directives. Only the
|
|
17658
|
+
// ones inside the host directive map will be exposed on the host.
|
|
17659
|
+
// 2. The public name of the property is aliased using the host directive alias map, rather
|
|
17660
|
+
// than the alias map from the definition.
|
|
17661
|
+
let finalPublicName = publicName;
|
|
17662
|
+
if (hostDirectiveAliasMap !== null) {
|
|
17663
|
+
// If there is no mapping, it's not part of the allowlist and this input/output
|
|
17664
|
+
// is not captured and should be ignored.
|
|
17665
|
+
if (!hostDirectiveAliasMap.hasOwnProperty(publicName)) {
|
|
17666
|
+
continue;
|
|
17667
|
+
}
|
|
17668
|
+
finalPublicName = hostDirectiveAliasMap[publicName];
|
|
17669
|
+
}
|
|
17670
|
+
if (mode === 0 /* CaptureNodeBindingMode.Inputs */) {
|
|
17671
|
+
addPropertyBinding(bindingsResult, directiveIndex, finalPublicName, internalName, inputFlags);
|
|
17672
|
+
}
|
|
17673
|
+
else {
|
|
17674
|
+
addPropertyBinding(bindingsResult, directiveIndex, finalPublicName, internalName);
|
|
17675
|
+
}
|
|
17676
|
+
}
|
|
17677
|
+
return bindingsResult;
|
|
17678
|
+
}
|
|
17679
|
+
function addPropertyBinding(bindings, directiveIndex, publicName, internalName, inputFlags) {
|
|
17680
|
+
let values;
|
|
17681
|
+
if (bindings.hasOwnProperty(publicName)) {
|
|
17682
|
+
(values = bindings[publicName]).push(directiveIndex, internalName);
|
|
17683
|
+
}
|
|
17684
|
+
else {
|
|
17685
|
+
values = bindings[publicName] = [directiveIndex, internalName];
|
|
17686
|
+
}
|
|
17687
|
+
if (inputFlags !== undefined) {
|
|
17688
|
+
values.push(inputFlags);
|
|
17689
|
+
}
|
|
17690
|
+
}
|
|
17691
|
+
/**
|
|
17692
|
+
* Generates initialInputData for a node and stores it in the template's static storage
|
|
17693
|
+
* so subsequent template invocations don't have to recalculate it.
|
|
17694
|
+
*
|
|
17695
|
+
* initialInputData is an array containing values that need to be set as input properties
|
|
17696
|
+
* for directives on this node, but only once on creation. We need this array to support
|
|
17697
|
+
* the case where you set an @Input property of a directive using attribute-like syntax.
|
|
17698
|
+
* e.g. if you have a `name` @Input, you can set it once like this:
|
|
17699
|
+
*
|
|
17700
|
+
* <my-component name="Bess"></my-component>
|
|
17701
|
+
*
|
|
17702
|
+
* @param inputs Input alias map that was generated from the directive def inputs.
|
|
17703
|
+
* @param directiveIndex Index of the directive that is currently being processed.
|
|
17704
|
+
* @param attrs Static attrs on this node.
|
|
17705
|
+
*/
|
|
17706
|
+
function generateInitialInputs(inputs, directiveIndex, attrs) {
|
|
17707
|
+
let inputsToStore = null;
|
|
17708
|
+
let i = 0;
|
|
17709
|
+
while (i < attrs.length) {
|
|
17710
|
+
const attrName = attrs[i];
|
|
17711
|
+
if (attrName === 0 /* AttributeMarker.NamespaceURI */) {
|
|
17712
|
+
// We do not allow inputs on namespaced attributes.
|
|
17713
|
+
i += 4;
|
|
17714
|
+
continue;
|
|
17715
|
+
}
|
|
17716
|
+
else if (attrName === 5 /* AttributeMarker.ProjectAs */) {
|
|
17717
|
+
// Skip over the `ngProjectAs` value.
|
|
17718
|
+
i += 2;
|
|
17719
|
+
continue;
|
|
17720
|
+
}
|
|
17721
|
+
// If we hit any other attribute markers, we're done anyway. None of those are valid inputs.
|
|
17722
|
+
if (typeof attrName === 'number')
|
|
17723
|
+
break;
|
|
17724
|
+
if (inputs.hasOwnProperty(attrName)) {
|
|
17725
|
+
if (inputsToStore === null)
|
|
17726
|
+
inputsToStore = [];
|
|
17727
|
+
// Find the input's public name from the input store. Note that we can be found easier
|
|
17728
|
+
// through the directive def, but we want to do it using the inputs store so that it can
|
|
17729
|
+
// account for host directive aliases.
|
|
17730
|
+
const inputConfig = inputs[attrName];
|
|
17731
|
+
for (let j = 0; j < inputConfig.length; j += 3) {
|
|
17732
|
+
if (inputConfig[j] === directiveIndex) {
|
|
17733
|
+
inputsToStore.push(attrName, inputConfig[j + 1], inputConfig[j + 2], attrs[i + 1]);
|
|
17734
|
+
// A directive can't have multiple inputs with the same name so we can break here.
|
|
17735
|
+
break;
|
|
17736
|
+
}
|
|
17737
|
+
}
|
|
17738
|
+
}
|
|
17739
|
+
i += 2;
|
|
17740
|
+
}
|
|
17741
|
+
return inputsToStore;
|
|
17742
|
+
}
|
|
17743
|
+
/**
|
|
17744
|
+
* Setup directive for instantiation.
|
|
17745
|
+
*
|
|
17746
|
+
* We need to create a `NodeInjectorFactory` which is then inserted in both the `Blueprint` as well
|
|
17747
|
+
* as `LView`. `TView` gets the `DirectiveDef`.
|
|
17748
|
+
*
|
|
17749
|
+
* @param tView `TView`
|
|
17750
|
+
* @param tNode `TNode`
|
|
17751
|
+
* @param lView `LView`
|
|
17752
|
+
* @param directiveIndex Index where the directive will be stored in the Expando.
|
|
17753
|
+
* @param def `DirectiveDef`
|
|
17754
|
+
*/
|
|
17755
|
+
function configureViewWithDirective(tView, tNode, lView, directiveIndex, def) {
|
|
17756
|
+
ngDevMode &&
|
|
17757
|
+
assertGreaterThanOrEqual(directiveIndex, HEADER_OFFSET, 'Must be in Expando section');
|
|
17758
|
+
tView.data[directiveIndex] = def;
|
|
17759
|
+
const directiveFactory = def.factory || (def.factory = getFactoryDef(def.type, true));
|
|
17760
|
+
// Even though `directiveFactory` will already be using `ɵɵdirectiveInject` in its generated code,
|
|
17761
|
+
// we also want to support `inject()` directly from the directive constructor context so we set
|
|
17762
|
+
// `ɵɵdirectiveInject` as the inject implementation here too.
|
|
17763
|
+
const nodeInjectorFactory = new NodeInjectorFactory(directiveFactory, isComponentDef(def), ɵɵdirectiveInject);
|
|
17764
|
+
tView.blueprint[directiveIndex] = nodeInjectorFactory;
|
|
17765
|
+
lView[directiveIndex] = nodeInjectorFactory;
|
|
17766
|
+
registerHostBindingOpCodes(tView, tNode, directiveIndex, allocExpando(tView, lView, def.hostVars, NO_CHANGE), def);
|
|
17767
|
+
}
|
|
17768
|
+
/**
|
|
17769
|
+
* Add `hostBindings` to the `TView.hostBindingOpCodes`.
|
|
17770
|
+
*
|
|
17771
|
+
* @param tView `TView` to which the `hostBindings` should be added.
|
|
17772
|
+
* @param tNode `TNode` the element which contains the directive
|
|
17773
|
+
* @param directiveIdx Directive index in view.
|
|
17774
|
+
* @param directiveVarsIdx Where will the directive's vars be stored
|
|
17775
|
+
* @param def `ComponentDef`/`DirectiveDef`, which contains the `hostVars`/`hostBindings` to add.
|
|
17776
|
+
*/
|
|
17777
|
+
function registerHostBindingOpCodes(tView, tNode, directiveIdx, directiveVarsIdx, def) {
|
|
17778
|
+
ngDevMode && assertFirstCreatePass(tView);
|
|
17779
|
+
const hostBindings = def.hostBindings;
|
|
17780
|
+
if (hostBindings) {
|
|
17781
|
+
let hostBindingOpCodes = tView.hostBindingOpCodes;
|
|
17782
|
+
if (hostBindingOpCodes === null) {
|
|
17783
|
+
hostBindingOpCodes = tView.hostBindingOpCodes = [];
|
|
17784
|
+
}
|
|
17785
|
+
const elementIndx = ~tNode.index;
|
|
17786
|
+
if (lastSelectedElementIdx(hostBindingOpCodes) != elementIndx) {
|
|
17787
|
+
// Conditionally add select element so that we are more efficient in execution.
|
|
17788
|
+
// NOTE: this is strictly not necessary and it trades code size for runtime perf.
|
|
17789
|
+
// (We could just always add it.)
|
|
17790
|
+
hostBindingOpCodes.push(elementIndx);
|
|
17791
|
+
}
|
|
17792
|
+
hostBindingOpCodes.push(directiveIdx, directiveVarsIdx, hostBindings);
|
|
17793
|
+
}
|
|
17794
|
+
}
|
|
17795
|
+
/**
|
|
17796
|
+
* Returns the last selected element index in the `HostBindingOpCodes`
|
|
17797
|
+
*
|
|
17798
|
+
* For perf reasons we don't need to update the selected element index in `HostBindingOpCodes` only
|
|
17799
|
+
* if it changes. This method returns the last index (or '0' if not found.)
|
|
17800
|
+
*
|
|
17801
|
+
* Selected element index are only the ones which are negative.
|
|
17802
|
+
*/
|
|
17803
|
+
function lastSelectedElementIdx(hostBindingOpCodes) {
|
|
17804
|
+
let i = hostBindingOpCodes.length;
|
|
17805
|
+
while (i > 0) {
|
|
17806
|
+
const value = hostBindingOpCodes[--i];
|
|
17807
|
+
if (typeof value === 'number' && value < 0) {
|
|
17808
|
+
return value;
|
|
17809
|
+
}
|
|
17810
|
+
}
|
|
17811
|
+
return 0;
|
|
17812
|
+
}
|
|
17813
|
+
/**
|
|
17814
|
+
* Builds up an export map as directives are created, so local refs can be quickly mapped
|
|
17815
|
+
* to their directive instances.
|
|
17816
|
+
*/
|
|
17817
|
+
function saveNameToExportMap(directiveIdx, def, exportsMap) {
|
|
17818
|
+
if (exportsMap) {
|
|
17819
|
+
if (def.exportAs) {
|
|
17820
|
+
for (let i = 0; i < def.exportAs.length; i++) {
|
|
17821
|
+
exportsMap[def.exportAs[i]] = directiveIdx;
|
|
17822
|
+
}
|
|
17823
|
+
}
|
|
17824
|
+
if (isComponentDef(def))
|
|
17825
|
+
exportsMap[''] = directiveIdx;
|
|
17826
|
+
}
|
|
17827
|
+
}
|
|
17828
|
+
/**
|
|
17829
|
+
* Initializes the flags on the current node, setting all indices to the initial index,
|
|
17830
|
+
* the directive count to 0, and adding the isComponent flag.
|
|
17831
|
+
* @param index the initial index
|
|
17832
|
+
*/
|
|
17833
|
+
function initTNodeFlags(tNode, index, numberOfDirectives) {
|
|
17834
|
+
ngDevMode &&
|
|
17835
|
+
assertNotEqual(numberOfDirectives, tNode.directiveEnd - tNode.directiveStart, 'Reached the max number of directives');
|
|
17836
|
+
tNode.flags |= 1 /* TNodeFlags.isDirectiveHost */;
|
|
17837
|
+
// When the first directive is created on a node, save the index
|
|
17838
|
+
tNode.directiveStart = index;
|
|
17839
|
+
tNode.directiveEnd = index + numberOfDirectives;
|
|
17840
|
+
tNode.providerIndexes = index;
|
|
17841
|
+
}
|
|
17842
|
+
function assertNoDuplicateDirectives(directives) {
|
|
17843
|
+
// The array needs at least two elements in order to have duplicates.
|
|
17844
|
+
if (directives.length < 2) {
|
|
17845
|
+
return;
|
|
17846
|
+
}
|
|
17847
|
+
const seenDirectives = new Set();
|
|
17848
|
+
for (const current of directives) {
|
|
17849
|
+
if (seenDirectives.has(current)) {
|
|
17850
|
+
throw new RuntimeError(309 /* RuntimeErrorCode.DUPLICATE_DIRECTIVE */, `Directive ${current.type.name} matches multiple times on the same element. ` +
|
|
17851
|
+
`Directives can only match an element once.`);
|
|
17852
|
+
}
|
|
17853
|
+
seenDirectives.add(current);
|
|
17854
|
+
}
|
|
17855
|
+
}
|
|
17856
|
+
|
|
17857
|
+
function elementStartFirstCreatePass(index, tView, lView, name, directiveMatcher, bindingsEnabled, attrsIndex, localRefsIndex) {
|
|
17858
|
+
ngDevMode && assertFirstCreatePass(tView);
|
|
17859
|
+
ngDevMode && ngDevMode.firstCreatePass++;
|
|
17860
|
+
const tViewConsts = tView.consts;
|
|
17861
|
+
const attrs = getConstant(tViewConsts, attrsIndex);
|
|
17862
|
+
const tNode = getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, name, attrs);
|
|
17863
|
+
if (bindingsEnabled) {
|
|
17864
|
+
resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex), directiveMatcher);
|
|
17865
|
+
}
|
|
17866
|
+
// Merge the template attrs last so that they have the highest priority.
|
|
17867
|
+
tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, tNode.attrs);
|
|
17868
|
+
if (tNode.attrs !== null) {
|
|
17869
|
+
computeStaticStyling(tNode, tNode.attrs, false);
|
|
17870
|
+
}
|
|
17871
|
+
if (tNode.mergedAttrs !== null) {
|
|
17872
|
+
computeStaticStyling(tNode, tNode.mergedAttrs, true);
|
|
17873
|
+
}
|
|
17874
|
+
if (tView.queries !== null) {
|
|
17875
|
+
tView.queries.elementStart(tView, tNode);
|
|
17876
|
+
}
|
|
17877
|
+
return tNode;
|
|
17878
|
+
}
|
|
17879
|
+
function elementEndFirstCreatePass(tView, tNode) {
|
|
17880
|
+
ngDevMode && assertFirstCreatePass(tView);
|
|
17881
|
+
registerPostOrderHooks(tView, tNode);
|
|
17882
|
+
if (isContentQueryHost(tNode)) {
|
|
17883
|
+
tView.queries.elementEnd(tNode);
|
|
17884
|
+
}
|
|
17885
|
+
}
|
|
17886
|
+
|
|
17796
17887
|
class ComponentFactoryResolver extends ComponentFactoryResolver$1 {
|
|
17797
17888
|
ngModule;
|
|
17798
17889
|
/**
|
|
@@ -17837,9 +17928,51 @@ function toRefArray(map, isInputMap) {
|
|
|
17837
17928
|
}
|
|
17838
17929
|
return array;
|
|
17839
17930
|
}
|
|
17840
|
-
function
|
|
17841
|
-
|
|
17842
|
-
|
|
17931
|
+
function verifyNotAnOrphanComponent(componentDef) {
|
|
17932
|
+
// TODO(pk): create assert that verifies ngDevMode
|
|
17933
|
+
if ((typeof ngJitMode === 'undefined' || ngJitMode) &&
|
|
17934
|
+
componentDef.debugInfo?.forbidOrphanRendering) {
|
|
17935
|
+
if (depsTracker.isOrphanComponent(componentDef.type)) {
|
|
17936
|
+
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.`);
|
|
17937
|
+
}
|
|
17938
|
+
}
|
|
17939
|
+
}
|
|
17940
|
+
function createRootViewInjector(componentDef, environmentInjector, injector) {
|
|
17941
|
+
let realEnvironmentInjector = environmentInjector instanceof EnvironmentInjector
|
|
17942
|
+
? environmentInjector
|
|
17943
|
+
: environmentInjector?.injector;
|
|
17944
|
+
if (realEnvironmentInjector && componentDef.getStandaloneInjector !== null) {
|
|
17945
|
+
realEnvironmentInjector =
|
|
17946
|
+
componentDef.getStandaloneInjector(realEnvironmentInjector) || realEnvironmentInjector;
|
|
17947
|
+
}
|
|
17948
|
+
const rootViewInjector = realEnvironmentInjector
|
|
17949
|
+
? new ChainedInjector(injector, realEnvironmentInjector)
|
|
17950
|
+
: injector;
|
|
17951
|
+
return rootViewInjector;
|
|
17952
|
+
}
|
|
17953
|
+
function createRootLViewEnvironment(rootLViewInjector) {
|
|
17954
|
+
const rendererFactory = rootLViewInjector.get(RendererFactory2, null);
|
|
17955
|
+
if (rendererFactory === null) {
|
|
17956
|
+
throw new RuntimeError(407 /* RuntimeErrorCode.RENDERER_NOT_FOUND */, ngDevMode &&
|
|
17957
|
+
'Angular was not able to inject a renderer (RendererFactory2). ' +
|
|
17958
|
+
'Likely this is due to a broken DI hierarchy. ' +
|
|
17959
|
+
'Make sure that any injector used to create this component has a correct parent.');
|
|
17960
|
+
}
|
|
17961
|
+
const sanitizer = rootLViewInjector.get(Sanitizer, null);
|
|
17962
|
+
const changeDetectionScheduler = rootLViewInjector.get(ChangeDetectionScheduler, null);
|
|
17963
|
+
return {
|
|
17964
|
+
rendererFactory,
|
|
17965
|
+
sanitizer,
|
|
17966
|
+
changeDetectionScheduler,
|
|
17967
|
+
};
|
|
17968
|
+
}
|
|
17969
|
+
function createHostElement(componentDef, render) {
|
|
17970
|
+
// Determine a tag name used for creating host elements when this component is created
|
|
17971
|
+
// dynamically. Default to 'div' if this component did not specify any tag name in its
|
|
17972
|
+
// selector.
|
|
17973
|
+
const tagName = (componentDef.selectors[0][0] || 'div').toLowerCase();
|
|
17974
|
+
const namespace = tagName === 'svg' ? SVG_NAMESPACE : tagName === 'math' ? MATH_ML_NAMESPACE : null;
|
|
17975
|
+
return createElementNode(render, tagName, namespace);
|
|
17843
17976
|
}
|
|
17844
17977
|
/**
|
|
17845
17978
|
* ComponentFactory interface implementation.
|
|
@@ -17884,63 +18017,22 @@ class ComponentFactory extends ComponentFactory$1 {
|
|
|
17884
18017
|
profiler(22 /* ProfilerEvent.DynamicComponentStart */);
|
|
17885
18018
|
const prevConsumer = setActiveConsumer$1(null);
|
|
17886
18019
|
try {
|
|
17887
|
-
|
|
17888
|
-
|
|
17889
|
-
|
|
17890
|
-
|
|
17891
|
-
|
|
17892
|
-
|
|
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
|
-
}
|
|
18020
|
+
const cmpDef = this.componentDef;
|
|
18021
|
+
ngDevMode && verifyNotAnOrphanComponent(cmpDef);
|
|
18022
|
+
const tAttributes = rootSelectorOrNode
|
|
18023
|
+
? ['ng-version', '19.2.0-next.3']
|
|
18024
|
+
: // Extract attributes and classes from the first selector only to match VE behavior.
|
|
18025
|
+
extractAttrsAndClassesFromSelector(this.componentDef.selectors[0]);
|
|
17940
18026
|
// Create the root view. Uses empty TView and ContentTemplate.
|
|
17941
|
-
const rootTView = createTView(0 /* TViewType.Root */, null, null, 1, 0, null, null, null, null,
|
|
17942
|
-
const
|
|
17943
|
-
|
|
18027
|
+
const rootTView = createTView(0 /* TViewType.Root */, null, null, 1, 0, null, null, null, null, [tAttributes], null);
|
|
18028
|
+
const rootViewInjector = createRootViewInjector(cmpDef, environmentInjector || this.ngModule, injector);
|
|
18029
|
+
const environment = createRootLViewEnvironment(rootViewInjector);
|
|
18030
|
+
const hostRenderer = environment.rendererFactory.createRenderer(null, cmpDef);
|
|
18031
|
+
const hostElement = rootSelectorOrNode
|
|
18032
|
+
? locateHostElement(hostRenderer, rootSelectorOrNode, cmpDef.encapsulation, rootViewInjector)
|
|
18033
|
+
: createHostElement(cmpDef, hostRenderer);
|
|
18034
|
+
const rootLView = createLView(null, rootTView, null, 512 /* LViewFlags.IsRoot */ | getInitialLViewFlagsFromDef(cmpDef), null, null, environment, hostRenderer, rootViewInjector, null, retrieveHydrationInfo(hostElement, rootViewInjector, true /* isRootView */));
|
|
18035
|
+
rootLView[HEADER_OFFSET] = hostElement;
|
|
17944
18036
|
// rootView is the parent when bootstrapping
|
|
17945
18037
|
// TODO(misko): it looks like we are entering view here but we don't really need to as
|
|
17946
18038
|
// `renderView` does that. However as the code is written it is needed because
|
|
@@ -17949,37 +18041,22 @@ class ComponentFactory extends ComponentFactory$1 {
|
|
|
17949
18041
|
enterView(rootLView);
|
|
17950
18042
|
let componentView = null;
|
|
17951
18043
|
try {
|
|
17952
|
-
|
|
17953
|
-
|
|
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);
|
|
18044
|
+
const hostTNode = elementStartFirstCreatePass(HEADER_OFFSET, rootTView, rootLView, '#host', () => [this.componentDef], true, 0);
|
|
18045
|
+
// ---- element instruction
|
|
17968
18046
|
// TODO(crisbeto): in practice `hostRNode` should always be defined, but there are some
|
|
17969
18047
|
// tests where the renderer is mocked out and `undefined` is returned. We should update the
|
|
17970
18048
|
// tests so that this check can be removed.
|
|
17971
|
-
if (
|
|
17972
|
-
setupStaticAttributes(hostRenderer,
|
|
17973
|
-
attachPatchData(
|
|
17974
|
-
}
|
|
17975
|
-
if (projectableNodes !== undefined) {
|
|
17976
|
-
projectNodes(hostTNode, this.ngContentSelectors, projectableNodes);
|
|
18049
|
+
if (hostElement) {
|
|
18050
|
+
setupStaticAttributes(hostRenderer, hostElement, hostTNode);
|
|
18051
|
+
attachPatchData(hostElement, rootLView);
|
|
17977
18052
|
}
|
|
17978
18053
|
// TODO(pk): this logic is similar to the instruction code where a node can have directives
|
|
17979
18054
|
createDirectivesInstances(rootTView, rootLView, hostTNode);
|
|
17980
18055
|
executeContentQueries(rootTView, hostTNode, rootLView);
|
|
17981
|
-
|
|
17982
|
-
|
|
18056
|
+
elementEndFirstCreatePass(rootTView, hostTNode);
|
|
18057
|
+
if (projectableNodes !== undefined) {
|
|
18058
|
+
projectNodes(hostTNode, this.ngContentSelectors, projectableNodes);
|
|
18059
|
+
}
|
|
17983
18060
|
componentView = getComponentLViewByIndex(hostTNode.index, rootLView);
|
|
17984
18061
|
// TODO(pk): why do we need this logic?
|
|
17985
18062
|
rootLView[CONTEXT] = componentView[CONTEXT];
|
|
@@ -20595,7 +20672,11 @@ function templateFirstCreatePass(index, tView, lView, templateFn, decls, vars, t
|
|
|
20595
20672
|
const tViewConsts = tView.consts;
|
|
20596
20673
|
// TODO(pk): refactor getOrCreateTNode to have the "create" only version
|
|
20597
20674
|
const tNode = getOrCreateTNode(tView, index, 4 /* TNodeType.Container */, tagName || null, attrs || null);
|
|
20598
|
-
|
|
20675
|
+
if (getBindingsEnabled()) {
|
|
20676
|
+
resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex), findDirectiveDefMatches);
|
|
20677
|
+
}
|
|
20678
|
+
// Merge the template attrs last so that they have the highest priority.
|
|
20679
|
+
tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, tNode.attrs);
|
|
20599
20680
|
registerPostOrderHooks(tView, tNode);
|
|
20600
20681
|
const embeddedTView = (tNode.tView = createTView(2 /* TViewType.Embedded */, tNode, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, tView.schemas, tViewConsts, null /* ssrId */));
|
|
20601
20682
|
if (tView.queries !== null) {
|
|
@@ -22898,6 +22979,77 @@ function setTestabilityGetter(getter) {
|
|
|
22898
22979
|
}
|
|
22899
22980
|
let _testabilityGetter;
|
|
22900
22981
|
|
|
22982
|
+
/**
|
|
22983
|
+
* A scheduler which manages the execution of effects.
|
|
22984
|
+
*/
|
|
22985
|
+
class EffectScheduler {
|
|
22986
|
+
/** @nocollapse */
|
|
22987
|
+
static ɵprov = /** @pureOrBreakMyCode */ /* @__PURE__ */ ɵɵdefineInjectable({
|
|
22988
|
+
token: EffectScheduler,
|
|
22989
|
+
providedIn: 'root',
|
|
22990
|
+
factory: () => new ZoneAwareEffectScheduler(),
|
|
22991
|
+
});
|
|
22992
|
+
}
|
|
22993
|
+
/**
|
|
22994
|
+
* A wrapper around `ZoneAwareQueueingScheduler` that schedules flushing via the microtask queue
|
|
22995
|
+
* when.
|
|
22996
|
+
*/
|
|
22997
|
+
class ZoneAwareEffectScheduler {
|
|
22998
|
+
queuedEffectCount = 0;
|
|
22999
|
+
queues = new Map();
|
|
23000
|
+
schedule(handle) {
|
|
23001
|
+
this.enqueue(handle);
|
|
23002
|
+
}
|
|
23003
|
+
remove(handle) {
|
|
23004
|
+
const zone = handle.zone;
|
|
23005
|
+
const queue = this.queues.get(zone);
|
|
23006
|
+
if (!queue.has(handle)) {
|
|
23007
|
+
return;
|
|
23008
|
+
}
|
|
23009
|
+
queue.delete(handle);
|
|
23010
|
+
this.queuedEffectCount--;
|
|
23011
|
+
}
|
|
23012
|
+
enqueue(handle) {
|
|
23013
|
+
const zone = handle.zone;
|
|
23014
|
+
if (!this.queues.has(zone)) {
|
|
23015
|
+
this.queues.set(zone, new Set());
|
|
23016
|
+
}
|
|
23017
|
+
const queue = this.queues.get(zone);
|
|
23018
|
+
if (queue.has(handle)) {
|
|
23019
|
+
return;
|
|
23020
|
+
}
|
|
23021
|
+
this.queuedEffectCount++;
|
|
23022
|
+
queue.add(handle);
|
|
23023
|
+
}
|
|
23024
|
+
/**
|
|
23025
|
+
* Run all scheduled effects.
|
|
23026
|
+
*
|
|
23027
|
+
* Execution order of effects within the same zone is guaranteed to be FIFO, but there is no
|
|
23028
|
+
* ordering guarantee between effects scheduled in different zones.
|
|
23029
|
+
*/
|
|
23030
|
+
flush() {
|
|
23031
|
+
while (this.queuedEffectCount > 0) {
|
|
23032
|
+
for (const [zone, queue] of this.queues) {
|
|
23033
|
+
// `zone` here must be defined.
|
|
23034
|
+
if (zone === null) {
|
|
23035
|
+
this.flushQueue(queue);
|
|
23036
|
+
}
|
|
23037
|
+
else {
|
|
23038
|
+
zone.run(() => this.flushQueue(queue));
|
|
23039
|
+
}
|
|
23040
|
+
}
|
|
23041
|
+
}
|
|
23042
|
+
}
|
|
23043
|
+
flushQueue(queue) {
|
|
23044
|
+
for (const handle of queue) {
|
|
23045
|
+
queue.delete(handle);
|
|
23046
|
+
this.queuedEffectCount--;
|
|
23047
|
+
// TODO: what happens if this throws an error?
|
|
23048
|
+
handle.run();
|
|
23049
|
+
}
|
|
23050
|
+
}
|
|
23051
|
+
}
|
|
23052
|
+
|
|
22901
23053
|
/**
|
|
22902
23054
|
* Determine if the argument is shaped like a Promise
|
|
22903
23055
|
*/
|
|
@@ -23157,77 +23309,6 @@ class ApplicationInitStatus {
|
|
|
23157
23309
|
args: [{ providedIn: 'root' }]
|
|
23158
23310
|
}], () => [], null); })();
|
|
23159
23311
|
|
|
23160
|
-
/**
|
|
23161
|
-
* A scheduler which manages the execution of effects.
|
|
23162
|
-
*/
|
|
23163
|
-
class EffectScheduler {
|
|
23164
|
-
/** @nocollapse */
|
|
23165
|
-
static ɵprov = /** @pureOrBreakMyCode */ /* @__PURE__ */ ɵɵdefineInjectable({
|
|
23166
|
-
token: EffectScheduler,
|
|
23167
|
-
providedIn: 'root',
|
|
23168
|
-
factory: () => new ZoneAwareEffectScheduler(),
|
|
23169
|
-
});
|
|
23170
|
-
}
|
|
23171
|
-
/**
|
|
23172
|
-
* A wrapper around `ZoneAwareQueueingScheduler` that schedules flushing via the microtask queue
|
|
23173
|
-
* when.
|
|
23174
|
-
*/
|
|
23175
|
-
class ZoneAwareEffectScheduler {
|
|
23176
|
-
queuedEffectCount = 0;
|
|
23177
|
-
queues = new Map();
|
|
23178
|
-
schedule(handle) {
|
|
23179
|
-
this.enqueue(handle);
|
|
23180
|
-
}
|
|
23181
|
-
remove(handle) {
|
|
23182
|
-
const zone = handle.zone;
|
|
23183
|
-
const queue = this.queues.get(zone);
|
|
23184
|
-
if (!queue.has(handle)) {
|
|
23185
|
-
return;
|
|
23186
|
-
}
|
|
23187
|
-
queue.delete(handle);
|
|
23188
|
-
this.queuedEffectCount--;
|
|
23189
|
-
}
|
|
23190
|
-
enqueue(handle) {
|
|
23191
|
-
const zone = handle.zone;
|
|
23192
|
-
if (!this.queues.has(zone)) {
|
|
23193
|
-
this.queues.set(zone, new Set());
|
|
23194
|
-
}
|
|
23195
|
-
const queue = this.queues.get(zone);
|
|
23196
|
-
if (queue.has(handle)) {
|
|
23197
|
-
return;
|
|
23198
|
-
}
|
|
23199
|
-
this.queuedEffectCount++;
|
|
23200
|
-
queue.add(handle);
|
|
23201
|
-
}
|
|
23202
|
-
/**
|
|
23203
|
-
* Run all scheduled effects.
|
|
23204
|
-
*
|
|
23205
|
-
* Execution order of effects within the same zone is guaranteed to be FIFO, but there is no
|
|
23206
|
-
* ordering guarantee between effects scheduled in different zones.
|
|
23207
|
-
*/
|
|
23208
|
-
flush() {
|
|
23209
|
-
while (this.queuedEffectCount > 0) {
|
|
23210
|
-
for (const [zone, queue] of this.queues) {
|
|
23211
|
-
// `zone` here must be defined.
|
|
23212
|
-
if (zone === null) {
|
|
23213
|
-
this.flushQueue(queue);
|
|
23214
|
-
}
|
|
23215
|
-
else {
|
|
23216
|
-
zone.run(() => this.flushQueue(queue));
|
|
23217
|
-
}
|
|
23218
|
-
}
|
|
23219
|
-
}
|
|
23220
|
-
}
|
|
23221
|
-
flushQueue(queue) {
|
|
23222
|
-
for (const handle of queue) {
|
|
23223
|
-
queue.delete(handle);
|
|
23224
|
-
this.queuedEffectCount--;
|
|
23225
|
-
// TODO: what happens if this throws an error?
|
|
23226
|
-
handle.run();
|
|
23227
|
-
}
|
|
23228
|
-
}
|
|
23229
|
-
}
|
|
23230
|
-
|
|
23231
23312
|
/**
|
|
23232
23313
|
* A DI token that provides a set of callbacks to
|
|
23233
23314
|
* be called for every component that is bootstrapped.
|
|
@@ -23269,24 +23350,6 @@ class NgProbeToken {
|
|
|
23269
23350
|
}
|
|
23270
23351
|
/** Maximum number of times ApplicationRef will refresh all attached views in a single tick. */
|
|
23271
23352
|
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
|
-
}
|
|
23290
23353
|
function optionsReducer(dst, objs) {
|
|
23291
23354
|
if (Array.isArray(objs)) {
|
|
23292
23355
|
return objs.reduce(optionsReducer, dst);
|
|
@@ -23405,12 +23468,6 @@ class ApplicationRef {
|
|
|
23405
23468
|
* @internal
|
|
23406
23469
|
*/
|
|
23407
23470
|
dirtyFlags = 0 /* ApplicationRefDirtyFlags.None */;
|
|
23408
|
-
/**
|
|
23409
|
-
* Like `dirtyFlags` but don't cause `tick()` to loop.
|
|
23410
|
-
*
|
|
23411
|
-
* @internal
|
|
23412
|
-
*/
|
|
23413
|
-
deferredDirtyFlags = 0 /* ApplicationRefDirtyFlags.None */;
|
|
23414
23471
|
/**
|
|
23415
23472
|
* Most recent snapshot from the `TracingService`, if any.
|
|
23416
23473
|
*
|
|
@@ -23576,18 +23633,19 @@ class ApplicationRef {
|
|
|
23576
23633
|
this._tick();
|
|
23577
23634
|
}
|
|
23578
23635
|
/** @internal */
|
|
23579
|
-
_tick
|
|
23636
|
+
_tick() {
|
|
23580
23637
|
profiler(12 /* ProfilerEvent.ChangeDetectionStart */);
|
|
23581
23638
|
if (this.tracingSnapshot !== null) {
|
|
23582
|
-
|
|
23583
|
-
this.tracingSnapshot = null;
|
|
23584
|
-
// Ensure we always run `_tick()` in the context of the most recent snapshot,
|
|
23639
|
+
// Ensure we always run `tickImpl()` in the context of the most recent snapshot,
|
|
23585
23640
|
// if one exists. Snapshots may be reference counted by the implementation so
|
|
23586
23641
|
// we want to ensure that if we request a snapshot that we use it.
|
|
23587
|
-
|
|
23588
|
-
|
|
23589
|
-
|
|
23642
|
+
this.tracingSnapshot.run(TracingAction.CHANGE_DETECTION, this.tickImpl);
|
|
23643
|
+
}
|
|
23644
|
+
else {
|
|
23645
|
+
this.tickImpl();
|
|
23590
23646
|
}
|
|
23647
|
+
}
|
|
23648
|
+
tickImpl = () => {
|
|
23591
23649
|
(typeof ngDevMode === 'undefined' || ngDevMode) && warnIfDestroyed(this._destroyed);
|
|
23592
23650
|
if (this._runningTick) {
|
|
23593
23651
|
throw new RuntimeError(101 /* RuntimeErrorCode.RECURSIVE_APPLICATION_REF_TICK */, ngDevMode && 'ApplicationRef.tick is called recursively');
|
|
@@ -23608,6 +23666,8 @@ class ApplicationRef {
|
|
|
23608
23666
|
}
|
|
23609
23667
|
finally {
|
|
23610
23668
|
this._runningTick = false;
|
|
23669
|
+
this.tracingSnapshot?.dispose();
|
|
23670
|
+
this.tracingSnapshot = null;
|
|
23611
23671
|
setActiveConsumer$1(prevConsumer);
|
|
23612
23672
|
this.afterTick.next();
|
|
23613
23673
|
profiler(13 /* ProfilerEvent.ChangeDetectionEnd */);
|
|
@@ -23621,9 +23681,6 @@ class ApplicationRef {
|
|
|
23621
23681
|
if (this._rendererFactory === null && !this._injector.destroyed) {
|
|
23622
23682
|
this._rendererFactory = this._injector.get(RendererFactory2, null, { optional: true });
|
|
23623
23683
|
}
|
|
23624
|
-
// When beginning synchronization, all deferred dirtiness becomes active dirtiness.
|
|
23625
|
-
this.dirtyFlags |= this.deferredDirtyFlags;
|
|
23626
|
-
this.deferredDirtyFlags = 0 /* ApplicationRefDirtyFlags.None */;
|
|
23627
23684
|
let runs = 0;
|
|
23628
23685
|
while (this.dirtyFlags !== 0 /* ApplicationRefDirtyFlags.None */ && runs++ < MAXIMUM_REFRESH_RERUNS) {
|
|
23629
23686
|
profiler(14 /* ProfilerEvent.ChangeDetectionSyncStart */);
|
|
@@ -23641,9 +23698,6 @@ class ApplicationRef {
|
|
|
23641
23698
|
* Perform a single synchronization pass.
|
|
23642
23699
|
*/
|
|
23643
23700
|
synchronizeOnce() {
|
|
23644
|
-
// If we happened to loop, deferred dirtiness can be processed as active dirtiness again.
|
|
23645
|
-
this.dirtyFlags |= this.deferredDirtyFlags;
|
|
23646
|
-
this.deferredDirtyFlags = 0 /* ApplicationRefDirtyFlags.None */;
|
|
23647
23701
|
// First, process any dirty root effects.
|
|
23648
23702
|
if (this.dirtyFlags & 16 /* ApplicationRefDirtyFlags.RootEffects */) {
|
|
23649
23703
|
this.dirtyFlags &= ~16 /* ApplicationRefDirtyFlags.RootEffects */;
|
|
@@ -27979,24 +28033,6 @@ function getExistingTNode(tView, index) {
|
|
|
27979
28033
|
return tNode;
|
|
27980
28034
|
}
|
|
27981
28035
|
|
|
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
|
-
}
|
|
28000
28036
|
/**
|
|
28001
28037
|
* Create DOM element. The instruction must later be followed by `elementEnd()` call.
|
|
28002
28038
|
*
|
|
@@ -28021,7 +28057,7 @@ function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
|
|
|
28021
28057
|
ngDevMode && assertIndexInRange(lView, adjustedIndex);
|
|
28022
28058
|
const renderer = lView[RENDERER];
|
|
28023
28059
|
const tNode = tView.firstCreatePass
|
|
28024
|
-
? elementStartFirstCreatePass(adjustedIndex, tView, lView, name, attrsIndex, localRefsIndex)
|
|
28060
|
+
? elementStartFirstCreatePass(adjustedIndex, tView, lView, name, findDirectiveDefMatches, getBindingsEnabled(), attrsIndex, localRefsIndex)
|
|
28025
28061
|
: tView.data[adjustedIndex];
|
|
28026
28062
|
const native = _locateOrCreateElementNode(tView, lView, tNode, renderer, name, index);
|
|
28027
28063
|
lView[adjustedIndex] = native;
|
|
@@ -28077,10 +28113,7 @@ function ɵɵelementEnd() {
|
|
|
28077
28113
|
decreaseElementDepthCount();
|
|
28078
28114
|
const tView = getTView();
|
|
28079
28115
|
if (tView.firstCreatePass) {
|
|
28080
|
-
|
|
28081
|
-
if (isContentQueryHost(currentTNode)) {
|
|
28082
|
-
tView.queries.elementEnd(currentTNode);
|
|
28083
|
-
}
|
|
28116
|
+
elementEndFirstCreatePass(tView, tNode);
|
|
28084
28117
|
}
|
|
28085
28118
|
if (tNode.classesWithoutHost != null && hasClassInput(tNode)) {
|
|
28086
28119
|
setDirectiveInputsWhichShadowsStyling(tView, tNode, getLView(), tNode.classesWithoutHost, true);
|
|
@@ -28108,7 +28141,7 @@ function ɵɵelement(index, name, attrsIndex, localRefsIndex) {
|
|
|
28108
28141
|
}
|
|
28109
28142
|
let _locateOrCreateElementNode = (tView, lView, tNode, renderer, name, index) => {
|
|
28110
28143
|
lastNodeWasCreated(true);
|
|
28111
|
-
return createElementNode(renderer, name, getNamespace
|
|
28144
|
+
return createElementNode(renderer, name, getNamespace());
|
|
28112
28145
|
};
|
|
28113
28146
|
/**
|
|
28114
28147
|
* Enables hydration code path (to lookup existing elements in DOM)
|
|
@@ -28123,7 +28156,7 @@ function locateOrCreateElementNodeImpl(tView, lView, tNode, renderer, name, inde
|
|
|
28123
28156
|
lastNodeWasCreated(isNodeCreationMode);
|
|
28124
28157
|
// Regular creation mode.
|
|
28125
28158
|
if (isNodeCreationMode) {
|
|
28126
|
-
return createElementNode(renderer, name, getNamespace
|
|
28159
|
+
return createElementNode(renderer, name, getNamespace());
|
|
28127
28160
|
}
|
|
28128
28161
|
// Hydration mode, looking up an existing element in DOM.
|
|
28129
28162
|
const native = locateNextRNode(hydrationInfo, tView, lView, tNode);
|
|
@@ -28176,7 +28209,11 @@ function elementContainerStartFirstCreatePass(index, tView, lView, attrsIndex, l
|
|
|
28176
28209
|
computeStaticStyling(tNode, attrs, true);
|
|
28177
28210
|
}
|
|
28178
28211
|
const localRefs = getConstant(tViewConsts, localRefsIndex);
|
|
28179
|
-
|
|
28212
|
+
if (getBindingsEnabled()) {
|
|
28213
|
+
resolveDirectives(tView, lView, tNode, localRefs, findDirectiveDefMatches);
|
|
28214
|
+
}
|
|
28215
|
+
// Merge the template attrs last so that they have the highest priority.
|
|
28216
|
+
tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, tNode.attrs);
|
|
28180
28217
|
if (tView.queries !== null) {
|
|
28181
28218
|
tView.queries.elementStart(tView, tNode);
|
|
28182
28219
|
}
|
|
@@ -33337,9 +33374,13 @@ function ɵsetClassDebugInfo(type, debugInfo) {
|
|
|
33337
33374
|
* @param applyMetadata Callback that will apply a new set of metadata on the `type` when invoked.
|
|
33338
33375
|
* @param environment Syntehtic namespace imports that need to be passed along to the callback.
|
|
33339
33376
|
* @param locals Local symbols from the source location that have to be exposed to the callback.
|
|
33377
|
+
* @param importMeta `import.meta` from the call site of the replacement function. Optional since
|
|
33378
|
+
* it isn't used internally.
|
|
33379
|
+
* @param id ID to the class being replaced. **Not** the same as the component definition ID.
|
|
33380
|
+
* Optional since the ID might not be available internally.
|
|
33340
33381
|
* @codeGenApi
|
|
33341
33382
|
*/
|
|
33342
|
-
function ɵɵreplaceMetadata(type, applyMetadata, namespaces, locals) {
|
|
33383
|
+
function ɵɵreplaceMetadata(type, applyMetadata, namespaces, locals, importMeta = null, id = null) {
|
|
33343
33384
|
ngDevMode && assertComponentDef(type);
|
|
33344
33385
|
const currentDef = getComponentDef(type);
|
|
33345
33386
|
// The reason `applyMetadata` is a callback that is invoked (almost) immediately is because
|
|
@@ -33361,7 +33402,7 @@ function ɵɵreplaceMetadata(type, applyMetadata, namespaces, locals) {
|
|
|
33361
33402
|
// Note: we have the additional check, because `IsRoot` can also indicate
|
|
33362
33403
|
// a component created through something like `createComponent`.
|
|
33363
33404
|
if (isRootView(root) && root[PARENT] === null) {
|
|
33364
|
-
recreateMatchingLViews(newDef, oldDef, root);
|
|
33405
|
+
recreateMatchingLViews(importMeta, id, newDef, oldDef, root);
|
|
33365
33406
|
}
|
|
33366
33407
|
}
|
|
33367
33408
|
}
|
|
@@ -33397,10 +33438,12 @@ function mergeWithExistingDefinition(currentDef, newDef) {
|
|
|
33397
33438
|
}
|
|
33398
33439
|
/**
|
|
33399
33440
|
* Finds all LViews matching a specific component definition and recreates them.
|
|
33441
|
+
* @param importMeta `import.meta` information.
|
|
33442
|
+
* @param id HMR ID of the component.
|
|
33400
33443
|
* @param oldDef Component definition to search for.
|
|
33401
33444
|
* @param rootLView View from which to start the search.
|
|
33402
33445
|
*/
|
|
33403
|
-
function recreateMatchingLViews(newDef, oldDef, rootLView) {
|
|
33446
|
+
function recreateMatchingLViews(importMeta, id, newDef, oldDef, rootLView) {
|
|
33404
33447
|
ngDevMode &&
|
|
33405
33448
|
assertDefined(oldDef.tView, 'Expected a component definition that has been instantiated at least once');
|
|
33406
33449
|
const tView = rootLView[TVIEW];
|
|
@@ -33408,7 +33451,7 @@ function recreateMatchingLViews(newDef, oldDef, rootLView) {
|
|
|
33408
33451
|
// produce false positives when using inheritance.
|
|
33409
33452
|
if (tView === oldDef.tView) {
|
|
33410
33453
|
ngDevMode && assertComponentDef(oldDef.type);
|
|
33411
|
-
recreateLView(newDef, oldDef, rootLView);
|
|
33454
|
+
recreateLView(importMeta, id, newDef, oldDef, rootLView);
|
|
33412
33455
|
return;
|
|
33413
33456
|
}
|
|
33414
33457
|
for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
|
|
@@ -33416,14 +33459,14 @@ function recreateMatchingLViews(newDef, oldDef, rootLView) {
|
|
|
33416
33459
|
if (isLContainer(current)) {
|
|
33417
33460
|
// The host can be an LView if a component is injecting `ViewContainerRef`.
|
|
33418
33461
|
if (isLView(current[HOST])) {
|
|
33419
|
-
recreateMatchingLViews(newDef, oldDef, current[HOST]);
|
|
33462
|
+
recreateMatchingLViews(importMeta, id, newDef, oldDef, current[HOST]);
|
|
33420
33463
|
}
|
|
33421
33464
|
for (let j = CONTAINER_HEADER_OFFSET; j < current.length; j++) {
|
|
33422
|
-
recreateMatchingLViews(newDef, oldDef, current[j]);
|
|
33465
|
+
recreateMatchingLViews(importMeta, id, newDef, oldDef, current[j]);
|
|
33423
33466
|
}
|
|
33424
33467
|
}
|
|
33425
33468
|
else if (isLView(current)) {
|
|
33426
|
-
recreateMatchingLViews(newDef, oldDef, current);
|
|
33469
|
+
recreateMatchingLViews(importMeta, id, newDef, oldDef, current);
|
|
33427
33470
|
}
|
|
33428
33471
|
}
|
|
33429
33472
|
}
|
|
@@ -33442,11 +33485,13 @@ function clearRendererCache(factory, def) {
|
|
|
33442
33485
|
}
|
|
33443
33486
|
/**
|
|
33444
33487
|
* Recreates an LView in-place from a new component definition.
|
|
33488
|
+
* @param importMeta `import.meta` information.
|
|
33489
|
+
* @param id HMR ID for the component.
|
|
33445
33490
|
* @param newDef Definition from which to recreate the view.
|
|
33446
33491
|
* @param oldDef Previous component definition being swapped out.
|
|
33447
33492
|
* @param lView View to be recreated.
|
|
33448
33493
|
*/
|
|
33449
|
-
function recreateLView(newDef, oldDef, lView) {
|
|
33494
|
+
function recreateLView(importMeta, id, newDef, oldDef, lView) {
|
|
33450
33495
|
const instance = lView[CONTEXT];
|
|
33451
33496
|
let host = lView[HOST];
|
|
33452
33497
|
// In theory the parent can also be an LContainer, but it appears like that's
|
|
@@ -33498,10 +33543,29 @@ function recreateLView(newDef, oldDef, lView) {
|
|
|
33498
33543
|
};
|
|
33499
33544
|
// The callback isn't guaranteed to be inside the Zone so we need to bring it in ourselves.
|
|
33500
33545
|
if (zone === null) {
|
|
33501
|
-
recreate
|
|
33546
|
+
executeWithInvalidateFallback(importMeta, id, recreate);
|
|
33502
33547
|
}
|
|
33503
33548
|
else {
|
|
33504
|
-
zone.run(recreate);
|
|
33549
|
+
zone.run(() => executeWithInvalidateFallback(importMeta, id, recreate));
|
|
33550
|
+
}
|
|
33551
|
+
}
|
|
33552
|
+
/**
|
|
33553
|
+
* Runs an HMR-related function and falls back to
|
|
33554
|
+
* invalidating the HMR data if it throws an error.
|
|
33555
|
+
*/
|
|
33556
|
+
function executeWithInvalidateFallback(importMeta, id, callback) {
|
|
33557
|
+
try {
|
|
33558
|
+
callback();
|
|
33559
|
+
}
|
|
33560
|
+
catch (e) {
|
|
33561
|
+
const errorMessage = e.message;
|
|
33562
|
+
// If we have all the necessary information and APIs to send off the invalidation
|
|
33563
|
+
// request, send it before rethrowing so the dev server can decide what to do.
|
|
33564
|
+
if (id !== null && errorMessage) {
|
|
33565
|
+
importMeta?.hot?.send?.('angular:invalidate', { id, message: errorMessage, error: true });
|
|
33566
|
+
}
|
|
33567
|
+
// Throw the error in case the page doesn't get refreshed.
|
|
33568
|
+
throw e;
|
|
33505
33569
|
}
|
|
33506
33570
|
}
|
|
33507
33571
|
/**
|
|
@@ -34929,7 +34993,7 @@ class Version {
|
|
|
34929
34993
|
/**
|
|
34930
34994
|
* @publicApi
|
|
34931
34995
|
*/
|
|
34932
|
-
const VERSION = new Version('19.2.0-next.
|
|
34996
|
+
const VERSION = new Version('19.2.0-next.3');
|
|
34933
34997
|
|
|
34934
34998
|
/**
|
|
34935
34999
|
* Combination of NgModuleFactory and ComponentFactories.
|
|
@@ -35339,13 +35403,6 @@ class ChangeDetectionSchedulerImpl {
|
|
|
35339
35403
|
this.appRef.dirtyFlags |= 4 /* ApplicationRefDirtyFlags.ViewTreeCheck */;
|
|
35340
35404
|
break;
|
|
35341
35405
|
}
|
|
35342
|
-
case 8 /* NotificationSource.DeferredRenderHook */: {
|
|
35343
|
-
// Render hooks are "deferred" when they're triggered from other render hooks. Using the
|
|
35344
|
-
// deferred dirty flags ensures that adding new hooks doesn't automatically trigger a loop
|
|
35345
|
-
// inside tick().
|
|
35346
|
-
this.appRef.deferredDirtyFlags |= 8 /* ApplicationRefDirtyFlags.AfterRender */;
|
|
35347
|
-
break;
|
|
35348
|
-
}
|
|
35349
35406
|
case 6 /* NotificationSource.CustomElement */: {
|
|
35350
35407
|
// We use `ViewTreeTraversal` to ensure we refresh the element even if this is triggered
|
|
35351
35408
|
// during CD. In practice this is a no-op since the elements code also calls via a
|
|
@@ -35354,7 +35411,7 @@ class ChangeDetectionSchedulerImpl {
|
|
|
35354
35411
|
force = true;
|
|
35355
35412
|
break;
|
|
35356
35413
|
}
|
|
35357
|
-
case
|
|
35414
|
+
case 12 /* NotificationSource.RootEffect */: {
|
|
35358
35415
|
this.appRef.dirtyFlags |= 16 /* ApplicationRefDirtyFlags.RootEffects */;
|
|
35359
35416
|
// Root effects still force a CD, even if the scheduler is disabled. This ensures that
|
|
35360
35417
|
// effects always run, even when triggered from outside the zone when the scheduler is
|
|
@@ -35362,7 +35419,7 @@ class ChangeDetectionSchedulerImpl {
|
|
|
35362
35419
|
force = true;
|
|
35363
35420
|
break;
|
|
35364
35421
|
}
|
|
35365
|
-
case
|
|
35422
|
+
case 13 /* NotificationSource.ViewEffect */: {
|
|
35366
35423
|
// This is technically a no-op, since view effects will also send a
|
|
35367
35424
|
// `MarkAncestorsForTraversal` notification. Still, we set this for logical consistency.
|
|
35368
35425
|
this.appRef.dirtyFlags |= 2 /* ApplicationRefDirtyFlags.ViewTreeTraversal */;
|
|
@@ -35372,7 +35429,7 @@ class ChangeDetectionSchedulerImpl {
|
|
|
35372
35429
|
force = true;
|
|
35373
35430
|
break;
|
|
35374
35431
|
}
|
|
35375
|
-
case
|
|
35432
|
+
case 11 /* NotificationSource.PendingTaskRemoved */: {
|
|
35376
35433
|
// Removing a pending task via the public API forces a scheduled tick, ensuring that
|
|
35377
35434
|
// stability is async and delayed until there was at least an opportunity to run
|
|
35378
35435
|
// application synchronization. This prevents some footguns when working with the
|
|
@@ -35381,10 +35438,10 @@ class ChangeDetectionSchedulerImpl {
|
|
|
35381
35438
|
force = true;
|
|
35382
35439
|
break;
|
|
35383
35440
|
}
|
|
35384
|
-
case
|
|
35385
|
-
case
|
|
35441
|
+
case 9 /* NotificationSource.ViewDetachedFromDOM */:
|
|
35442
|
+
case 8 /* NotificationSource.ViewAttached */:
|
|
35386
35443
|
case 7 /* NotificationSource.RenderHook */:
|
|
35387
|
-
case
|
|
35444
|
+
case 10 /* NotificationSource.AsyncAnimationsLoaded */:
|
|
35388
35445
|
default: {
|
|
35389
35446
|
// These notifications only schedule a tick but do not change whether we should refresh
|
|
35390
35447
|
// views. Instead, we only need to run render hooks unless another notification from the
|
|
@@ -36078,6 +36135,24 @@ function moduleDoBootstrap(moduleRef, allPlatformModules) {
|
|
|
36078
36135
|
}
|
|
36079
36136
|
allPlatformModules.push(moduleRef);
|
|
36080
36137
|
}
|
|
36138
|
+
function _callAndReportToErrorHandler(errorHandler, ngZone, callback) {
|
|
36139
|
+
try {
|
|
36140
|
+
const result = callback();
|
|
36141
|
+
if (isPromise(result)) {
|
|
36142
|
+
return result.catch((e) => {
|
|
36143
|
+
ngZone.runOutsideAngular(() => errorHandler.handleError(e));
|
|
36144
|
+
// rethrow as the exception handler might not do it
|
|
36145
|
+
throw e;
|
|
36146
|
+
});
|
|
36147
|
+
}
|
|
36148
|
+
return result;
|
|
36149
|
+
}
|
|
36150
|
+
catch (e) {
|
|
36151
|
+
ngZone.runOutsideAngular(() => errorHandler.handleError(e));
|
|
36152
|
+
// rethrow as the exception handler might not do it
|
|
36153
|
+
throw e;
|
|
36154
|
+
}
|
|
36155
|
+
}
|
|
36081
36156
|
|
|
36082
36157
|
/**
|
|
36083
36158
|
* The Angular platform is the entry point for Angular on a web page.
|
|
@@ -40575,23 +40650,6 @@ function untracked(nonReactiveReadsFn) {
|
|
|
40575
40650
|
}
|
|
40576
40651
|
}
|
|
40577
40652
|
|
|
40578
|
-
class ViewContext {
|
|
40579
|
-
view;
|
|
40580
|
-
node;
|
|
40581
|
-
constructor(view, node) {
|
|
40582
|
-
this.view = view;
|
|
40583
|
-
this.node = node;
|
|
40584
|
-
}
|
|
40585
|
-
/**
|
|
40586
|
-
* @internal
|
|
40587
|
-
* @nocollapse
|
|
40588
|
-
*/
|
|
40589
|
-
static __NG_ELEMENT_ID__ = injectViewContext;
|
|
40590
|
-
}
|
|
40591
|
-
function injectViewContext() {
|
|
40592
|
-
return new ViewContext(getLView(), getCurrentTNode());
|
|
40593
|
-
}
|
|
40594
|
-
|
|
40595
40653
|
/**
|
|
40596
40654
|
* Controls whether effects use the legacy `microtaskEffect` by default.
|
|
40597
40655
|
*/
|
|
@@ -40859,7 +40917,7 @@ const ROOT_EFFECT_NODE =
|
|
|
40859
40917
|
...BASE_EFFECT_NODE,
|
|
40860
40918
|
consumerMarkedDirty() {
|
|
40861
40919
|
this.scheduler.schedule(this);
|
|
40862
|
-
this.notifier.notify(
|
|
40920
|
+
this.notifier.notify(12 /* NotificationSource.RootEffect */);
|
|
40863
40921
|
},
|
|
40864
40922
|
destroy() {
|
|
40865
40923
|
consumerDestroy$1(this);
|
|
@@ -40874,7 +40932,7 @@ const VIEW_EFFECT_NODE =
|
|
|
40874
40932
|
consumerMarkedDirty() {
|
|
40875
40933
|
this.view[FLAGS] |= 8192 /* LViewFlags.HasChildViewsToRefresh */;
|
|
40876
40934
|
markAncestorsForTraversal(this.view);
|
|
40877
|
-
this.notifier.notify(
|
|
40935
|
+
this.notifier.notify(13 /* NotificationSource.ViewEffect */);
|
|
40878
40936
|
},
|
|
40879
40937
|
destroy() {
|
|
40880
40938
|
consumerDestroy$1(this);
|
|
@@ -40901,7 +40959,7 @@ function createRootEffect(fn, scheduler, notifier) {
|
|
|
40901
40959
|
node.notifier = notifier;
|
|
40902
40960
|
node.zone = typeof Zone !== 'undefined' ? Zone.current : null;
|
|
40903
40961
|
node.scheduler.schedule(node);
|
|
40904
|
-
node.notifier.notify(
|
|
40962
|
+
node.notifier.notify(12 /* NotificationSource.RootEffect */);
|
|
40905
40963
|
return node;
|
|
40906
40964
|
}
|
|
40907
40965
|
|
|
@@ -40992,10 +41050,10 @@ class AfterRenderEffectSequence extends AfterRenderSequence {
|
|
|
40992
41050
|
* These are initialized to `undefined` but set in the constructor.
|
|
40993
41051
|
*/
|
|
40994
41052
|
nodes = [undefined, undefined, undefined, undefined];
|
|
40995
|
-
constructor(impl, effectHooks, scheduler, destroyRef, snapshot = null) {
|
|
41053
|
+
constructor(impl, effectHooks, view, scheduler, destroyRef, snapshot = null) {
|
|
40996
41054
|
// Note that we also initialize the underlying `AfterRenderSequence` hooks to `undefined` and
|
|
40997
41055
|
// populate them as we create reactive nodes below.
|
|
40998
|
-
super(impl, [undefined, undefined, undefined, undefined], false, destroyRef, snapshot);
|
|
41056
|
+
super(impl, [undefined, undefined, undefined, undefined], view, false, destroyRef, snapshot);
|
|
40999
41057
|
this.scheduler = scheduler;
|
|
41000
41058
|
// Setup a reactive node for each phase.
|
|
41001
41059
|
for (const phase of AFTER_RENDER_PHASES) {
|
|
@@ -41054,7 +41112,8 @@ function afterRenderEffect(callbackOrSpec, options) {
|
|
|
41054
41112
|
if (typeof spec === 'function') {
|
|
41055
41113
|
spec = { mixedReadWrite: callbackOrSpec };
|
|
41056
41114
|
}
|
|
41057
|
-
const
|
|
41115
|
+
const viewContext = injector.get(ViewContext, null, { optional: true });
|
|
41116
|
+
const sequence = new AfterRenderEffectSequence(manager.impl, [spec.earlyRead, spec.write, spec.mixedReadWrite, spec.read], viewContext?.view, scheduler, injector.get(DestroyRef), tracing?.snapshot(null));
|
|
41058
41117
|
manager.impl.register(sequence);
|
|
41059
41118
|
return sequence;
|
|
41060
41119
|
}
|
|
@@ -41136,16 +41195,16 @@ class ResourceImpl extends BaseWritableResource {
|
|
|
41136
41195
|
loaderFn;
|
|
41137
41196
|
defaultValue;
|
|
41138
41197
|
equal;
|
|
41198
|
+
pendingTasks;
|
|
41139
41199
|
/**
|
|
41140
41200
|
* The current state of the resource. Status, value, and error are derived from this.
|
|
41141
41201
|
*/
|
|
41142
41202
|
state;
|
|
41143
41203
|
/**
|
|
41144
|
-
*
|
|
41145
|
-
*
|
|
41204
|
+
* Combines the current request with a reload counter which allows the resource to be reloaded on
|
|
41205
|
+
* imperative command.
|
|
41146
41206
|
*/
|
|
41147
|
-
|
|
41148
|
-
pendingTasks;
|
|
41207
|
+
extRequest;
|
|
41149
41208
|
effectRef;
|
|
41150
41209
|
pendingController;
|
|
41151
41210
|
resolvePendingTask = undefined;
|
|
@@ -41155,56 +41214,55 @@ class ResourceImpl extends BaseWritableResource {
|
|
|
41155
41214
|
// Feed a computed signal for the value to `BaseWritableResource`, which will upgrade it to a
|
|
41156
41215
|
// `WritableSignal` that delegates to `ResourceImpl.set`.
|
|
41157
41216
|
computed(() => {
|
|
41158
|
-
const
|
|
41159
|
-
return
|
|
41217
|
+
const streamValue = this.state().stream?.();
|
|
41218
|
+
return streamValue && isResolved(streamValue) ? streamValue.value : this.defaultValue;
|
|
41160
41219
|
}, { equal }));
|
|
41161
41220
|
this.loaderFn = loaderFn;
|
|
41162
41221
|
this.defaultValue = defaultValue;
|
|
41163
41222
|
this.equal = equal;
|
|
41164
|
-
this.pendingTasks = injector.get(PendingTasks);
|
|
41165
41223
|
// Extend `request()` to include a writable reload signal.
|
|
41166
|
-
this.
|
|
41167
|
-
|
|
41168
|
-
reload:
|
|
41169
|
-
})
|
|
41224
|
+
this.extRequest = linkedSignal({
|
|
41225
|
+
source: request,
|
|
41226
|
+
computation: (request) => ({ request, reload: 0 }),
|
|
41227
|
+
});
|
|
41170
41228
|
// The main resource state is managed in a `linkedSignal`, which allows the resource to change
|
|
41171
41229
|
// state instantaneously when the request signal changes.
|
|
41172
41230
|
this.state = linkedSignal({
|
|
41173
|
-
//
|
|
41174
|
-
|
|
41175
|
-
//
|
|
41176
|
-
|
|
41177
|
-
|
|
41178
|
-
|
|
41179
|
-
|
|
41180
|
-
|
|
41231
|
+
// Whenever the request changes,
|
|
41232
|
+
source: this.extRequest,
|
|
41233
|
+
// Compute the state of the resource given a change in status.
|
|
41234
|
+
computation: (extRequest, previous) => {
|
|
41235
|
+
const status = extRequest.request === undefined ? ResourceStatus.Idle : ResourceStatus.Loading;
|
|
41236
|
+
if (!previous) {
|
|
41237
|
+
return {
|
|
41238
|
+
extRequest,
|
|
41239
|
+
status,
|
|
41240
|
+
previousStatus: ResourceStatus.Idle,
|
|
41241
|
+
stream: undefined,
|
|
41242
|
+
};
|
|
41243
|
+
}
|
|
41244
|
+
else {
|
|
41245
|
+
return {
|
|
41246
|
+
extRequest,
|
|
41247
|
+
status,
|
|
41248
|
+
previousStatus: projectStatusOfState(previous.value),
|
|
41249
|
+
// If the request hasn't changed, keep the previous stream.
|
|
41250
|
+
stream: previous.value.extRequest.request === extRequest.request
|
|
41251
|
+
? previous.value.stream
|
|
41252
|
+
: undefined,
|
|
41253
|
+
};
|
|
41181
41254
|
}
|
|
41182
|
-
return reload() === 0 ? ResourceStatus.Loading : ResourceStatus.Reloading;
|
|
41183
41255
|
},
|
|
41184
|
-
// Compute the state of the resource given a change in status.
|
|
41185
|
-
computation: (status, previous) => ({
|
|
41186
|
-
status,
|
|
41187
|
-
// When the state of the resource changes due to the request, remember the previous status
|
|
41188
|
-
// for the loader to consider.
|
|
41189
|
-
previousStatus: computeStatusOfState(previous?.value),
|
|
41190
|
-
// In `Reloading` state, we keep the previous value if there is one, since the identity of
|
|
41191
|
-
// the request hasn't changed. Otherwise, we switch back to the default value.
|
|
41192
|
-
stream: previous && status === ResourceStatus.Reloading ? previous.value.stream : undefined,
|
|
41193
|
-
}),
|
|
41194
41256
|
});
|
|
41195
41257
|
this.effectRef = effect(this.loadEffect.bind(this), {
|
|
41196
41258
|
injector,
|
|
41197
41259
|
manualCleanup: true,
|
|
41198
41260
|
});
|
|
41261
|
+
this.pendingTasks = injector.get(PendingTasks);
|
|
41199
41262
|
// Cancel any pending request when the resource itself is destroyed.
|
|
41200
41263
|
injector.get(DestroyRef).onDestroy(() => this.destroy());
|
|
41201
41264
|
}
|
|
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
|
-
});
|
|
41265
|
+
status = computed(() => projectStatusOfState(this.state()));
|
|
41208
41266
|
error = computed(() => {
|
|
41209
41267
|
const stream = this.state().stream?.();
|
|
41210
41268
|
return stream && !isResolved(stream) ? stream.error : undefined;
|
|
@@ -41217,12 +41275,14 @@ class ResourceImpl extends BaseWritableResource {
|
|
|
41217
41275
|
return;
|
|
41218
41276
|
}
|
|
41219
41277
|
const current = untracked(this.value);
|
|
41220
|
-
|
|
41278
|
+
const state = untracked(this.state);
|
|
41279
|
+
if (state.status === ResourceStatus.Local &&
|
|
41221
41280
|
(this.equal ? this.equal(current, value) : current === value)) {
|
|
41222
41281
|
return;
|
|
41223
41282
|
}
|
|
41224
41283
|
// Enter Local state with the user-defined value.
|
|
41225
41284
|
this.state.set({
|
|
41285
|
+
extRequest: state.extRequest,
|
|
41226
41286
|
status: ResourceStatus.Local,
|
|
41227
41287
|
previousStatus: ResourceStatus.Local,
|
|
41228
41288
|
stream: signal({ value }),
|
|
@@ -41233,14 +41293,12 @@ class ResourceImpl extends BaseWritableResource {
|
|
|
41233
41293
|
}
|
|
41234
41294
|
reload() {
|
|
41235
41295
|
// We don't want to restart in-progress loads.
|
|
41236
|
-
const status = untracked(this.
|
|
41237
|
-
if (status === ResourceStatus.Idle ||
|
|
41238
|
-
status === ResourceStatus.Loading ||
|
|
41239
|
-
status === ResourceStatus.Reloading) {
|
|
41296
|
+
const { status } = untracked(this.state);
|
|
41297
|
+
if (status === ResourceStatus.Idle || status === ResourceStatus.Loading) {
|
|
41240
41298
|
return false;
|
|
41241
41299
|
}
|
|
41242
|
-
// Increment the reload
|
|
41243
|
-
|
|
41300
|
+
// Increment the request reload to trigger the `state` linked signal to switch us to `Reload`
|
|
41301
|
+
this.extRequest.update(({ request, reload }) => ({ request, reload: reload + 1 }));
|
|
41244
41302
|
return true;
|
|
41245
41303
|
}
|
|
41246
41304
|
destroy() {
|
|
@@ -41249,28 +41307,23 @@ class ResourceImpl extends BaseWritableResource {
|
|
|
41249
41307
|
this.abortInProgressLoad();
|
|
41250
41308
|
// Destroyed resources enter Idle state.
|
|
41251
41309
|
this.state.set({
|
|
41310
|
+
extRequest: { request: undefined, reload: 0 },
|
|
41252
41311
|
status: ResourceStatus.Idle,
|
|
41253
41312
|
previousStatus: ResourceStatus.Idle,
|
|
41254
41313
|
stream: undefined,
|
|
41255
41314
|
});
|
|
41256
41315
|
}
|
|
41257
41316
|
async loadEffect() {
|
|
41317
|
+
const extRequest = this.extRequest();
|
|
41258
41318
|
// Capture the previous status before any state transitions. Note that this is `untracked` since
|
|
41259
41319
|
// we do not want the effect to depend on the state of the resource, only on the request.
|
|
41260
41320
|
const { status: currentStatus, previousStatus } = untracked(this.state);
|
|
41261
|
-
|
|
41262
|
-
// Subscribe side-effectfully to `reloadCounter`, although we don't actually care about its
|
|
41263
|
-
// value. This is used to rerun the effect when `reload()` is triggered.
|
|
41264
|
-
reloadCounter();
|
|
41265
|
-
if (request === undefined) {
|
|
41321
|
+
if (extRequest.request === undefined) {
|
|
41266
41322
|
// Nothing to load (and we should already be in a non-loading state).
|
|
41267
41323
|
return;
|
|
41268
41324
|
}
|
|
41269
|
-
else if (currentStatus !== ResourceStatus.Loading
|
|
41270
|
-
|
|
41271
|
-
// We might've transitioned into a loading state, but has since been overwritten (likely via
|
|
41272
|
-
// `.set`).
|
|
41273
|
-
// In this case, the resource has nothing to do.
|
|
41325
|
+
else if (currentStatus !== ResourceStatus.Loading) {
|
|
41326
|
+
// We're not in a loading or reloading state, so this loading request is stale.
|
|
41274
41327
|
return;
|
|
41275
41328
|
}
|
|
41276
41329
|
// Cancel any previous loading attempts.
|
|
@@ -41293,27 +41346,31 @@ class ResourceImpl extends BaseWritableResource {
|
|
|
41293
41346
|
// which side of the `await` they are.
|
|
41294
41347
|
const stream = await untracked(() => {
|
|
41295
41348
|
return this.loaderFn({
|
|
41296
|
-
request: request,
|
|
41349
|
+
request: extRequest.request,
|
|
41297
41350
|
abortSignal,
|
|
41298
41351
|
previous: {
|
|
41299
41352
|
status: previousStatus,
|
|
41300
41353
|
},
|
|
41301
41354
|
});
|
|
41302
41355
|
});
|
|
41303
|
-
|
|
41356
|
+
// If this request has been aborted, or the current request no longer
|
|
41357
|
+
// matches this load, then we should ignore this resolution.
|
|
41358
|
+
if (abortSignal.aborted || untracked(this.extRequest) !== extRequest) {
|
|
41304
41359
|
return;
|
|
41305
41360
|
}
|
|
41306
41361
|
this.state.set({
|
|
41362
|
+
extRequest,
|
|
41307
41363
|
status: ResourceStatus.Resolved,
|
|
41308
41364
|
previousStatus: ResourceStatus.Resolved,
|
|
41309
41365
|
stream,
|
|
41310
41366
|
});
|
|
41311
41367
|
}
|
|
41312
41368
|
catch (err) {
|
|
41313
|
-
if (abortSignal.aborted) {
|
|
41369
|
+
if (abortSignal.aborted || untracked(this.extRequest) !== extRequest) {
|
|
41314
41370
|
return;
|
|
41315
41371
|
}
|
|
41316
41372
|
this.state.set({
|
|
41373
|
+
extRequest,
|
|
41317
41374
|
status: ResourceStatus.Resolved,
|
|
41318
41375
|
previousStatus: ResourceStatus.Error,
|
|
41319
41376
|
stream: signal({ error: err }),
|
|
@@ -41355,10 +41412,13 @@ function getLoader(options) {
|
|
|
41355
41412
|
function isStreamingResourceOptions(options) {
|
|
41356
41413
|
return !!options.stream;
|
|
41357
41414
|
}
|
|
41358
|
-
|
|
41359
|
-
|
|
41360
|
-
|
|
41361
|
-
|
|
41415
|
+
/**
|
|
41416
|
+
* Project from a state with `ResourceInternalStatus` to the user-facing `ResourceStatus`
|
|
41417
|
+
*/
|
|
41418
|
+
function projectStatusOfState(state) {
|
|
41419
|
+
switch (state.status) {
|
|
41420
|
+
case ResourceStatus.Loading:
|
|
41421
|
+
return state.extRequest.reload === 0 ? ResourceStatus.Loading : ResourceStatus.Reloading;
|
|
41362
41422
|
case ResourceStatus.Resolved:
|
|
41363
41423
|
return isResolved(untracked(state.stream)) ? ResourceStatus.Resolved : ResourceStatus.Error;
|
|
41364
41424
|
default:
|