UNPKG

@angular/core

Version:

Angular - the core framework

775 lines 118 kB
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import { setActiveConsumer } from '@angular/core/primitives/signals'; import { CachedInjectorService } from '../cached_injector_service'; import { EnvironmentInjector, InjectionToken } from '../di'; import { internalImportProvidersFrom } from '../di/provider_collection'; import { RuntimeError } from '../errors'; import { findMatchingDehydratedView } from '../hydration/views'; import { populateDehydratedViewsInLContainer } from '../linker/view_container_ref'; import { PendingTasks } from '../pending_tasks'; import { assertLContainer, assertTNodeForLView } from '../render3/assert'; import { bindingUpdated } from '../render3/bindings'; import { ChainedInjector } from '../render3/chained_injector'; import { getComponentDef, getDirectiveDef, getPipeDef } from '../render3/definition'; import { getTemplateLocationDetails } from '../render3/instructions/element_validation'; import { markViewDirty } from '../render3/instructions/mark_view_dirty'; import { handleError } from '../render3/instructions/shared'; import { declareTemplate } from '../render3/instructions/template'; import { isDestroyed } from '../render3/interfaces/type_checks'; import { HEADER_OFFSET, INJECTOR, PARENT, TVIEW } from '../render3/interfaces/view'; import { getCurrentTNode, getLView, getSelectedTNode, getTView, nextBindingIndex, } from '../render3/state'; import { isPlatformBrowser } from '../render3/util/misc_utils'; import { getConstant, getTNode, removeLViewOnDestroy, storeLViewOnDestroy, } from '../render3/util/view_utils'; import { addLViewToLContainer, createAndRenderEmbeddedLView, removeLViewFromLContainer, shouldAddViewToDom, } from '../render3/view_manipulation'; import { assertDefined, throwError } from '../util/assert'; import { performanceMarkFeature } from '../util/performance'; import { invokeAllTriggerCleanupFns, invokeTriggerCleanupFns, storeTriggerCleanupFn, } from './cleanup'; import { onHover, onInteraction, onViewport, registerDomTrigger } from './dom_triggers'; import { onIdle } from './idle_scheduler'; import { DEFER_BLOCK_STATE, DeferBlockBehavior, DeferBlockInternalState, DeferBlockState, DeferDependenciesLoadingState, LOADING_AFTER_CLEANUP_FN, NEXT_DEFER_BLOCK_STATE, STATE_IS_FROZEN_UNTIL, } from './interfaces'; import { onTimer, scheduleTimerTrigger } from './timer_scheduler'; import { addDepsToRegistry, assertDeferredDependenciesLoaded, getLDeferBlockDetails, getLoadingBlockAfter, getMinimumDurationForState, getPrimaryBlockTNode, getTDeferBlockDetails, getTemplateIndexForState, setLDeferBlockDetails, setTDeferBlockDetails, } from './utils'; /** * **INTERNAL**, avoid referencing it in application code. * * * Injector token that allows to provide `DeferBlockDependencyInterceptor` class * implementation. * * This token is only injected in devMode */ export const DEFER_BLOCK_DEPENDENCY_INTERCEPTOR = new InjectionToken('DEFER_BLOCK_DEPENDENCY_INTERCEPTOR'); /** * **INTERNAL**, token used for configuring defer block behavior. */ export const DEFER_BLOCK_CONFIG = new InjectionToken(ngDevMode ? 'DEFER_BLOCK_CONFIG' : ''); /** * Returns whether defer blocks should be triggered. * * Currently, defer blocks are not triggered on the server, * only placeholder content is rendered (if provided). */ function shouldTriggerDeferBlock(injector) { const config = injector.get(DEFER_BLOCK_CONFIG, null, { optional: true }); if (config?.behavior === DeferBlockBehavior.Manual) { return false; } return isPlatformBrowser(injector); } /** * Reference to the timer-based scheduler implementation of defer block state * rendering method. It's used to make timer-based scheduling tree-shakable. * If `minimum` or `after` parameters are used, compiler generates an extra * argument for the `ɵɵdefer` instruction, which references a timer-based * implementation. */ let applyDeferBlockStateWithSchedulingImpl = null; /** * Enables timer-related scheduling if `after` or `minimum` parameters are setup * on the `@loading` or `@placeholder` blocks. */ export function ɵɵdeferEnableTimerScheduling(tView, tDetails, placeholderConfigIndex, loadingConfigIndex) { const tViewConsts = tView.consts; if (placeholderConfigIndex != null) { tDetails.placeholderBlockConfig = getConstant(tViewConsts, placeholderConfigIndex); } if (loadingConfigIndex != null) { tDetails.loadingBlockConfig = getConstant(tViewConsts, loadingConfigIndex); } // Enable implementation that supports timer-based scheduling. if (applyDeferBlockStateWithSchedulingImpl === null) { applyDeferBlockStateWithSchedulingImpl = applyDeferBlockStateWithScheduling; } } /** * Creates runtime data structures for defer blocks. * * @param index Index of the `defer` instruction. * @param primaryTmplIndex Index of the template with the primary block content. * @param dependencyResolverFn Function that contains dependencies for this defer block. * @param loadingTmplIndex Index of the template with the loading block content. * @param placeholderTmplIndex Index of the template with the placeholder block content. * @param errorTmplIndex Index of the template with the error block content. * @param loadingConfigIndex Index in the constants array of the configuration of the loading. * block. * @param placeholderConfigIndex Index in the constants array of the configuration of the * placeholder block. * @param enableTimerScheduling Function that enables timer-related scheduling if `after` * or `minimum` parameters are setup on the `@loading` or `@placeholder` blocks. * * @codeGenApi */ export function ɵɵdefer(index, primaryTmplIndex, dependencyResolverFn, loadingTmplIndex, placeholderTmplIndex, errorTmplIndex, loadingConfigIndex, placeholderConfigIndex, enableTimerScheduling) { const lView = getLView(); const tView = getTView(); const adjustedIndex = index + HEADER_OFFSET; const tNode = declareTemplate(lView, tView, index, null, 0, 0); if (tView.firstCreatePass) { performanceMarkFeature('NgDefer'); const tDetails = { primaryTmplIndex, loadingTmplIndex: loadingTmplIndex ?? null, placeholderTmplIndex: placeholderTmplIndex ?? null, errorTmplIndex: errorTmplIndex ?? null, placeholderBlockConfig: null, loadingBlockConfig: null, dependencyResolverFn: dependencyResolverFn ?? null, loadingState: DeferDependenciesLoadingState.NOT_STARTED, loadingPromise: null, providers: null, }; enableTimerScheduling?.(tView, tDetails, placeholderConfigIndex, loadingConfigIndex); setTDeferBlockDetails(tView, adjustedIndex, tDetails); } const lContainer = lView[adjustedIndex]; // If hydration is enabled, looks up dehydrated views in the DOM // using hydration annotation info and stores those views on LContainer. // In client-only mode, this function is a noop. populateDehydratedViewsInLContainer(lContainer, tNode, lView); // Init instance-specific defer details and store it. const lDetails = [ null, // NEXT_DEFER_BLOCK_STATE DeferBlockInternalState.Initial, // DEFER_BLOCK_STATE null, // STATE_IS_FROZEN_UNTIL null, // LOADING_AFTER_CLEANUP_FN null, // TRIGGER_CLEANUP_FNS null, // PREFETCH_TRIGGER_CLEANUP_FNS ]; setLDeferBlockDetails(lView, adjustedIndex, lDetails); const cleanupTriggersFn = () => invokeAllTriggerCleanupFns(lDetails); // When defer block is triggered - unsubscribe from LView destroy cleanup. storeTriggerCleanupFn(0 /* TriggerType.Regular */, lDetails, () => removeLViewOnDestroy(lView, cleanupTriggersFn)); storeLViewOnDestroy(lView, cleanupTriggersFn); } /** * Loads defer block dependencies when a trigger value becomes truthy. * @codeGenApi */ export function ɵɵdeferWhen(rawValue) { const lView = getLView(); const bindingIndex = nextBindingIndex(); if (bindingUpdated(lView, bindingIndex, rawValue)) { const prevConsumer = setActiveConsumer(null); try { const value = Boolean(rawValue); // handle truthy or falsy values const tNode = getSelectedTNode(); const lDetails = getLDeferBlockDetails(lView, tNode); const renderedState = lDetails[DEFER_BLOCK_STATE]; if (value === false && renderedState === DeferBlockInternalState.Initial) { // If nothing is rendered yet, render a placeholder (if defined). renderPlaceholder(lView, tNode); } else if (value === true && (renderedState === DeferBlockInternalState.Initial || renderedState === DeferBlockState.Placeholder)) { // The `when` condition has changed to `true`, trigger defer block loading // if the block is either in initial (nothing is rendered) or a placeholder // state. triggerDeferBlock(lView, tNode); } } finally { setActiveConsumer(prevConsumer); } } } /** * Prefetches the deferred content when a value becomes truthy. * @codeGenApi */ export function ɵɵdeferPrefetchWhen(rawValue) { const lView = getLView(); const bindingIndex = nextBindingIndex(); if (bindingUpdated(lView, bindingIndex, rawValue)) { const prevConsumer = setActiveConsumer(null); try { const value = Boolean(rawValue); // handle truthy or falsy values const tView = lView[TVIEW]; const tNode = getSelectedTNode(); const tDetails = getTDeferBlockDetails(tView, tNode); if (value === true && tDetails.loadingState === DeferDependenciesLoadingState.NOT_STARTED) { // If loading has not been started yet, trigger it now. triggerPrefetching(tDetails, lView, tNode); } } finally { setActiveConsumer(prevConsumer); } } } /** * Sets up logic to handle the `on idle` deferred trigger. * @codeGenApi */ export function ɵɵdeferOnIdle() { scheduleDelayedTrigger(onIdle); } /** * Sets up logic to handle the `prefetch on idle` deferred trigger. * @codeGenApi */ export function ɵɵdeferPrefetchOnIdle() { scheduleDelayedPrefetching(onIdle); } /** * Sets up logic to handle the `on immediate` deferred trigger. * @codeGenApi */ export function ɵɵdeferOnImmediate() { const lView = getLView(); const tNode = getCurrentTNode(); const tView = lView[TVIEW]; const injector = lView[INJECTOR]; const tDetails = getTDeferBlockDetails(tView, tNode); // Render placeholder block only if loading template is not present and we're on // the client to avoid content flickering, since it would be immediately replaced // by the loading block. if (!shouldTriggerDeferBlock(injector) || tDetails.loadingTmplIndex === null) { renderPlaceholder(lView, tNode); } triggerDeferBlock(lView, tNode); } /** * Sets up logic to handle the `prefetch on immediate` deferred trigger. * @codeGenApi */ export function ɵɵdeferPrefetchOnImmediate() { const lView = getLView(); const tNode = getCurrentTNode(); const tView = lView[TVIEW]; const tDetails = getTDeferBlockDetails(tView, tNode); if (tDetails.loadingState === DeferDependenciesLoadingState.NOT_STARTED) { triggerResourceLoading(tDetails, lView, tNode); } } /** * Creates runtime data structures for the `on timer` deferred trigger. * @param delay Amount of time to wait before loading the content. * @codeGenApi */ export function ɵɵdeferOnTimer(delay) { scheduleDelayedTrigger(onTimer(delay)); } /** * Creates runtime data structures for the `prefetch on timer` deferred trigger. * @param delay Amount of time to wait before prefetching the content. * @codeGenApi */ export function ɵɵdeferPrefetchOnTimer(delay) { scheduleDelayedPrefetching(onTimer(delay)); } /** * Creates runtime data structures for the `on hover` deferred trigger. * @param triggerIndex Index at which to find the trigger element. * @param walkUpTimes Number of times to walk up/down the tree hierarchy to find the trigger. * @codeGenApi */ export function ɵɵdeferOnHover(triggerIndex, walkUpTimes) { const lView = getLView(); const tNode = getCurrentTNode(); renderPlaceholder(lView, tNode); registerDomTrigger(lView, tNode, triggerIndex, walkUpTimes, onHover, () => triggerDeferBlock(lView, tNode), 0 /* TriggerType.Regular */); } /** * Creates runtime data structures for the `prefetch on hover` deferred trigger. * @param triggerIndex Index at which to find the trigger element. * @param walkUpTimes Number of times to walk up/down the tree hierarchy to find the trigger. * @codeGenApi */ export function ɵɵdeferPrefetchOnHover(triggerIndex, walkUpTimes) { const lView = getLView(); const tNode = getCurrentTNode(); const tView = lView[TVIEW]; const tDetails = getTDeferBlockDetails(tView, tNode); if (tDetails.loadingState === DeferDependenciesLoadingState.NOT_STARTED) { registerDomTrigger(lView, tNode, triggerIndex, walkUpTimes, onHover, () => triggerPrefetching(tDetails, lView, tNode), 1 /* TriggerType.Prefetch */); } } /** * Creates runtime data structures for the `on interaction` deferred trigger. * @param triggerIndex Index at which to find the trigger element. * @param walkUpTimes Number of times to walk up/down the tree hierarchy to find the trigger. * @codeGenApi */ export function ɵɵdeferOnInteraction(triggerIndex, walkUpTimes) { const lView = getLView(); const tNode = getCurrentTNode(); renderPlaceholder(lView, tNode); registerDomTrigger(lView, tNode, triggerIndex, walkUpTimes, onInteraction, () => triggerDeferBlock(lView, tNode), 0 /* TriggerType.Regular */); } /** * Creates runtime data structures for the `prefetch on interaction` deferred trigger. * @param triggerIndex Index at which to find the trigger element. * @param walkUpTimes Number of times to walk up/down the tree hierarchy to find the trigger. * @codeGenApi */ export function ɵɵdeferPrefetchOnInteraction(triggerIndex, walkUpTimes) { const lView = getLView(); const tNode = getCurrentTNode(); const tView = lView[TVIEW]; const tDetails = getTDeferBlockDetails(tView, tNode); if (tDetails.loadingState === DeferDependenciesLoadingState.NOT_STARTED) { registerDomTrigger(lView, tNode, triggerIndex, walkUpTimes, onInteraction, () => triggerPrefetching(tDetails, lView, tNode), 1 /* TriggerType.Prefetch */); } } /** * Creates runtime data structures for the `on viewport` deferred trigger. * @param triggerIndex Index at which to find the trigger element. * @param walkUpTimes Number of times to walk up/down the tree hierarchy to find the trigger. * @codeGenApi */ export function ɵɵdeferOnViewport(triggerIndex, walkUpTimes) { const lView = getLView(); const tNode = getCurrentTNode(); renderPlaceholder(lView, tNode); registerDomTrigger(lView, tNode, triggerIndex, walkUpTimes, onViewport, () => triggerDeferBlock(lView, tNode), 0 /* TriggerType.Regular */); } /** * Creates runtime data structures for the `prefetch on viewport` deferred trigger. * @param triggerIndex Index at which to find the trigger element. * @param walkUpTimes Number of times to walk up/down the tree hierarchy to find the trigger. * @codeGenApi */ export function ɵɵdeferPrefetchOnViewport(triggerIndex, walkUpTimes) { const lView = getLView(); const tNode = getCurrentTNode(); const tView = lView[TVIEW]; const tDetails = getTDeferBlockDetails(tView, tNode); if (tDetails.loadingState === DeferDependenciesLoadingState.NOT_STARTED) { registerDomTrigger(lView, tNode, triggerIndex, walkUpTimes, onViewport, () => triggerPrefetching(tDetails, lView, tNode), 1 /* TriggerType.Prefetch */); } } /********** Helper functions **********/ /** * Schedules triggering of a defer block for `on idle` and `on timer` conditions. */ function scheduleDelayedTrigger(scheduleFn) { const lView = getLView(); const tNode = getCurrentTNode(); renderPlaceholder(lView, tNode); // Only trigger the scheduled trigger on the browser // since we don't want to delay the server response. if (isPlatformBrowser(lView[INJECTOR])) { const cleanupFn = scheduleFn(() => triggerDeferBlock(lView, tNode), lView); const lDetails = getLDeferBlockDetails(lView, tNode); storeTriggerCleanupFn(0 /* TriggerType.Regular */, lDetails, cleanupFn); } } /** * Schedules prefetching for `on idle` and `on timer` triggers. * * @param scheduleFn A function that does the scheduling. */ function scheduleDelayedPrefetching(scheduleFn) { const lView = getLView(); // Only trigger the scheduled trigger on the browser // since we don't want to delay the server response. if (isPlatformBrowser(lView[INJECTOR])) { const tNode = getCurrentTNode(); const tView = lView[TVIEW]; const tDetails = getTDeferBlockDetails(tView, tNode); if (tDetails.loadingState === DeferDependenciesLoadingState.NOT_STARTED) { const lDetails = getLDeferBlockDetails(lView, tNode); const prefetch = () => triggerPrefetching(tDetails, lView, tNode); const cleanupFn = scheduleFn(prefetch, lView); storeTriggerCleanupFn(1 /* TriggerType.Prefetch */, lDetails, cleanupFn); } } } /** * Transitions a defer block to the new state. Updates the necessary * data structures and renders corresponding block. * * @param newState New state that should be applied to the defer block. * @param tNode TNode that represents a defer block. * @param lContainer Represents an instance of a defer block. * @param skipTimerScheduling Indicates that `@loading` and `@placeholder` block * should be rendered immediately, even if they have `after` or `minimum` config * options setup. This flag to needed for testing APIs to transition defer block * between states via `DeferFixture.render` method. */ export function renderDeferBlockState(newState, tNode, lContainer, skipTimerScheduling = false) { const hostLView = lContainer[PARENT]; const hostTView = hostLView[TVIEW]; // Check if this view is not destroyed. Since the loading process was async, // the view might end up being destroyed by the time rendering happens. if (isDestroyed(hostLView)) return; // Make sure this TNode belongs to TView that represents host LView. ngDevMode && assertTNodeForLView(tNode, hostLView); const lDetails = getLDeferBlockDetails(hostLView, tNode); ngDevMode && assertDefined(lDetails, 'Expected a defer block state defined'); const currentState = lDetails[DEFER_BLOCK_STATE]; if (isValidStateChange(currentState, newState) && isValidStateChange(lDetails[NEXT_DEFER_BLOCK_STATE] ?? -1, newState)) { const injector = hostLView[INJECTOR]; const tDetails = getTDeferBlockDetails(hostTView, tNode); // Skips scheduling on the server since it can delay the server response. const needsScheduling = !skipTimerScheduling && isPlatformBrowser(injector) && (getLoadingBlockAfter(tDetails) !== null || getMinimumDurationForState(tDetails, DeferBlockState.Loading) !== null || getMinimumDurationForState(tDetails, DeferBlockState.Placeholder)); if (ngDevMode && needsScheduling) { assertDefined(applyDeferBlockStateWithSchedulingImpl, 'Expected scheduling function to be defined'); } const applyStateFn = needsScheduling ? applyDeferBlockStateWithSchedulingImpl : applyDeferBlockState; try { applyStateFn(newState, lDetails, lContainer, tNode, hostLView); } catch (error) { handleError(hostLView, error); } } } /** * Checks whether there is a cached injector associated with a given defer block * declaration and returns if it exists. If there is no cached injector present - * creates a new injector and stores in the cache. */ function getOrCreateEnvironmentInjector(parentInjector, tDetails, providers) { return parentInjector .get(CachedInjectorService) .getOrCreateInjector(tDetails, parentInjector, providers, ngDevMode ? 'DeferBlock Injector' : ''); } /** * Creates a new injector, which contains providers collected from dependencies (NgModules) of * defer-loaded components. This function detects different types of parent injectors and creates * a new injector based on that. */ function createDeferBlockInjector(parentInjector, tDetails, providers) { // Check if the parent injector is an instance of a `ChainedInjector`. // // In this case, we retain the shape of the injector and use a newly created // `EnvironmentInjector` as a parent in the `ChainedInjector`. That is needed to // make sure that the primary injector gets consulted first (since it's typically // a NodeInjector) and `EnvironmentInjector` tree is consulted after that. if (parentInjector instanceof ChainedInjector) { const origInjector = parentInjector.injector; // Guaranteed to be an environment injector const parentEnvInjector = parentInjector.parentInjector; const envInjector = getOrCreateEnvironmentInjector(parentEnvInjector, tDetails, providers); return new ChainedInjector(origInjector, envInjector); } const parentEnvInjector = parentInjector.get(EnvironmentInjector); // If the `parentInjector` is *not* an `EnvironmentInjector` - we need to create // a new `ChainedInjector` with the following setup: // // - the provided `parentInjector` becomes a primary injector // - an existing (real) `EnvironmentInjector` becomes a parent injector for // a newly-created one, which contains extra providers // // So the final order in which injectors would be consulted in this case would look like this: // // 1. Provided `parentInjector` // 2. Newly-created `EnvironmentInjector` with extra providers // 3. `EnvironmentInjector` from the `parentInjector` if (parentEnvInjector !== parentInjector) { const envInjector = getOrCreateEnvironmentInjector(parentEnvInjector, tDetails, providers); return new ChainedInjector(parentInjector, envInjector); } // The `parentInjector` is an instance of an `EnvironmentInjector`. // No need for special handling, we can use `parentInjector` as a // parent injector directly. return getOrCreateEnvironmentInjector(parentInjector, tDetails, providers); } /** * Applies changes to the DOM to reflect a given state. */ function applyDeferBlockState(newState, lDetails, lContainer, tNode, hostLView) { const stateTmplIndex = getTemplateIndexForState(newState, hostLView, tNode); if (stateTmplIndex !== null) { lDetails[DEFER_BLOCK_STATE] = newState; const hostTView = hostLView[TVIEW]; const adjustedIndex = stateTmplIndex + HEADER_OFFSET; const activeBlockTNode = getTNode(hostTView, adjustedIndex); // There is only 1 view that can be present in an LContainer that // represents a defer block, so always refer to the first one. const viewIndex = 0; removeLViewFromLContainer(lContainer, viewIndex); let injector; if (newState === DeferBlockState.Complete) { // When we render a defer block in completed state, there might be // newly loaded standalone components used within the block, which may // import NgModules with providers. In order to make those providers // available for components declared in that NgModule, we create an instance // of an environment injector to host those providers and pass this injector // to the logic that creates a view. const tDetails = getTDeferBlockDetails(hostTView, tNode); const providers = tDetails.providers; if (providers && providers.length > 0) { injector = createDeferBlockInjector(hostLView[INJECTOR], tDetails, providers); } } const dehydratedView = findMatchingDehydratedView(lContainer, activeBlockTNode.tView.ssrId); const embeddedLView = createAndRenderEmbeddedLView(hostLView, activeBlockTNode, null, { dehydratedView, injector, }); addLViewToLContainer(lContainer, embeddedLView, viewIndex, shouldAddViewToDom(activeBlockTNode, dehydratedView)); markViewDirty(embeddedLView, 2 /* NotificationSource.DeferBlockStateUpdate */); } } /** * Extends the `applyDeferBlockState` with timer-based scheduling. * This function becomes available on a page if there are defer blocks * that use `after` or `minimum` parameters in the `@loading` or * `@placeholder` blocks. */ function applyDeferBlockStateWithScheduling(newState, lDetails, lContainer, tNode, hostLView) { const now = Date.now(); const hostTView = hostLView[TVIEW]; const tDetails = getTDeferBlockDetails(hostTView, tNode); if (lDetails[STATE_IS_FROZEN_UNTIL] === null || lDetails[STATE_IS_FROZEN_UNTIL] <= now) { lDetails[STATE_IS_FROZEN_UNTIL] = null; const loadingAfter = getLoadingBlockAfter(tDetails); const inLoadingAfterPhase = lDetails[LOADING_AFTER_CLEANUP_FN] !== null; if (newState === DeferBlockState.Loading && loadingAfter !== null && !inLoadingAfterPhase) { // Trying to render loading, but it has an `after` config, // so schedule an update action after a timeout. lDetails[NEXT_DEFER_BLOCK_STATE] = newState; const cleanupFn = scheduleDeferBlockUpdate(loadingAfter, lDetails, tNode, lContainer, hostLView); lDetails[LOADING_AFTER_CLEANUP_FN] = cleanupFn; } else { // If we transition to a complete or an error state and there is a pending // operation to render loading after a timeout - invoke a cleanup operation, // which stops the timer. if (newState > DeferBlockState.Loading && inLoadingAfterPhase) { lDetails[LOADING_AFTER_CLEANUP_FN](); lDetails[LOADING_AFTER_CLEANUP_FN] = null; lDetails[NEXT_DEFER_BLOCK_STATE] = null; } applyDeferBlockState(newState, lDetails, lContainer, tNode, hostLView); const duration = getMinimumDurationForState(tDetails, newState); if (duration !== null) { lDetails[STATE_IS_FROZEN_UNTIL] = now + duration; scheduleDeferBlockUpdate(duration, lDetails, tNode, lContainer, hostLView); } } } else { // We are still rendering the previous state. // Update the `NEXT_DEFER_BLOCK_STATE`, which would be // picked up once it's time to transition to the next state. lDetails[NEXT_DEFER_BLOCK_STATE] = newState; } } /** * Schedules an update operation after a specified timeout. */ function scheduleDeferBlockUpdate(timeout, lDetails, tNode, lContainer, hostLView) { const callback = () => { const nextState = lDetails[NEXT_DEFER_BLOCK_STATE]; lDetails[STATE_IS_FROZEN_UNTIL] = null; lDetails[NEXT_DEFER_BLOCK_STATE] = null; if (nextState !== null) { renderDeferBlockState(nextState, tNode, lContainer); } }; return scheduleTimerTrigger(timeout, callback, hostLView); } /** * Checks whether we can transition to the next state. * * We transition to the next state if the previous state was represented * with a number that is less than the next state. For example, if the current * state is "loading" (represented as `1`), we should not show a placeholder * (represented as `0`), but we can show a completed state (represented as `2`) * or an error state (represented as `3`). */ function isValidStateChange(currentState, newState) { return currentState < newState; } /** * Trigger prefetching of dependencies for a defer block. * * @param tDetails Static information about this defer block. * @param lView LView of a host view. */ export function triggerPrefetching(tDetails, lView, tNode) { if (lView[INJECTOR] && shouldTriggerDeferBlock(lView[INJECTOR])) { triggerResourceLoading(tDetails, lView, tNode); } } /** * Trigger loading of defer block dependencies if the process hasn't started yet. * * @param tDetails Static information about this defer block. * @param lView LView of a host view. */ export function triggerResourceLoading(tDetails, lView, tNode) { const injector = lView[INJECTOR]; const tView = lView[TVIEW]; if (tDetails.loadingState !== DeferDependenciesLoadingState.NOT_STARTED) { // If the loading status is different from initial one, it means that // the loading of dependencies is in progress and there is nothing to do // in this function. All details can be obtained from the `tDetails` object. return tDetails.loadingPromise ?? Promise.resolve(); } const lDetails = getLDeferBlockDetails(lView, tNode); const primaryBlockTNode = getPrimaryBlockTNode(tView, tDetails); // Switch from NOT_STARTED -> IN_PROGRESS state. tDetails.loadingState = DeferDependenciesLoadingState.IN_PROGRESS; // Prefetching is triggered, cleanup all registered prefetch triggers. invokeTriggerCleanupFns(1 /* TriggerType.Prefetch */, lDetails); let dependenciesFn = tDetails.dependencyResolverFn; if (ngDevMode) { // Check if dependency function interceptor is configured. const deferDependencyInterceptor = injector.get(DEFER_BLOCK_DEPENDENCY_INTERCEPTOR, null, { optional: true, }); if (deferDependencyInterceptor) { dependenciesFn = deferDependencyInterceptor.intercept(dependenciesFn); } } // Indicate that an application is not stable and has a pending task. const pendingTasks = injector.get(PendingTasks); const taskId = pendingTasks.add(); // The `dependenciesFn` might be `null` when all dependencies within // a given defer block were eagerly referenced elsewhere in a file, // thus no dynamic `import()`s were produced. if (!dependenciesFn) { tDetails.loadingPromise = Promise.resolve().then(() => { tDetails.loadingPromise = null; tDetails.loadingState = DeferDependenciesLoadingState.COMPLETE; pendingTasks.remove(taskId); }); return tDetails.loadingPromise; } // Start downloading of defer block dependencies. tDetails.loadingPromise = Promise.allSettled(dependenciesFn()).then((results) => { let failed = false; const directiveDefs = []; const pipeDefs = []; for (const result of results) { if (result.status === 'fulfilled') { const dependency = result.value; const directiveDef = getComponentDef(dependency) || getDirectiveDef(dependency); if (directiveDef) { directiveDefs.push(directiveDef); } else { const pipeDef = getPipeDef(dependency); if (pipeDef) { pipeDefs.push(pipeDef); } } } else { failed = true; break; } } // Loading is completed, we no longer need the loading Promise // and a pending task should also be removed. tDetails.loadingPromise = null; pendingTasks.remove(taskId); if (failed) { tDetails.loadingState = DeferDependenciesLoadingState.FAILED; if (tDetails.errorTmplIndex === null) { const templateLocation = ngDevMode ? getTemplateLocationDetails(lView) : ''; const error = new RuntimeError(750 /* RuntimeErrorCode.DEFER_LOADING_FAILED */, ngDevMode && 'Loading dependencies for `@defer` block failed, ' + `but no \`@error\` block was configured${templateLocation}. ` + 'Consider using the `@error` block to render an error state.'); handleError(lView, error); } } else { tDetails.loadingState = DeferDependenciesLoadingState.COMPLETE; // Update directive and pipe registries to add newly downloaded dependencies. const primaryBlockTView = primaryBlockTNode.tView; if (directiveDefs.length > 0) { primaryBlockTView.directiveRegistry = addDepsToRegistry(primaryBlockTView.directiveRegistry, directiveDefs); // Extract providers from all NgModules imported by standalone components // used within this defer block. const directiveTypes = directiveDefs.map((def) => def.type); const providers = internalImportProvidersFrom(false, ...directiveTypes); tDetails.providers = providers; } if (pipeDefs.length > 0) { primaryBlockTView.pipeRegistry = addDepsToRegistry(primaryBlockTView.pipeRegistry, pipeDefs); } } }); return tDetails.loadingPromise; } /** Utility function to render placeholder content (if present) */ function renderPlaceholder(lView, tNode) { const lContainer = lView[tNode.index]; ngDevMode && assertLContainer(lContainer); renderDeferBlockState(DeferBlockState.Placeholder, tNode, lContainer); } /** * Subscribes to the "loading" Promise and renders corresponding defer sub-block, * based on the loading results. * * @param lContainer Represents an instance of a defer block. * @param tNode Represents defer block info shared across all instances. */ function renderDeferStateAfterResourceLoading(tDetails, tNode, lContainer) { ngDevMode && assertDefined(tDetails.loadingPromise, 'Expected loading Promise to exist on this defer block'); tDetails.loadingPromise.then(() => { if (tDetails.loadingState === DeferDependenciesLoadingState.COMPLETE) { ngDevMode && assertDeferredDependenciesLoaded(tDetails); // Everything is loaded, show the primary block content renderDeferBlockState(DeferBlockState.Complete, tNode, lContainer); } else if (tDetails.loadingState === DeferDependenciesLoadingState.FAILED) { renderDeferBlockState(DeferBlockState.Error, tNode, lContainer); } }); } /** * Attempts to trigger loading of defer block dependencies. * If the block is already in a loading, completed or an error state - * no additional actions are taken. */ function triggerDeferBlock(lView, tNode) { const tView = lView[TVIEW]; const lContainer = lView[tNode.index]; const injector = lView[INJECTOR]; ngDevMode && assertLContainer(lContainer); if (!shouldTriggerDeferBlock(injector)) return; const lDetails = getLDeferBlockDetails(lView, tNode); const tDetails = getTDeferBlockDetails(tView, tNode); // Defer block is triggered, cleanup all registered trigger functions. invokeAllTriggerCleanupFns(lDetails); switch (tDetails.loadingState) { case DeferDependenciesLoadingState.NOT_STARTED: renderDeferBlockState(DeferBlockState.Loading, tNode, lContainer); triggerResourceLoading(tDetails, lView, tNode); // The `loadingState` might have changed to "loading". if (tDetails.loadingState === DeferDependenciesLoadingState.IN_PROGRESS) { renderDeferStateAfterResourceLoading(tDetails, tNode, lContainer); } break; case DeferDependenciesLoadingState.IN_PROGRESS: renderDeferBlockState(DeferBlockState.Loading, tNode, lContainer); renderDeferStateAfterResourceLoading(tDetails, tNode, lContainer); break; case DeferDependenciesLoadingState.COMPLETE: ngDevMode && assertDeferredDependenciesLoaded(tDetails); renderDeferBlockState(DeferBlockState.Complete, tNode, lContainer); break; case DeferDependenciesLoadingState.FAILED: renderDeferBlockState(DeferBlockState.Error, tNode, lContainer); break; default: if (ngDevMode) { throwError('Unknown defer block state'); } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5zdHJ1Y3Rpb25zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vcGFja2FnZXMvY29yZS9zcmMvZGVmZXIvaW5zdHJ1Y3Rpb25zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRztBQUVILE9BQU8sRUFBQyxpQkFBaUIsRUFBQyxNQUFNLGtDQUFrQyxDQUFDO0FBRW5FLE9BQU8sRUFBQyxxQkFBcUIsRUFBQyxNQUFNLDRCQUE0QixDQUFDO0FBRWpFLE9BQU8sRUFBQyxtQkFBbUIsRUFBRSxjQUFjLEVBQXFCLE1BQU0sT0FBTyxDQUFDO0FBQzlFLE9BQU8sRUFBQywyQkFBMkIsRUFBQyxNQUFNLDJCQUEyQixDQUFDO0FBQ3RFLE9BQU8sRUFBQyxZQUFZLEVBQW1CLE1BQU0sV0FBVyxDQUFDO0FBQ3pELE9BQU8sRUFBQywwQkFBMEIsRUFBQyxNQUFNLG9CQUFvQixDQUFDO0FBQzlELE9BQU8sRUFBQyxtQ0FBbUMsRUFBQyxNQUFNLDhCQUE4QixDQUFDO0FBQ2pGLE9BQU8sRUFBQyxZQUFZLEVBQUMsTUFBTSxrQkFBa0IsQ0FBQztBQUM5QyxPQUFPLEVBQUMsZ0JBQWdCLEVBQUUsbUJBQW1CLEVBQUMsTUFBTSxtQkFBbUIsQ0FBQztBQUN4RSxPQUFPLEVBQUMsY0FBYyxFQUFDLE1BQU0scUJBQXFCLENBQUM7QUFDbkQsT0FBTyxFQUFDLGVBQWUsRUFBQyxNQUFNLDZCQUE2QixDQUFDO0FBQzVELE9BQU8sRUFBQyxlQUFlLEVBQUUsZUFBZSxFQUFFLFVBQVUsRUFBQyxNQUFNLHVCQUF1QixDQUFDO0FBQ25GLE9BQU8sRUFBQywwQkFBMEIsRUFBQyxNQUFNLDRDQUE0QyxDQUFDO0FBQ3RGLE9BQU8sRUFBQyxhQUFhLEVBQUMsTUFBTSx5Q0FBeUMsQ0FBQztBQUN0RSxPQUFPLEVBQUMsV0FBVyxFQUFDLE1BQU0sZ0NBQWdDLENBQUM7QUFDM0QsT0FBTyxFQUFDLGVBQWUsRUFBQyxNQUFNLGtD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