UNPKG

@angular/core

Version:

Angular - the core framework

965 lines • 118 kB
/** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** * @license * Copyright Google Inc. 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 { ViewEncapsulation } from '../metadata/view'; import { assertDefined, assertDomNode } from '../util/assert'; import { assertLContainer, assertLView } from './assert'; import { attachPatchData } from './context_discovery'; import { CONTAINER_HEADER_OFFSET, NATIVE, unusedValueExportToPlacateAjd as unused1 } from './interfaces/container'; import { NodeInjectorFactory } from './interfaces/injector'; import { unusedValueExportToPlacateAjd as unused2 } from './interfaces/node'; import { unusedValueExportToPlacateAjd as unused3 } from './interfaces/projection'; import { isProceduralRenderer, unusedValueExportToPlacateAjd as unused4 } from './interfaces/renderer'; import { CHILD_HEAD, CLEANUP, FLAGS, HOST, NEXT, PARENT, QUERIES, RENDERER, TVIEW, T_HOST, unusedValueExportToPlacateAjd as unused5 } from './interfaces/view'; import { assertNodeOfPossibleTypes, assertNodeType } from './node_assert'; import { renderStringify } from './util/misc_utils'; import { findComponentView, getLViewParent } from './util/view_traversal_utils'; import { getNativeByTNode, isLContainer, isLView, isRootView, unwrapRNode, viewAttachedToContainer } from './util/view_utils'; /** @type {?} */ const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5; /** * @param {?} tNode * @param {?} embeddedView * @return {?} */ export function getLContainer(tNode, embeddedView) { ngDevMode && assertLView(embeddedView); /** @type {?} */ const container = (/** @type {?} */ (embeddedView[PARENT])); if (tNode.index === -1) { // This is a dynamically created view inside a dynamic container. // The parent isn't an LContainer if the embedded view hasn't been attached yet. return isLContainer(container) ? container : null; } else { ngDevMode && assertLContainer(container); // This is a inline view node (e.g. embeddedViewStart) return container; } } /** * Retrieves render parent for a given view. * Might be null if a view is not yet attached to any container. * @param {?} tViewNode * @param {?} view * @return {?} */ function getContainerRenderParent(tViewNode, view) { /** @type {?} */ const container = getLContainer(tViewNode, view); return container ? nativeParentNode(view[RENDERER], container[NATIVE]) : null; } /** @enum {number} */ const WalkTNodeTreeAction = { /** node insert in the native environment */ Insert: 0, /** node detach from the native environment */ Detach: 1, /** node destruction using the renderer's API */ Destroy: 2, }; /** * NOTE: for performance reasons, the possible actions are inlined within the function instead of * being passed as an argument. * @param {?} action * @param {?} renderer * @param {?} parent * @param {?} lNodeToHandle * @param {?=} beforeNode * @return {?} */ function executeActionOnElementOrContainer(action, renderer, parent, lNodeToHandle, beforeNode) { ngDevMode && assertDefined(lNodeToHandle, '\'lNodeToHandle\' is undefined'); /** @type {?} */ let lContainer; /** @type {?} */ let isComponent = false; // We are expecting an RNode, but in the case of a component or LContainer the `RNode` is wrapped // in an array which needs to be unwrapped. We need to know if it is a component and if // it has LContainer so that we can process all of those cases appropriately. if (isLContainer(lNodeToHandle)) { lContainer = lNodeToHandle; } else if (isLView(lNodeToHandle)) { isComponent = true; ngDevMode && assertDefined(lNodeToHandle[HOST], 'HOST must be defined for a component LView'); lNodeToHandle = (/** @type {?} */ (lNodeToHandle[HOST])); } /** @type {?} */ const rNode = unwrapRNode(lNodeToHandle); ngDevMode && assertDomNode(rNode); if (action === 0 /* Insert */) { nativeInsertBefore(renderer, (/** @type {?} */ (parent)), rNode, beforeNode || null); } else if (action === 1 /* Detach */) { nativeRemoveNode(renderer, rNode, isComponent); } else if (action === 2 /* Destroy */) { ngDevMode && ngDevMode.rendererDestroyNode++; (/** @type {?} */ (((/** @type {?} */ (renderer))).destroyNode))(rNode); } if (lContainer != null) { executeActionOnContainer(renderer, action, lContainer, parent, beforeNode); } } /** * @param {?} value * @param {?} renderer * @return {?} */ export function createTextNode(value, renderer) { return isProceduralRenderer(renderer) ? renderer.createText(renderStringify(value)) : renderer.createTextNode(renderStringify(value)); } /** * @param {?} lView * @param {?} insertMode * @param {?=} beforeNode * @return {?} */ export function addRemoveViewFromContainer(lView, insertMode, beforeNode) { /** @type {?} */ const renderParent = getContainerRenderParent((/** @type {?} */ (lView[TVIEW].node)), lView); ngDevMode && assertNodeType((/** @type {?} */ (lView[TVIEW].node)), 2 /* View */); if (renderParent) { /** @type {?} */ const renderer = lView[RENDERER]; /** @type {?} */ const action = insertMode ? 0 /* Insert */ : 1 /* Detach */; executeActionOnView(renderer, action, lView, renderParent, beforeNode); } } /** * Detach a `LView` from the DOM by detaching its nodes. * * @param {?} lView the `LView` to be detached. * @return {?} */ export function renderDetachView(lView) { executeActionOnView(lView[RENDERER], 1 /* Detach */, lView, null, null); } /** * Traverses down and up the tree of views and containers to remove listeners and * call onDestroy callbacks. * * Notes: * - Because it's used for onDestroy calls, it needs to be bottom-up. * - Must process containers instead of their views to avoid splicing * when views are destroyed and re-added. * - Using a while loop because it's faster than recursion * - Destroy only called on movement to sibling or movement to parent (laterally or up) * * @param {?} rootView The view to destroy * @return {?} */ export function destroyViewTree(rootView) { // If the view has no children, we can clean it up and return early. /** @type {?} */ let lViewOrLContainer = rootView[CHILD_HEAD]; if (!lViewOrLContainer) { return cleanUpView(rootView); } while (lViewOrLContainer) { /** @type {?} */ let next = null; if (isLView(lViewOrLContainer)) { // If LView, traverse down to child. next = lViewOrLContainer[CHILD_HEAD]; } else { ngDevMode && assertLContainer(lViewOrLContainer); // If container, traverse down to its first LView. /** @type {?} */ const firstView = lViewOrLContainer[CONTAINER_HEADER_OFFSET]; if (firstView) next = firstView; } if (!next) { // Only clean up view when moving to the side or up, as destroy hooks // should be called in order from the bottom up. while (lViewOrLContainer && !(/** @type {?} */ (lViewOrLContainer))[NEXT] && lViewOrLContainer !== rootView) { cleanUpView(lViewOrLContainer); lViewOrLContainer = getParentState(lViewOrLContainer, rootView); } cleanUpView(lViewOrLContainer || rootView); next = lViewOrLContainer && (/** @type {?} */ (lViewOrLContainer))[NEXT]; } lViewOrLContainer = next; } } /** * Inserts a view into a container. * * This adds the view to the container's array of active views in the correct * position. It also adds the view's elements to the DOM if the container isn't a * root node of another view (in that case, the view's elements will be added when * the container's parent view is added later). * * @param {?} lView The view to insert * @param {?} lContainer The container into which the view should be inserted * @param {?} index Which index in the container to insert the child view into * @return {?} */ export function insertView(lView, lContainer, index) { ngDevMode && assertLView(lView); ngDevMode && assertLContainer(lContainer); /** @type {?} */ const indexInContainer = CONTAINER_HEADER_OFFSET + index; /** @type {?} */ const containerLength = lContainer.length; if (index > 0) { // This is a new view, we need to add it to the children. lContainer[indexInContainer - 1][NEXT] = lView; } if (index < containerLength - CONTAINER_HEADER_OFFSET) { lView[NEXT] = lContainer[indexInContainer]; lContainer.splice(CONTAINER_HEADER_OFFSET + index, 0, lView); } else { lContainer.push(lView); lView[NEXT] = null; } lView[PARENT] = lContainer; // Notify query that a new view has been added if (lView[QUERIES]) { (/** @type {?} */ (lView[QUERIES])).insertView(index); } // Sets the attached flag lView[FLAGS] |= 128 /* Attached */; } /** * Detaches a view from a container. * * This method splices the view from the container's array of active views. It also * removes the view's elements from the DOM. * * @param {?} lContainer The container from which to detach a view * @param {?} removeIndex The index of the view to detach * @return {?} Detached LView instance. */ export function detachView(lContainer, removeIndex) { if (lContainer.length <= CONTAINER_HEADER_OFFSET) return; /** @type {?} */ const indexInContainer = CONTAINER_HEADER_OFFSET + removeIndex; /** @type {?} */ const viewToDetach = lContainer[indexInContainer]; if (viewToDetach) { if (removeIndex > 0) { lContainer[indexInContainer - 1][NEXT] = (/** @type {?} */ (viewToDetach[NEXT])); } lContainer.splice(CONTAINER_HEADER_OFFSET + removeIndex, 1); addRemoveViewFromContainer(viewToDetach, false); if ((viewToDetach[FLAGS] & 128 /* Attached */) && !(viewToDetach[FLAGS] & 256 /* Destroyed */) && viewToDetach[QUERIES]) { (/** @type {?} */ (viewToDetach[QUERIES])).removeView(); } viewToDetach[PARENT] = null; viewToDetach[NEXT] = null; // Unsets the attached flag viewToDetach[FLAGS] &= ~128 /* Attached */; } return viewToDetach; } /** * Removes a view from a container, i.e. detaches it and then destroys the underlying LView. * * @param {?} lContainer The container from which to remove a view * @param {?} removeIndex The index of the view to remove * @return {?} */ export function removeView(lContainer, removeIndex) { /** @type {?} */ const detachedView = detachView(lContainer, removeIndex); detachedView && destroyLView(detachedView); } /** * A standalone function which destroys an LView, * conducting cleanup (e.g. removing listeners, calling onDestroys). * * @param {?} lView The view to be destroyed. * @return {?} */ export function destroyLView(lView) { if (!(lView[FLAGS] & 256 /* Destroyed */)) { /** @type {?} */ const renderer = lView[RENDERER]; if (isProceduralRenderer(renderer) && renderer.destroyNode) { executeActionOnView(renderer, 2 /* Destroy */, lView, null, null); } destroyViewTree(lView); } } /** * Determines which LViewOrLContainer to jump to when traversing back up the * tree in destroyViewTree. * * Normally, the view's parent LView should be checked, but in the case of * embedded views, the container (which is the view node's parent, but not the * LView's parent) needs to be checked for a possible next property. * * @param {?} lViewOrLContainer The LViewOrLContainer for which we need a parent state * @param {?} rootView The rootView, so we don't propagate too far up the view tree * @return {?} The correct parent LViewOrLContainer */ export function getParentState(lViewOrLContainer, rootView) { /** @type {?} */ let tNode; if (isLView(lViewOrLContainer) && (tNode = lViewOrLContainer[T_HOST]) && tNode.type === 2 /* View */) { // if it's an embedded view, the state needs to go up to the container, in case the // container has a next return getLContainer((/** @type {?} */ (tNode)), lViewOrLContainer); } else { // otherwise, use parent view for containers or component views return lViewOrLContainer[PARENT] === rootView ? null : lViewOrLContainer[PARENT]; } } /** * Calls onDestroys hooks for all directives and pipes in a given view and then removes all * listeners. Listeners are removed as the last step so events delivered in the onDestroys hooks * can be propagated to \@Output listeners. * * @param {?} view The LView to clean up * @return {?} */ function cleanUpView(view) { if (isLView(view) && !(view[FLAGS] & 256 /* Destroyed */)) { // Usually the Attached flag is removed when the view is detached from its parent, however // if it's a root view, the flag won't be unset hence why we're also removing on destroy. view[FLAGS] &= ~128 /* Attached */; // Mark the LView as destroyed *before* executing the onDestroy hooks. An onDestroy hook // runs arbitrary user code, which could include its own `viewRef.destroy()` (or similar). If // We don't flag the view as destroyed before the hooks, this could lead to an infinite loop. // This also aligns with the ViewEngine behavior. It also means that the onDestroy hook is // really more of an "afterDestroy" hook if you think about it. view[FLAGS] |= 256 /* Destroyed */; executeOnDestroys(view); removeListeners(view); /** @type {?} */ const hostTNode = view[T_HOST]; // For component views only, the local renderer is destroyed as clean up time. if (hostTNode && hostTNode.type === 3 /* Element */ && isProceduralRenderer(view[RENDERER])) { ngDevMode && ngDevMode.rendererDestroy++; ((/** @type {?} */ (view[RENDERER]))).destroy(); } // For embedded views still attached to a container: remove query result from this view. if (viewAttachedToContainer(view) && view[QUERIES]) { (/** @type {?} */ (view[QUERIES])).removeView(); } } } /** * Removes listeners and unsubscribes from output subscriptions * @param {?} lView * @return {?} */ function removeListeners(lView) { /** @type {?} */ const tCleanup = lView[TVIEW].cleanup; if (tCleanup !== null) { /** @type {?} */ const lCleanup = (/** @type {?} */ (lView[CLEANUP])); for (let i = 0; i < tCleanup.length - 1; i += 2) { if (typeof tCleanup[i] === 'string') { // This is a native DOM listener /** @type {?} */ const idxOrTargetGetter = tCleanup[i + 1]; /** @type {?} */ const target = typeof idxOrTargetGetter === 'function' ? idxOrTargetGetter(lView) : unwrapRNode(lView[idxOrTargetGetter]); /** @type {?} */ const listener = lCleanup[tCleanup[i + 2]]; /** @type {?} */ const useCaptureOrSubIdx = tCleanup[i + 3]; if (typeof useCaptureOrSubIdx === 'boolean') { // native DOM listener registered with Renderer3 target.removeEventListener(tCleanup[i], listener, useCaptureOrSubIdx); } else { if (useCaptureOrSubIdx >= 0) { // unregister lCleanup[useCaptureOrSubIdx](); } else { // Subscription lCleanup[-useCaptureOrSubIdx].unsubscribe(); } } i += 2; } else { // This is a cleanup function that is grouped with the index of its context /** @type {?} */ const context = lCleanup[tCleanup[i + 1]]; tCleanup[i].call(context); } } lView[CLEANUP] = null; } } /** * Calls onDestroy hooks for this view * @param {?} view * @return {?} */ function executeOnDestroys(view) { /** @type {?} */ const tView = view[TVIEW]; /** @type {?} */ let destroyHooks; if (tView != null && (destroyHooks = tView.destroyHooks) != null) { for (let i = 0; i < destroyHooks.length; i += 2) { /** @type {?} */ const context = view[(/** @type {?} */ (destroyHooks[i]))]; // Only call the destroy hook if the context has been requested. if (!(context instanceof NodeInjectorFactory)) { ((/** @type {?} */ (destroyHooks[i + 1]))).call(context); } } } } /** * Returns a native element if a node can be inserted into the given parent. * * There are two reasons why we may not be able to insert a element immediately. * - Projection: When creating a child content element of a component, we have to skip the * insertion because the content of a component will be projected. * `<component><content>delayed due to projection</content></component>` * - Parent container is disconnected: This can happen when we are inserting a view into * parent container, which itself is disconnected. For example the parent container is part * of a View which has not be inserted or is made for projection but has not been inserted * into destination. * @param {?} tNode * @param {?} currentView * @return {?} */ function getRenderParent(tNode, currentView) { // Nodes of the top-most view can be inserted eagerly. if (isRootView(currentView)) { return nativeParentNode(currentView[RENDERER], getNativeByTNode(tNode, currentView)); } // Skip over element and ICU containers as those are represented by a comment node and // can't be used as a render parent. /** @type {?} */ const parent = getHighestElementOrICUContainer(tNode); /** @type {?} */ const renderParent = parent.parent; // If the parent is null, then we are inserting across views: either into an embedded view or a // component view. if (renderParent == null) { /** @type {?} */ const hostTNode = (/** @type {?} */ (currentView[T_HOST])); if (hostTNode.type === 2 /* View */) { // We are inserting a root element of an embedded view We might delay insertion of children // for a given view if it is disconnected. This might happen for 2 main reasons: // - view is not inserted into any container(view was created but not inserted yet) // - view is inserted into a container but the container itself is not inserted into the DOM // (container might be part of projection or child of a view that is not inserted yet). // In other words we can insert children of a given view if this view was inserted into a // container and the container itself has its render parent determined. return getContainerRenderParent((/** @type {?} */ (hostTNode)), currentView); } else { // We are inserting a root element of the component view into the component host element and // it should always be eager. return getHostNative(currentView); } } else { /** @type {?} */ const isIcuCase = parent && parent.type === 5 /* IcuContainer */; // If the parent of this node is an ICU container, then it is represented by comment node and we // need to use it as an anchor. If it is projected then its direct parent node is the renderer. if (isIcuCase && parent.flags & 2 /* isProjected */) { return (/** @type {?} */ (getNativeByTNode(parent, currentView).parentNode)); } ngDevMode && assertNodeType(renderParent, 3 /* Element */); if (renderParent.flags & 1 /* isComponent */ && !isIcuCase) { /** @type {?} */ const tData = currentView[TVIEW].data; /** @type {?} */ const tNode = (/** @type {?} */ (tData[renderParent.index])); /** @type {?} */ const encapsulation = ((/** @type {?} */ (tData[tNode.directiveStart]))).encapsulation; // We've got a parent which is an element in the current view. We just need to verify if the // parent element is not a component. Component's content nodes are not inserted immediately // because they will be projected, and so doing insert at this point would be wasteful. // Since the projection would then move it to its final destination. Note that we can't // make this assumption when using the Shadow DOM, because the native projection placeholders // (<content> or <slot>) have to be in place as elements are being inserted. if (encapsulation !== ViewEncapsulation.ShadowDom && encapsulation !== ViewEncapsulation.Native) { return null; } } return (/** @type {?} */ (getNativeByTNode(renderParent, currentView))); } } /** * Gets the native host element for a given view. Will return null if the current view does not have * a host element. * @param {?} currentView * @return {?} */ function getHostNative(currentView) { ngDevMode && assertLView(currentView); /** @type {?} */ const hostTNode = currentView[T_HOST]; return hostTNode && hostTNode.type === 3 /* Element */ ? ((/** @type {?} */ (getNativeByTNode(hostTNode, (/** @type {?} */ (getLViewParent(currentView))))))) : null; } /** * Inserts a native node before another native node for a given parent using {\@link Renderer3}. * This is a utility function that can be used when native nodes were determined - it abstracts an * actual renderer being used. * @param {?} renderer * @param {?} parent * @param {?} child * @param {?} beforeNode * @return {?} */ export function nativeInsertBefore(renderer, parent, child, beforeNode) { ngDevMode && ngDevMode.rendererInsertBefore++; if (isProceduralRenderer(renderer)) { renderer.insertBefore(parent, child, beforeNode); } else { parent.insertBefore(child, beforeNode, true); } } /** * @param {?} renderer * @param {?} parent * @param {?} child * @return {?} */ function nativeAppendChild(renderer, parent, child) { ngDevMode && ngDevMode.rendererAppendChild++; if (isProceduralRenderer(renderer)) { renderer.appendChild(parent, child); } else { parent.appendChild(child); } } /** * @param {?} renderer * @param {?} parent * @param {?} child * @param {?} beforeNode * @return {?} */ function nativeAppendOrInsertBefore(renderer, parent, child, beforeNode) { if (beforeNode !== null) { nativeInsertBefore(renderer, parent, child, beforeNode); } else { nativeAppendChild(renderer, parent, child); } } /** * Removes a node from the DOM given its native parent. * @param {?} renderer * @param {?} parent * @param {?} child * @param {?=} isHostElement * @return {?} */ function nativeRemoveChild(renderer, parent, child, isHostElement) { if (isProceduralRenderer(renderer)) { renderer.removeChild(parent, child, isHostElement); } else { parent.removeChild(child); } } /** * Returns a native parent of a given native node. * @param {?} renderer * @param {?} node * @return {?} */ export function nativeParentNode(renderer, node) { return (/** @type {?} */ ((isProceduralRenderer(renderer) ? renderer.parentNode(node) : node.parentNode))); } /** * Returns a native sibling of a given native node. * @param {?} renderer * @param {?} node * @return {?} */ export function nativeNextSibling(renderer, node) { return isProceduralRenderer(renderer) ? renderer.nextSibling(node) : node.nextSibling; } /** * Finds a native "anchor" node for cases where we can't append a native child directly * (`appendChild`) and need to use a reference (anchor) node for the `insertBefore` operation. * @param {?} parentTNode * @param {?} lView * @return {?} */ function getNativeAnchorNode(parentTNode, lView) { if (parentTNode.type === 2 /* View */) { /** @type {?} */ const lContainer = (/** @type {?} */ (getLContainer((/** @type {?} */ (parentTNode)), lView))); /** @type {?} */ const index = lContainer.indexOf(lView, CONTAINER_HEADER_OFFSET) - CONTAINER_HEADER_OFFSET; return getBeforeNodeForView(index, lContainer); } else if (parentTNode.type === 4 /* ElementContainer */ || parentTNode.type === 5 /* IcuContainer */) { return getNativeByTNode(parentTNode, lView); } return null; } /** * Appends the `child` native node (or a collection of nodes) to the `parent`. * * The element insertion might be delayed {\@link canInsertNativeNode}. * * @param {?} childEl The native child (or children) that should be appended * @param {?} childTNode The TNode of the child element * @param {?} currentView The current LView * @return {?} Whether or not the child was appended */ export function appendChild(childEl, childTNode, currentView) { /** @type {?} */ const renderParent = getRenderParent(childTNode, currentView); if (renderParent != null) { /** @type {?} */ const renderer = currentView[RENDERER]; /** @type {?} */ const parentTNode = childTNode.parent || (/** @type {?} */ (currentView[T_HOST])); /** @type {?} */ const anchorNode = getNativeAnchorNode(parentTNode, currentView); if (Array.isArray(childEl)) { for (let nativeNode of childEl) { nativeAppendOrInsertBefore(renderer, renderParent, nativeNode, anchorNode); } } else { nativeAppendOrInsertBefore(renderer, renderParent, childEl, anchorNode); } } } /** * Gets the top-level element or an ICU container if those containers are nested. * * @param {?} tNode The starting TNode for which we should skip element and ICU containers * @return {?} The TNode of the highest level ICU container or element container */ function getHighestElementOrICUContainer(tNode) { while (tNode.parent != null && (tNode.parent.type === 4 /* ElementContainer */ || tNode.parent.type === 5 /* IcuContainer */)) { tNode = tNode.parent; } return tNode; } /** * @param {?} viewIndexInContainer * @param {?} lContainer * @return {?} */ export function getBeforeNodeForView(viewIndexInContainer, lContainer) { /** @type {?} */ const nextViewIndex = CONTAINER_HEADER_OFFSET + viewIndexInContainer + 1; if (nextViewIndex < lContainer.length) { /** @type {?} */ const lView = (/** @type {?} */ (lContainer[nextViewIndex])); ngDevMode && assertDefined(lView[T_HOST], 'Missing Host TNode'); /** @type {?} */ const tViewNodeChild = ((/** @type {?} */ (lView[T_HOST]))).child; return tViewNodeChild !== null ? getNativeByTNode(tViewNodeChild, lView) : lContainer[NATIVE]; } else { return lContainer[NATIVE]; } } /** * Removes a native node itself using a given renderer. To remove the node we are looking up its * parent from the native tree as not all platforms / browsers support the equivalent of * node.remove(). * * @param {?} renderer A renderer to be used * @param {?} rNode The native node that should be removed * @param {?=} isHostElement A flag indicating if a node to be removed is a host of a component. * @return {?} */ export function nativeRemoveNode(renderer, rNode, isHostElement) { /** @type {?} */ const nativeParent = nativeParentNode(renderer, rNode); if (nativeParent) { nativeRemoveChild(renderer, nativeParent, rNode, isHostElement); } } /** * Appends nodes to a target projection place. Nodes to insert were previously re-distribution and * stored on a component host level. * @param {?} lView A LView where nodes are inserted (target LView) * @param {?} tProjectionNode A projection node where previously re-distribution should be appended * (target insertion place) * @param {?} selectorIndex A bucket from where nodes to project should be taken * @param {?} componentView A where projectable nodes were initially created (source view) * @return {?} */ export function appendProjectedNodes(lView, tProjectionNode, selectorIndex, componentView) { /** @type {?} */ const projectedView = (/** @type {?} */ ((/** @type {?} */ (componentView[PARENT])))); /** @type {?} */ const componentNode = (/** @type {?} */ (componentView[T_HOST])); /** @type {?} */ let nodeToProject = ((/** @type {?} */ (componentNode.projection)))[selectorIndex]; if (Array.isArray(nodeToProject)) { appendChild(nodeToProject, tProjectionNode, lView); } else { while (nodeToProject) { if (!(nodeToProject.flags & 32 /* isDetached */)) { if (nodeToProject.type === 1 /* Projection */) { appendProjectedNodes(lView, tProjectionNode, ((/** @type {?} */ (nodeToProject))).projection, findComponentView(projectedView)); } else { // This flag must be set now or we won't know that this node is projected // if the nodes are inserted into a container later. nodeToProject.flags |= 2 /* isProjected */; appendProjectedNode(nodeToProject, tProjectionNode, lView, projectedView); } } nodeToProject = nodeToProject.projectionNext; } } } /** * Loops over all children of a TNode container and appends them to the DOM * * @param {?} ngContainerChildTNode The first child of the TNode container * @param {?} tProjectionNode The projection (ng-content) TNode * @param {?} currentView Current LView * @param {?} projectionView Projection view (view above current) * @return {?} */ function appendProjectedChildren(ngContainerChildTNode, tProjectionNode, currentView, projectionView) { while (ngContainerChildTNode) { appendProjectedNode(ngContainerChildTNode, tProjectionNode, currentView, projectionView); ngContainerChildTNode = ngContainerChildTNode.next; } } /** * Appends a projected node to the DOM, or in the case of a projected container, * appends the nodes from all of the container's active views to the DOM. * * @param {?} projectedTNode The TNode to be projected * @param {?} tProjectionNode The projection (ng-content) TNode * @param {?} currentView Current LView * @param {?} projectionView Projection view (view above current) * @return {?} */ function appendProjectedNode(projectedTNode, tProjectionNode, currentView, projectionView) { /** @type {?} */ const native = getNativeByTNode(projectedTNode, projectionView); appendChild(native, tProjectionNode, currentView); // the projected contents are processed while in the shadow view (which is the currentView) // therefore we need to extract the view where the host element lives since it's the // logical container of the content projected views attachPatchData(native, projectionView); /** @type {?} */ const nodeOrContainer = projectionView[projectedTNode.index]; if (projectedTNode.type === 0 /* Container */) { // The node we are adding is a container and we are adding it to an element which // is not a component (no more re-projection). // Alternatively a container is projected at the root of a component's template // and can't be re-projected (as not content of any component). // Assign the final projection location in those cases. for (let i = CONTAINER_HEADER_OFFSET; i < nodeOrContainer.length; i++) { addRemoveViewFromContainer(nodeOrContainer[i], true, nodeOrContainer[NATIVE]); } } else if (projectedTNode.type === 5 /* IcuContainer */) { // The node we are adding is an ICU container which is why we also need to project all the // children nodes that might have been created previously and are linked to this anchor /** @type {?} */ let ngContainerChildTNode = (/** @type {?} */ (projectedTNode.child)); appendProjectedChildren(ngContainerChildTNode, ngContainerChildTNode, projectionView, projectionView); } else { if (projectedTNode.type === 4 /* ElementContainer */) { appendProjectedChildren(projectedTNode.child, tProjectionNode, currentView, projectionView); } if (isLContainer(nodeOrContainer)) { appendChild(nodeOrContainer[NATIVE], tProjectionNode, currentView); } } } /** * `executeActionOnView` performs an operation on the view as specified in `action` (insert, detach, * destroy) * * Inserting a view without projection or containers at top level is simple. Just iterate over the * root nodes of the View, and for each node perform the `action`. * * Things get more complicated with containers and projections. That is because coming across: * - Container: implies that we have to insert/remove/destroy the views of that container as well * which in turn can have their own Containers at the View roots. * - Projection: implies that we have to insert/remove/destroy the nodes of the projection. The * complication is that the nodes we are projecting can themselves have Containers * or other Projections. * * As you can see this is a very recursive problem. While the recursive implementation is not the * most efficient one, trying to unroll the nodes non-recursively results in very complex code that * is very hard (to maintain). We are sacrificing a bit of performance for readability using a * recursive implementation. * * @param {?} renderer Renderer to use * @param {?} action action to perform (insert, detach, destroy) * @param {?} lView The LView which needs to be inserted, detached, destroyed. * @param {?} renderParent parent DOM element for insertion/removal. * @param {?} beforeNode Before which node the insertions should happen. * @return {?} */ function executeActionOnView(renderer, action, lView, renderParent, beforeNode) { /** @type {?} */ const tView = lView[TVIEW]; ngDevMode && assertNodeType((/** @type {?} */ (tView.node)), 2 /* View */); /** @type {?} */ let viewRootTNode = (/** @type {?} */ (tView.node)).child; while (viewRootTNode !== null) { executeActionOnNode(renderer, action, lView, viewRootTNode, renderParent, beforeNode); viewRootTNode = viewRootTNode.next; } } /** * `executeActionOnProjection` performs an operation on the projection specified by `action` * (insert, detach, destroy). * * Inserting a projection requires us to locate the projected nodes from the parent component. The * complication is that those nodes themselves could be re-projected from their parent component. * * @param {?} renderer Renderer to use * @param {?} action action to perform (insert, detach, destroy) * @param {?} lView The LView which needs to be inserted, detached, destroyed. * @param {?} tProjectionNode * @param {?} renderParent parent DOM element for insertion/removal. * @param {?} beforeNode Before which node the insertions should happen. * @return {?} */ function executeActionOnProjection(renderer, action, lView, tProjectionNode, renderParent, beforeNode) { /** @type {?} */ const componentLView = findComponentView(lView); /** @type {?} */ const componentNode = (/** @type {?} */ (componentLView[T_HOST])); /** @type {?} */ const nodeToProject = (/** @type {?} */ ((/** @type {?} */ (componentNode.projection))[tProjectionNode.projection])); if (Array.isArray(nodeToProject)) { for (let i = 0; i < nodeToProject.length; i++) { /** @type {?} */ const rNode = nodeToProject[i]; ngDevMode && assertDomNode(rNode); executeActionOnElementOrContainer(action, renderer, renderParent, rNode, beforeNode); } } else { /** @type {?} */ let projectionTNode = nodeToProject; /** @type {?} */ const projectedComponentLView = (/** @type {?} */ (componentLView[PARENT])); while (projectionTNode !== null) { executeActionOnNode(renderer, action, projectedComponentLView, projectionTNode, renderParent, beforeNode); projectionTNode = projectionTNode.projectionNext; } } } /** * `executeActionOnContainer` performs an operation on the container and its views as specified by * `action` (insert, detach, destroy) * * Inserting a Container is complicated by the fact that the container may have Views which * themselves have containers or projections. * * @param {?} renderer Renderer to use * @param {?} action action to perform (insert, detach, destroy) * @param {?} lContainer The LContainer which needs to be inserted, detached, destroyed. * @param {?} renderParent parent DOM element for insertion/removal. * @param {?} beforeNode Before which node the insertions should happen. * @return {?} */ function executeActionOnContainer(renderer, action, lContainer, renderParent, beforeNode) { ngDevMode && assertLContainer(lContainer); /** @type {?} */ const anchor = lContainer[NATIVE]; // LContainer has its own before node. /** @type {?} */ const native = unwrapRNode(lContainer); // An LContainer can be created dynamically on any node by injecting ViewContainerRef. // Asking for a ViewContainerRef on an element will result in a creation of a separate anchor node // (comment in the DOM) that will be different from the LContainer's host node. In this particular // case we need to execute action on 2 nodes: // - container's host node (this is done in the executeNodeAction) // - container's host node (this is done here) if (anchor !== native) { executeActionOnElementOrContainer(action, renderer, renderParent, anchor, beforeNode); } for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) { /** @type {?} */ const lView = (/** @type {?} */ (lContainer[i])); executeActionOnView(renderer, action, lView, renderParent, anchor); } } /** * `executeActionOnElementContainer` performs an operation on the ng-container node and its child * nodes as specified by the `action` (insert, detach, destroy). * * @param {?} renderer Renderer to use * @param {?} action action to perform (insert, detach, destroy) * @param {?} lView The LView which needs to be inserted, detached, destroyed. * @param {?} tElementContainerNode The TNode associated with the ElementContainer. * @param {?} renderParent parent DOM element for insertion/removal. * @param {?} beforeNode Before which node the insertions should happen. * @return {?} */ function executeActionOnElementContainer(renderer, action, lView, tElementContainerNode, renderParent, beforeNode) { /** @type {?} */ const node = lView[tElementContainerNode.index]; executeActionOnElementOrContainer(action, renderer, renderParent, node, beforeNode); /** @type {?} */ let childTNode = tElementContainerNode.child; while (childTNode) { executeActionOnNode(renderer, action, lView, childTNode, renderParent, beforeNode); childTNode = childTNode.next; } } /** * @param {?} renderer * @param {?} action * @param {?} lView * @param {?} tNode * @param {?} renderParent * @param {?} beforeNode * @return {?} */ function executeActionOnNode(renderer, action, lView, tNode, renderParent, beforeNode) { /** @type {?} */ const elementContainerRootTNodeType = tNode.type; if (elementContainerRootTNodeType === 4 /* ElementContainer */) { executeActionOnElementContainer(renderer, action, lView, (/** @type {?} */ (tNode)), renderParent, beforeNode); } else if (elementContainerRootTNodeType === 1 /* Projection */) { executeActionOnProjection(renderer, action, lView, (/** @type {?} */ (tNode)), renderParent, beforeNode); } else { ngDevMode && assertNodeOfPossibleTypes(tNode, 3 /* Element */, 0 /* Container */); executeActionOnElementOrContainer(action, renderer, renderParent, lView[tNode.index], beforeNode); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZV9tYW5pcHVsYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9wYWNrYWdlcy9jb3JlL3NyYy9yZW5kZXIzL25vZGVfbWFuaXB1bGF0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7O0FBUUEsT0FBTyxFQUFDLGlCQUFpQixFQUFDLE1BQU0sa0JBQWtCLENBQUM7QUFDbkQsT0FBTyxFQUFDLGFBQWEsRUFBRSxhQUFhLEVBQUMsTUFBTSxnQkFBZ0IsQ0FBQztBQUU1RCxPQUFPLEVBQUMsZ0JBQWdCLEVBQUUsV0FBVyxFQUFDLE1BQU0sVUFBVSxDQUFDO0FBQ3ZELE9BQU8sRUFBQyxlQUFlLEVBQUMsTUFBTSxxQkFBcUIsQ0FBQztBQUNwRCxPQUFPLEVBQUMsdUJBQXVCLEVBQWMsTUFBTSxFQUFFLDZCQUE2QixJQUFJLE9BQU8sRUFBQyxNQUFNLHdCQUF3QixDQUFDO0FBRTdILE9BQU8sRUFBQyxtQkFBbUIsRUFBQyxNQUFNLHVCQUF1QixDQUFDO0FBQzFELE9BQU8sRUFBZ0csNkJBQTZCLElBQUksT0FBTyxFQUFDLE1BQU0sbUJBQW1CLENBQUM7QUFDMUssT0FBTyxFQUFDLDZCQUE2QixJQUFJLE9BQU8sRUFBQyxNQUFNLHlCQUF5QixDQUFDO0FBQ2pGLE9BQU8sRUFBeUQsb0JBQW9CLEVBQUUsNkJBQTZCLElBQUksT0FBTyxFQUFDLE1BQU0sdUJBQXVCLENBQUM7QUFFN0osT0FBTyxFQUFDLFVBQVUsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBK0IsSUFBSSxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsNkJBQTZCLElBQUksT0FBTyxFQUFDLE1BQU0sbUJBQW1CLENBQUM7QUFDMUwsT0FBTyxFQUFDLHlCQUF5QixFQUFFLGNBQWMsRUFBQyxNQUFNLGVBQWUsQ0FBQztBQUN4RSxPQUFPLEVBQUMsZUFBZSxF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