UNPKG

@angular/core

Version:

Angular - the core framework

796 lines • 114 kB
/** * @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 * as tslib_1 from "tslib"; 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'; var unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5; export function getLContainer(tNode, embeddedView) { ngDevMode && assertLView(embeddedView); var container = 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. */ function getContainerRenderParent(tViewNode, view) { var container = getLContainer(tViewNode, view); return container ? nativeParentNode(view[RENDERER], container[NATIVE]) : null; } /** * NOTE: for performance reasons, the possible actions are inlined within the function instead of * being passed as an argument. */ function executeActionOnElementOrContainer(action, renderer, parent, lNodeToHandle, beforeNode) { ngDevMode && assertDefined(lNodeToHandle, '\'lNodeToHandle\' is undefined'); var lContainer; var 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 = lNodeToHandle[HOST]; } var rNode = unwrapRNode(lNodeToHandle); ngDevMode && assertDomNode(rNode); if (action === 0 /* Insert */) { nativeInsertBefore(renderer, parent, rNode, beforeNode || null); } else if (action === 1 /* Detach */) { nativeRemoveNode(renderer, rNode, isComponent); } else if (action === 2 /* Destroy */) { ngDevMode && ngDevMode.rendererDestroyNode++; renderer.destroyNode(rNode); } if (lContainer != null) { executeActionOnContainer(renderer, action, lContainer, parent, beforeNode); } } export function createTextNode(value, renderer) { return isProceduralRenderer(renderer) ? renderer.createText(renderStringify(value)) : renderer.createTextNode(renderStringify(value)); } export function addRemoveViewFromContainer(lView, insertMode, beforeNode) { var renderParent = getContainerRenderParent(lView[TVIEW].node, lView); ngDevMode && assertNodeType(lView[TVIEW].node, 2 /* View */); if (renderParent) { var renderer = lView[RENDERER]; var 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. */ 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 */ export function destroyViewTree(rootView) { // If the view has no children, we can clean it up and return early. var lViewOrLContainer = rootView[CHILD_HEAD]; if (!lViewOrLContainer) { return cleanUpView(rootView); } while (lViewOrLContainer) { var 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. var 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 && !lViewOrLContainer[NEXT] && lViewOrLContainer !== rootView) { cleanUpView(lViewOrLContainer); lViewOrLContainer = getParentState(lViewOrLContainer, rootView); } cleanUpView(lViewOrLContainer || rootView); next = lViewOrLContainer && 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 */ export function insertView(lView, lContainer, index) { ngDevMode && assertLView(lView); ngDevMode && assertLContainer(lContainer); var indexInContainer = CONTAINER_HEADER_OFFSET + index; var 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]) { 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 * @returns Detached LView instance. */ export function detachView(lContainer, removeIndex) { if (lContainer.length <= CONTAINER_HEADER_OFFSET) return; var indexInContainer = CONTAINER_HEADER_OFFSET + removeIndex; var viewToDetach = lContainer[indexInContainer]; if (viewToDetach) { if (removeIndex > 0) { lContainer[indexInContainer - 1][NEXT] = viewToDetach[NEXT]; } lContainer.splice(CONTAINER_HEADER_OFFSET + removeIndex, 1); addRemoveViewFromContainer(viewToDetach, false); if ((viewToDetach[FLAGS] & 128 /* Attached */) && !(viewToDetach[FLAGS] & 256 /* Destroyed */) && viewToDetach[QUERIES]) { 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 */ export function removeView(lContainer, removeIndex) { var 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. */ export function destroyLView(lView) { if (!(lView[FLAGS] & 256 /* Destroyed */)) { var 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 * @returns The correct parent LViewOrLContainer */ export function getParentState(lViewOrLContainer, rootView) { var 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(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 */ 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); var 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++; view[RENDERER].destroy(); } // For embedded views still attached to a container: remove query result from this view. if (viewAttachedToContainer(view) && view[QUERIES]) { view[QUERIES].removeView(); } } } /** Removes listeners and unsubscribes from output subscriptions */ function removeListeners(lView) { var tCleanup = lView[TVIEW].cleanup; if (tCleanup !== null) { var lCleanup = lView[CLEANUP]; for (var i = 0; i < tCleanup.length - 1; i += 2) { if (typeof tCleanup[i] === 'string') { // This is a native DOM listener var idxOrTargetGetter = tCleanup[i + 1]; var target = typeof idxOrTargetGetter === 'function' ? idxOrTargetGetter(lView) : unwrapRNode(lView[idxOrTargetGetter]); var listener = lCleanup[tCleanup[i + 2]]; var 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 var context = lCleanup[tCleanup[i + 1]]; tCleanup[i].call(context); } } lView[CLEANUP] = null; } } /** Calls onDestroy hooks for this view */ function executeOnDestroys(view) { var tView = view[TVIEW]; var destroyHooks; if (tView != null && (destroyHooks = tView.destroyHooks) != null) { for (var i = 0; i < destroyHooks.length; i += 2) { var context = view[destroyHooks[i]]; // Only call the destroy hook if the context has been requested. if (!(context instanceof NodeInjectorFactory)) { 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. */ 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. var parent = getHighestElementOrICUContainer(tNode); var 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) { var hostTNode = 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(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 { var 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 getNativeByTNode(parent, currentView).parentNode; } ngDevMode && assertNodeType(renderParent, 3 /* Element */); if (renderParent.flags & 1 /* isComponent */ && !isIcuCase) { var tData = currentView[TVIEW].data; var tNode_1 = tData[renderParent.index]; var encapsulation = tData[tNode_1.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 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. */ function getHostNative(currentView) { ngDevMode && assertLView(currentView); var hostTNode = currentView[T_HOST]; return hostTNode && hostTNode.type === 3 /* Element */ ? getNativeByTNode(hostTNode, 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. */ export function nativeInsertBefore(renderer, parent, child, beforeNode) { ngDevMode && ngDevMode.rendererInsertBefore++; if (isProceduralRenderer(renderer)) { renderer.insertBefore(parent, child, beforeNode); } else { parent.insertBefore(child, beforeNode, true); } } function nativeAppendChild(renderer, parent, child) { ngDevMode && ngDevMode.rendererAppendChild++; if (isProceduralRenderer(renderer)) { renderer.appendChild(parent, child); } else { parent.appendChild(child); } } 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. */ 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. */ export function nativeParentNode(renderer, node) { return (isProceduralRenderer(renderer) ? renderer.parentNode(node) : node.parentNode); } /** * Returns a native sibling of a given native node. */ 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 */ function getNativeAnchorNode(parentTNode, lView) { if (parentTNode.type === 2 /* View */) { var lContainer = getLContainer(parentTNode, lView); var 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 * @returns Whether or not the child was appended */ export function appendChild(childEl, childTNode, currentView) { var e_1, _a; var renderParent = getRenderParent(childTNode, currentView); if (renderParent != null) { var renderer = currentView[RENDERER]; var parentTNode = childTNode.parent || currentView[T_HOST]; var anchorNode = getNativeAnchorNode(parentTNode, currentView); if (Array.isArray(childEl)) { try { for (var childEl_1 = tslib_1.__values(childEl), childEl_1_1 = childEl_1.next(); !childEl_1_1.done; childEl_1_1 = childEl_1.next()) { var nativeNode = childEl_1_1.value; nativeAppendOrInsertBefore(renderer, renderParent, nativeNode, anchorNode); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (childEl_1_1 && !childEl_1_1.done && (_a = childEl_1.return)) _a.call(childEl_1); } finally { if (e_1) throw e_1.error; } } } 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 * @returns 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; } export function getBeforeNodeForView(viewIndexInContainer, lContainer) { var nextViewIndex = CONTAINER_HEADER_OFFSET + viewIndexInContainer + 1; if (nextViewIndex < lContainer.length) { var lView = lContainer[nextViewIndex]; ngDevMode && assertDefined(lView[T_HOST], 'Missing Host TNode'); var tViewNodeChild = 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. */ export function nativeRemoveNode(renderer, rNode, isHostElement) { var 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) */ export function appendProjectedNodes(lView, tProjectionNode, selectorIndex, componentView) { var projectedView = componentView[PARENT]; var componentNode = componentView[T_HOST]; var nodeToProject = 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, 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) */ 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) */ function appendProjectedNode(projectedTNode, tProjectionNode, currentView, projectionView) { var 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); var 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 (var 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 var ngContainerChildTNode = 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. */ function executeActionOnView(renderer, action, lView, renderParent, beforeNode) { var tView = lView[TVIEW]; ngDevMode && assertNodeType(tView.node, 2 /* View */); var viewRootTNode = 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 renderParent parent DOM element for insertion/removal. * @param beforeNode Before which node the insertions should happen. */ function executeActionOnProjection(renderer, action, lView, tProjectionNode, renderParent, beforeNode) { var componentLView = findComponentView(lView); var componentNode = componentLView[T_HOST]; var nodeToProject = componentNode.projection[tProjectionNode.projection]; if (Array.isArray(nodeToProject)) { for (var i = 0; i < nodeToProject.length; i++) { var rNode = nodeToProject[i]; ngDevMode && assertDomNode(rNode); executeActionOnElementOrContainer(action, renderer, renderParent, rNode, beforeNode); } } else { var projectionTNode = nodeToProject; var projectedComponentLView = 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. */ function executeActionOnContainer(renderer, action, lContainer, renderParent, beforeNode) { ngDevMode && assertLContainer(lContainer); var anchor = lContainer[NATIVE]; // LContainer has its own before node. var 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 (var i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) { var lView = 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. */ function executeActionOnElementContainer(renderer, action, lView, tElementContainerNode, renderParent, beforeNode) { var node = lView[tElementContainerNode.index]; executeActionOnElementOrContainer(action, renderer, renderParent, node, beforeNode); var childTNode = tElementContainerNode.child; while (childTNode) { executeActionOnNode(renderer, action, lView, childTNode, renderParent, beforeNode); childTNode = childTNode.next; } } function executeActionOnNode(renderer, action, lView, tNode, renderParent, beforeNode) { var elementContainerRootTNodeType = tNode.type; if (elementContainerRootTNodeType === 4 /* ElementContainer */) { executeActionOnElementContainer(renderer, action, lView, tNode, renderParent, beforeNode); } else if (elementContainerRootTNodeType === 1 /* Projection */) { executeActionOnProjection(renderer, action, lView, tNode, renderParent, beforeNode); } else { ngDevMode && assertNodeOfPossibleTypes(tNode, 3 /* Element */, 0 /* Container */); executeActionOnElementOrContainer(action, renderer, renderParent, lView[tNode.index], beforeNode); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZV9tYW5pcHVsYXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9wYWNrYWdlcy9jb3JlL3NyYy9yZW5kZXIzL25vZGVfbWFuaXB1bGF0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRzs7QUFFSCxPQUFPLEVBQUMsaUJBQWlCLEVBQUMsTUFBTSxrQkFBa0IsQ0FBQztBQUNuRCxPQUFPLEVBQUMsYUFBYSxFQUFFLGFBQWEsRUFBQyxNQUFNLGdCQUFnQixDQUFDO0FBRTVELE9BQU8sRUFBQyxnQkFBZ0IsRUFBRSxXQUFXLEVBQUMsTUFBTSxVQUFVLENBQUM7QUFDdkQsT0FBTyxFQUFDLGVBQWUsRUFBQyxNQUFNLHFCQUFxQixDQUFDO0FBQ3BELE9BQU8sRUFBQyx1QkFBdUIsRUFBYyxNQUFNLEVBQUUsNkJBQTZCLElBQUksT0FBTyxFQUFDLE1BQU0sd0JBQXdCLENBQUM7QUFFN0gsT0FBTyxFQUFDLG1CQUFtQixFQUFDLE1BQU0sdUJBQXVCLENBQUM7QUFDMUQsT0FBTyxFQUFnRyw2QkFBNkIsSUFBSSxPQUFPLEVBQUMsTUFBTSxtQkFBbUIsQ0FBQztBQUMxSyxPQUFPLEVBQUMsNkJBQTZCLElBQUksT0FBTyxFQUFDLE1BQU0seUJBQXlCLENBQUM7QUFDakYsT0FBTyxFQUF5RCxvQkFBb0IsRUFBRSw2QkFBNkIsSUFBSSxPQUFPLEVBQUMsTUFBTSx1QkFBdUIsQ0FBQztBQUU3SixPQUFPLEVBQUMsVUFBVSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUErQixJQUFJLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSw2QkFBNkIsSUFBSSxPQUFPLEVBQUMsTUFBTSxtQkFBbUIsQ0FBQztBQUMxTCxPQUFPLEVBQUMseUJBQXlCLEVBQUUsY0FBYyxFQUFDLE1BQU0sZUFBZSxDQUFDO0FBQ3hFLE9B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