refactor(ivy): simplify logic of projectable nodes insertion (#29008)
This commit removes code duplication around projectables nodes insertion. It also simplfy the overall logic by using recursive function calls instead of hand-unrolled stack (we can always optimise this part if needed). PR Close #29008
This commit is contained in:
parent
efa10e33a9
commit
91a161aa13
|
@ -37,7 +37,7 @@ import {SanitizerFn} from './interfaces/sanitization';
|
||||||
import {StylingContext} from './interfaces/styling';
|
import {StylingContext} from './interfaces/styling';
|
||||||
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, T_HOST} from './interfaces/view';
|
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, T_HOST} from './interfaces/view';
|
||||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||||
import {appendChild, appendProjectedNode, createTextNode, insertView, removeView} from './node_manipulation';
|
import {appendChild, appendProjectedNodes, createTextNode, insertView, removeView} from './node_manipulation';
|
||||||
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
||||||
import {applyOnCreateInstructions} from './node_util';
|
import {applyOnCreateInstructions} from './node_util';
|
||||||
import {decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getContextLView, getCurrentDirectiveDef, getElementDepthCount, getIsParent, getLView, getPreviousOrParentTNode, increaseElementDepthCount, isCreationMode, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setIsParent, setPreviousOrParentTNode} from './state';
|
import {decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getContextLView, getCurrentDirectiveDef, getElementDepthCount, getIsParent, getLView, getPreviousOrParentTNode, increaseElementDepthCount, isCreationMode, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setIsParent, setPreviousOrParentTNode} from './state';
|
||||||
|
@ -2625,14 +2625,6 @@ export function projectionDef(selectors?: CssSelectorList[], textSelectors?: str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Stack used to keep track of projection nodes in projection() instruction.
|
|
||||||
*
|
|
||||||
* This is deliberately created outside of projection() to avoid allocating
|
|
||||||
* a new array each time the function is called. Instead the array will be
|
|
||||||
* re-used by each invocation. This works because the function is not reentrant.
|
|
||||||
*/
|
|
||||||
const projectionNodeStack: (LView | TNode)[] = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts previously re-distributed projected nodes. This instruction must be preceded by a call
|
* Inserts previously re-distributed projected nodes. This instruction must be preceded by a call
|
||||||
|
@ -2655,52 +2647,7 @@ export function projection(nodeIndex: number, selectorIndex: number = 0, attrs?:
|
||||||
setIsParent(false);
|
setIsParent(false);
|
||||||
|
|
||||||
// re-distribution of projectable nodes is stored on a component's view level
|
// re-distribution of projectable nodes is stored on a component's view level
|
||||||
const componentView = findComponentView(lView);
|
appendProjectedNodes(lView, tProjectionNode, selectorIndex, findComponentView(lView));
|
||||||
const componentNode = componentView[T_HOST] as TElementNode;
|
|
||||||
let nodeToProject = (componentNode.projection as(TNode | null)[])[selectorIndex];
|
|
||||||
let projectedView = componentView[PARENT] !as LView;
|
|
||||||
ngDevMode && assertLView(projectedView);
|
|
||||||
let projectionNodeIndex = -1;
|
|
||||||
|
|
||||||
if (Array.isArray(nodeToProject)) {
|
|
||||||
appendChild(nodeToProject, tProjectionNode, lView);
|
|
||||||
} else {
|
|
||||||
while (nodeToProject) {
|
|
||||||
if (nodeToProject.type === TNodeType.Projection) {
|
|
||||||
// This node is re-projected, so we must go up the tree to get its projected nodes.
|
|
||||||
const currentComponentView = findComponentView(projectedView);
|
|
||||||
const currentComponentHost = currentComponentView[T_HOST] as TElementNode;
|
|
||||||
const firstProjectedNode = (currentComponentHost.projection as(
|
|
||||||
TNode | null)[])[nodeToProject.projection as number];
|
|
||||||
|
|
||||||
if (firstProjectedNode) {
|
|
||||||
if (Array.isArray(firstProjectedNode)) {
|
|
||||||
appendChild(firstProjectedNode, tProjectionNode, lView);
|
|
||||||
} else {
|
|
||||||
projectionNodeStack[++projectionNodeIndex] = nodeToProject;
|
|
||||||
projectionNodeStack[++projectionNodeIndex] = projectedView;
|
|
||||||
|
|
||||||
nodeToProject = firstProjectedNode;
|
|
||||||
projectedView = getLViewParent(currentComponentView) !;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} 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 |= TNodeFlags.isProjected;
|
|
||||||
appendProjectedNode(nodeToProject, tProjectionNode, lView, projectedView);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are finished with a list of re-projected nodes, we need to get
|
|
||||||
// back to the root projection node that was re-projected.
|
|
||||||
if (nodeToProject.next === null && projectedView !== componentView[PARENT] !) {
|
|
||||||
projectedView = projectionNodeStack[projectionNodeIndex--] as LView;
|
|
||||||
nodeToProject = projectionNodeStack[projectionNodeIndex--] as TNode;
|
|
||||||
}
|
|
||||||
nodeToProject = nodeToProject.next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {attachPatchData} from './context_discovery';
|
||||||
import {LContainer, NATIVE, VIEWS, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
|
import {LContainer, NATIVE, VIEWS, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
|
||||||
import {ComponentDef} from './interfaces/definition';
|
import {ComponentDef} from './interfaces/definition';
|
||||||
import {NodeInjectorFactory} from './interfaces/injector';
|
import {NodeInjectorFactory} from './interfaces/injector';
|
||||||
import {TElementNode, TNode, TNodeFlags, TNodeType, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
|
import {TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
|
||||||
import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
|
import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
|
||||||
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
|
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
|
||||||
import {CHILD_HEAD, CLEANUP, FLAGS, HEADER_OFFSET, HookData, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, T_HOST, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
|
import {CHILD_HEAD, CLEANUP, FLAGS, HEADER_OFFSET, HookData, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, T_HOST, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
|
||||||
|
@ -717,6 +717,41 @@ export function nativeRemoveNode(renderer: Renderer3, rNode: RNode, isHostElemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 VLview)
|
||||||
|
* @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: LView, tProjectionNode: TProjectionNode, selectorIndex: number,
|
||||||
|
componentView: LView): void {
|
||||||
|
const projectedView = componentView[PARENT] !as LView;
|
||||||
|
const componentNode = componentView[T_HOST] as TElementNode;
|
||||||
|
let nodeToProject = (componentNode.projection as(TNode | null)[])[selectorIndex];
|
||||||
|
|
||||||
|
if (Array.isArray(nodeToProject)) {
|
||||||
|
appendChild(nodeToProject, tProjectionNode, lView);
|
||||||
|
} else {
|
||||||
|
while (nodeToProject) {
|
||||||
|
if (nodeToProject.type === TNodeType.Projection) {
|
||||||
|
appendProjectedNodes(
|
||||||
|
lView, tProjectionNode, (nodeToProject as TProjectionNode).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 |= TNodeFlags.isProjected;
|
||||||
|
appendProjectedNode(nodeToProject, tProjectionNode, lView, projectedView);
|
||||||
|
}
|
||||||
|
nodeToProject = nodeToProject.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends a projected node to the DOM, or in the case of a projected container,
|
* 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.
|
* appends the nodes from all of the container's active views to the DOM.
|
||||||
|
@ -726,7 +761,7 @@ export function nativeRemoveNode(renderer: Renderer3, rNode: RNode, isHostElemen
|
||||||
* @param currentView Current LView
|
* @param currentView Current LView
|
||||||
* @param projectionView Projection view (view above current)
|
* @param projectionView Projection view (view above current)
|
||||||
*/
|
*/
|
||||||
export function appendProjectedNode(
|
function appendProjectedNode(
|
||||||
projectedTNode: TNode, tProjectionNode: TNode, currentView: LView,
|
projectedTNode: TNode, tProjectionNode: TNode, currentView: LView,
|
||||||
projectionView: LView): void {
|
projectionView: LView): void {
|
||||||
const native = getNativeByTNode(projectedTNode, projectionView);
|
const native = getNativeByTNode(projectedTNode, projectionView);
|
||||||
|
|
Loading…
Reference in New Issue