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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user