fix(core): properly get root nodes from embedded views with <ng-content> (#36051)
This commit fixes 2 separate issues related to root nodes retrieval from embedded views with `<ng-content>`: 1) we did not account for the case where there were no projectable nodes for a given `<ng-content>`; 2) we did not account for the case where projectable nodes for a given `<ng-content>` were represented as an array of native nodes (happens in the case of dynamically created components with projectable nodes); Fixes #35967 PR Close #36051
This commit is contained in:

committed by
Alex Rickabaugh

parent
4573a9997b
commit
a576852ad9
@ -26,7 +26,7 @@ import {getComponentDef} from './definition';
|
||||
import {NodeInjector} from './di';
|
||||
import {assignTViewNodeToLView, createLView, createTView, elementCreate, locateHostElement, renderView} from './instructions/shared';
|
||||
import {ComponentDef} from './interfaces/definition';
|
||||
import {TContainerNode, TElementContainerNode, TElementNode} from './interfaces/node';
|
||||
import {TContainerNode, TElementContainerNode, TElementNode, TNode} from './interfaces/node';
|
||||
import {domRendererFactory3, RendererFactory3, RNode} from './interfaces/renderer';
|
||||
import {LView, LViewFlags, TVIEW, TViewType} from './interfaces/view';
|
||||
import {MATH_ML_NAMESPACE, SVG_NAMESPACE} from './namespaces';
|
||||
@ -201,15 +201,19 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
||||
}
|
||||
}
|
||||
|
||||
tElementNode = getTNode(rootLView[TVIEW], 0) as TElementNode;
|
||||
tElementNode = getTNode(rootTView, 0) as TElementNode;
|
||||
|
||||
if (projectableNodes) {
|
||||
// projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade
|
||||
// case). Here we do normalize passed data structure to be an array of arrays to avoid
|
||||
// complex checks down the line.
|
||||
tElementNode.projection = projectableNodes.map((nodesforSlot: RNode[]) => {
|
||||
return Array.from(nodesforSlot);
|
||||
});
|
||||
if (projectableNodes !== undefined) {
|
||||
const projection: (TNode|RNode[]|null)[] = tElementNode.projection = [];
|
||||
for (let i = 0; i < this.ngContentSelectors.length; i++) {
|
||||
const nodesforSlot = projectableNodes[i];
|
||||
// Projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade
|
||||
// case). Here we do normalize passed data structure to be an array of arrays to avoid
|
||||
// complex checks down the line.
|
||||
// We also normalize the length of the passed in projectable nodes (to match the number of
|
||||
// <ng-container> slots defined by a component).
|
||||
projection.push(nodesforSlot != null ? Array.from(nodesforSlot) : null);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
|
||||
|
@ -10,7 +10,7 @@ import {ApplicationRef} from '../application_ref';
|
||||
import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref';
|
||||
import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_container_ref';
|
||||
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref';
|
||||
|
||||
import {assertDefined} from '../util/assert';
|
||||
import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupFn} from './instructions/shared';
|
||||
import {CONTAINER_HEADER_OFFSET} from './interfaces/container';
|
||||
import {TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
|
||||
@ -359,11 +359,22 @@ function collectNativeNodes(
|
||||
} else if (tNodeType === TNodeType.Projection) {
|
||||
const componentView = lView[DECLARATION_COMPONENT_VIEW];
|
||||
const componentHost = componentView[T_HOST] as TElementNode;
|
||||
const parentView = getLViewParent(componentView);
|
||||
let firstProjectedNode: TNode|null =
|
||||
(componentHost.projection as (TNode | null)[])[tNode.projection as number];
|
||||
if (firstProjectedNode !== null && parentView !== null) {
|
||||
collectNativeNodes(parentView[TVIEW], parentView, firstProjectedNode, result, true);
|
||||
const slotIdx = tNode.projection as number;
|
||||
ngDevMode &&
|
||||
assertDefined(
|
||||
componentHost.projection,
|
||||
'Components with projection nodes (<ng-content>) must have projection slots defined.');
|
||||
|
||||
const nodesInSlot = componentHost.projection![slotIdx];
|
||||
if (Array.isArray(nodesInSlot)) {
|
||||
result.push(...nodesInSlot);
|
||||
} else {
|
||||
const parentView = getLViewParent(componentView)!;
|
||||
ngDevMode &&
|
||||
assertDefined(
|
||||
parentView,
|
||||
'Component views should always have a parent view (component\'s host view)');
|
||||
collectNativeNodes(parentView[TVIEW], parentView, nodesInSlot, result, true);
|
||||
}
|
||||
}
|
||||
tNode = isProjection ? tNode.projectionNext : tNode.next;
|
||||
|
Reference in New Issue
Block a user