refactor(ivy): replace LNode.dynamicLContainerNode with flat LContainers (#26407)
PR Close #26407
This commit is contained in:
parent
70cd112872
commit
735dfd3b1a
|
@ -8,8 +8,10 @@
|
|||
import './ng_dev_mode';
|
||||
|
||||
import {assertEqual} from './assert';
|
||||
import {ACTIVE_INDEX, HOST_NATIVE, LContainer} from './interfaces/container';
|
||||
import {LElementNode, TNode, TNodeFlags} from './interfaces/node';
|
||||
import {RElement} from './interfaces/renderer';
|
||||
import {StylingContext, StylingIndex} from './interfaces/styling';
|
||||
import {CONTEXT, HEADER_OFFSET, LViewData, TVIEW} from './interfaces/view';
|
||||
|
||||
/**
|
||||
|
@ -390,6 +392,26 @@ function getDirectiveEndIndex(tNode: TNode, startIndex: number): number {
|
|||
return count ? (startIndex + count) : -1;
|
||||
}
|
||||
|
||||
export function readElementValue(value: LElementNode | any[]): LElementNode {
|
||||
return (Array.isArray(value) ? (value as any as any[])[0] : value) as LElementNode;
|
||||
/**
|
||||
* Takes the value of a slot in `LViewData` and returns the element node.
|
||||
*
|
||||
* Normally, element nodes are stored flat, but if the node has styles/classes on it,
|
||||
* it might be wrapped in a styling context. Or if that node has a directive that injects
|
||||
* ViewContainerRef, it may be wrapped in an LContainer.
|
||||
*
|
||||
* @param value The initial value in `LViewData`
|
||||
*/
|
||||
export function readElementValue(value: LElementNode | StylingContext | LContainer): LElementNode {
|
||||
if (Array.isArray(value)) {
|
||||
if (typeof value[ACTIVE_INDEX] === 'number') {
|
||||
// This is an LContainer. It may also have a styling context.
|
||||
value = value[HOST_NATIVE] as LElementNode | StylingContext;
|
||||
return Array.isArray(value) ? value[StylingIndex.ElementPosition] ! : value;
|
||||
} else {
|
||||
// This is a StylingContext, which stores the element node at 0.
|
||||
return value[StylingIndex.ElementPosition] as LElementNode;
|
||||
}
|
||||
} else {
|
||||
return value; // Regular LNode is stored here
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,13 @@
|
|||
|
||||
import {assertEqual, assertLessThan} from './assert';
|
||||
import {NO_CHANGE, _getViewData, adjustBlueprintForNewNode, bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4, createNodeAtIndex, getRenderer, getTNode, load, loadElement, resetComponentState} from './instructions';
|
||||
import {RENDER_PARENT} from './interfaces/container';
|
||||
import {LContainer, NATIVE, RENDER_PARENT} from './interfaces/container';
|
||||
import {LContainerNode, LNode, TElementNode, TNode, TNodeType} from './interfaces/node';
|
||||
import {StylingContext} from './interfaces/styling';
|
||||
import {BINDING_INDEX, HEADER_OFFSET, HOST_NODE, TVIEW} from './interfaces/view';
|
||||
import {appendChild, createTextNode, removeChild} from './node_manipulation';
|
||||
import {stringify} from './util';
|
||||
import {getLNode, isLContainer, stringify} from './util';
|
||||
|
||||
|
||||
/**
|
||||
* A list of flags to encode the i18n instructions used to translate the template.
|
||||
|
@ -245,9 +247,7 @@ function generateMappingInstructions(
|
|||
return partIndex;
|
||||
}
|
||||
|
||||
// TODO: Remove LNode arg when we remove dynamicContainerNode
|
||||
function appendI18nNode(
|
||||
node: LNode, tNode: TNode, parentTNode: TNode, previousTNode: TNode): TNode {
|
||||
function appendI18nNode(tNode: TNode, parentTNode: TNode, previousTNode: TNode): TNode {
|
||||
if (ngDevMode) {
|
||||
ngDevMode.rendererMoveNode++;
|
||||
}
|
||||
|
@ -272,11 +272,13 @@ function appendI18nNode(
|
|||
}
|
||||
}
|
||||
|
||||
appendChild(node.native, tNode, viewData);
|
||||
const native = getLNode(tNode, viewData).native;
|
||||
appendChild(native, tNode, viewData);
|
||||
|
||||
// Template containers also have a comment node for the `ViewContainerRef` that should be moved
|
||||
if (tNode.type === TNodeType.Container && node.dynamicLContainerNode) {
|
||||
appendChild(node.dynamicLContainerNode.native, tNode, viewData);
|
||||
const slotValue = viewData[tNode.index];
|
||||
if (tNode.type !== TNodeType.Container && isLContainer(slotValue)) {
|
||||
// Nodes that inject ViewContainerRef also have a comment node that should be moved
|
||||
appendChild(slotValue[NATIVE], tNode, viewData);
|
||||
}
|
||||
|
||||
return tNode;
|
||||
|
@ -327,20 +329,16 @@ export function i18nApply(startIndex: number, instructions: I18nInstruction[]):
|
|||
const instruction = instructions[i] as number;
|
||||
switch (instruction & I18nInstructions.InstructionMask) {
|
||||
case I18nInstructions.Element:
|
||||
const elementIndex = instruction & I18nInstructions.IndexMask;
|
||||
const element: LNode = load(elementIndex);
|
||||
const elementTNode = getTNode(elementIndex);
|
||||
localPreviousTNode =
|
||||
appendI18nNode(element, elementTNode, localParentTNode, localPreviousTNode);
|
||||
const elementTNode = getTNode(instruction & I18nInstructions.IndexMask);
|
||||
localPreviousTNode = appendI18nNode(elementTNode, localParentTNode, localPreviousTNode);
|
||||
localParentTNode = elementTNode;
|
||||
break;
|
||||
case I18nInstructions.Expression:
|
||||
case I18nInstructions.TemplateRoot:
|
||||
case I18nInstructions.Any:
|
||||
const nodeIndex = instruction & I18nInstructions.IndexMask;
|
||||
const node: LNode = load(nodeIndex);
|
||||
localPreviousTNode =
|
||||
appendI18nNode(node, getTNode(nodeIndex), localParentTNode, localPreviousTNode);
|
||||
appendI18nNode(getTNode(nodeIndex), localParentTNode, localPreviousTNode);
|
||||
break;
|
||||
case I18nInstructions.Text:
|
||||
if (ngDevMode) {
|
||||
|
@ -352,11 +350,9 @@ export function i18nApply(startIndex: number, instructions: I18nInstruction[]):
|
|||
// Create text node at the current end of viewData. Must subtract header offset because
|
||||
// createNodeAtIndex takes a raw index (not adjusted by header offset).
|
||||
adjustBlueprintForNewNode(viewData);
|
||||
const lastNodeIndex = viewData.length - 1 - HEADER_OFFSET;
|
||||
const textTNode =
|
||||
createNodeAtIndex(lastNodeIndex, TNodeType.Element, textRNode, null, null);
|
||||
localPreviousTNode = appendI18nNode(
|
||||
loadElement(lastNodeIndex), textTNode, localParentTNode, localPreviousTNode);
|
||||
const textTNode = createNodeAtIndex(
|
||||
viewData.length - 1 - HEADER_OFFSET, TNodeType.Element, textRNode, null, null);
|
||||
localPreviousTNode = appendI18nNode(textTNode, localParentTNode, localPreviousTNode);
|
||||
resetComponentState();
|
||||
break;
|
||||
case I18nInstructions.CloseNode:
|
||||
|
@ -368,15 +364,18 @@ export function i18nApply(startIndex: number, instructions: I18nInstruction[]):
|
|||
ngDevMode.rendererRemoveNode++;
|
||||
}
|
||||
const removeIndex = instruction & I18nInstructions.IndexMask;
|
||||
const removedNode: LNode|LContainerNode = load(removeIndex);
|
||||
const removedNode: LNode|LContainerNode = loadElement(removeIndex);
|
||||
const removedTNode = getTNode(removeIndex);
|
||||
removeChild(removedTNode, removedNode.native || null, viewData);
|
||||
|
||||
// For template containers we also need to remove their `ViewContainerRef` from the DOM
|
||||
if (removedTNode.type === TNodeType.Container && removedNode.dynamicLContainerNode) {
|
||||
removeChild(removedTNode, removedNode.dynamicLContainerNode.native || null, viewData);
|
||||
const slotValue = load(removeIndex) as LNode | LContainer | StylingContext;
|
||||
if (isLContainer(slotValue)) {
|
||||
const lContainer = slotValue as LContainer;
|
||||
if (removedTNode.type !== TNodeType.Container) {
|
||||
removeChild(removedTNode, lContainer[NATIVE] || null, viewData);
|
||||
}
|
||||
removedTNode.detached = true;
|
||||
removedNode.dynamicLContainerNode.data[RENDER_PARENT] = null;
|
||||
lContainer[RENDER_PARENT] = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import {assertDefined, assertEqual, assertLessThan, assertNotEqual} from './asse
|
|||
import {attachPatchData, getLElementFromComponent, readElementValue, readPatchedLViewData} from './context_discovery';
|
||||
import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors';
|
||||
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
|
||||
import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
|
||||
import {ACTIVE_INDEX, HOST_NATIVE, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
|
||||
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
||||
import {INJECTOR_SIZE} from './interfaces/injector';
|
||||
import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LElementContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode} from './interfaces/node';
|
||||
|
@ -29,7 +29,7 @@ import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
|||
import {appendChild, appendProjectedNode, createTextNode, findComponentView, getHostElementNode, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation';
|
||||
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
||||
import {allocStylingContext, createStylingContextTemplate, renderStyling as renderElementStyles, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings';
|
||||
import {assertDataInRangeInternal, getLNode, getRootView, isContentQueryHost, isDifferent, loadElementInternal, loadInternal, stringify} from './util';
|
||||
import {assertDataInRangeInternal, getLNode, getRootView, isContentQueryHost, isDifferent, isLContainer, loadElementInternal, loadInternal, stringify} from './util';
|
||||
|
||||
|
||||
/**
|
||||
|
@ -430,7 +430,7 @@ export function createLViewData<T>(
|
|||
export function createLNodeObject(
|
||||
type: TNodeType, native: RText | RElement | RComment | null, state: any): LElementNode&
|
||||
LTextNode&LViewNode&LContainerNode&LProjectionNode {
|
||||
return {native: native as any, data: state, dynamicLContainerNode: null};
|
||||
return {native: native as any, data: state};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -453,7 +453,7 @@ export function createNodeAtIndex(
|
|||
lViewData: LViewData): TViewNode;
|
||||
export function createNodeAtIndex(
|
||||
index: number, type: TNodeType.Container, native: RComment, name: string | null,
|
||||
attrs: TAttributes | null, lContainer: LContainer): TContainerNode;
|
||||
attrs: TAttributes | null, data: null): TContainerNode;
|
||||
export function createNodeAtIndex(
|
||||
index: number, type: TNodeType.Projection, native: null, name: null, attrs: TAttributes | null,
|
||||
lProjection: null): TProjectionNode;
|
||||
|
@ -935,9 +935,11 @@ function cacheMatchingDirectivesForNode(
|
|||
function generateExpandoBlock(tNode: TNode, matches: CurrentMatchesList | null): void {
|
||||
const directiveCount = matches ? matches.length / 2 : 0;
|
||||
const elementIndex = -(tNode.index - HEADER_OFFSET);
|
||||
if (directiveCount > 0) {
|
||||
(tView.expandoInstructions || (tView.expandoInstructions = [
|
||||
])).push(elementIndex, directiveCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On the first template pass, we need to reserve space for host binding values
|
||||
|
@ -1418,7 +1420,7 @@ export function elementAttribute(
|
|||
export function elementProperty<T>(
|
||||
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn): void {
|
||||
if (value === NO_CHANGE) return;
|
||||
const node = loadElement(index) as LElementNode;
|
||||
const node = loadElement(index) as LElementNode | LContainerNode | LElementContainerNode;
|
||||
const tNode = getTNode(index);
|
||||
// if tNode.inputs is undefined, a listener has created outputs, but inputs haven't
|
||||
// yet been checked
|
||||
|
@ -1431,12 +1433,12 @@ export function elementProperty<T>(
|
|||
let dataValue: PropertyAliasValue|undefined;
|
||||
if (inputData && (dataValue = inputData[propName])) {
|
||||
setInputsForProperty(dataValue, value);
|
||||
markDirtyIfOnPush(node);
|
||||
} else {
|
||||
if (tNode.type === TNodeType.Element) markDirtyIfOnPush(node as LElementNode);
|
||||
} else if (tNode.type === TNodeType.Element) {
|
||||
// It is assumed that the sanitizer is only added when the compiler determines that the property
|
||||
// is risky, so sanitization can be done without further checks.
|
||||
value = sanitizer != null ? (sanitizer(value) as any) : value;
|
||||
const native = node.native;
|
||||
const native = node.native as RElement;
|
||||
ngDevMode && ngDevMode.rendererSetProperty++;
|
||||
isProceduralRenderer(renderer) ? renderer.setProperty(native, propName, value) :
|
||||
(native.setProperty ? native.setProperty(propName, value) :
|
||||
|
@ -1639,16 +1641,22 @@ export function elementStyling<T>(
|
|||
* @param index Index of the style allocation. See: `elementStyling`.
|
||||
*/
|
||||
function getStylingContext(index: number): StylingContext {
|
||||
let stylingContext = load<StylingContext>(index);
|
||||
if (!Array.isArray(stylingContext)) {
|
||||
const lElement = stylingContext as any as LElementNode;
|
||||
const tNode = getTNode(index);
|
||||
ngDevMode &&
|
||||
assertDefined(tNode.stylingTemplate, 'getStylingContext() called before elementStyling()');
|
||||
stylingContext = viewData[index + HEADER_OFFSET] =
|
||||
allocStylingContext(lElement, tNode.stylingTemplate !);
|
||||
let slotValue = viewData[index + HEADER_OFFSET];
|
||||
|
||||
if (isLContainer(slotValue)) {
|
||||
const lContainer = slotValue;
|
||||
slotValue = lContainer[HOST_NATIVE];
|
||||
if (!Array.isArray(slotValue)) {
|
||||
return lContainer[HOST_NATIVE] =
|
||||
allocStylingContext(slotValue, getTNode(index).stylingTemplate !);
|
||||
}
|
||||
return stylingContext;
|
||||
} else if (!Array.isArray(slotValue)) {
|
||||
// This is a regular ElementNode
|
||||
return viewData[index + HEADER_OFFSET] =
|
||||
allocStylingContext(slotValue, getTNode(index).stylingTemplate !);
|
||||
}
|
||||
|
||||
return slotValue as StylingContext;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1969,19 +1977,26 @@ function generateInitialInputs(
|
|||
/**
|
||||
* Creates a LContainer, either from a container instruction, or for a ViewContainerRef.
|
||||
*
|
||||
* @param hostLNode The host node for the LContainer
|
||||
* @param hostTNode The host TNode for the LContainer
|
||||
* @param currentView The parent view of the LContainer
|
||||
* @param native The native comment element
|
||||
* @param isForViewContainerRef Optional a flag indicating the ViewContainerRef case
|
||||
* @returns LContainer
|
||||
*/
|
||||
export function createLContainer(
|
||||
currentView: LViewData, isForViewContainerRef?: boolean): LContainer {
|
||||
hostLNode: LElementNode | LContainerNode | LElementContainerNode,
|
||||
hostTNode: TElementNode | TContainerNode | TElementContainerNode, currentView: LViewData,
|
||||
native: RComment, isForViewContainerRef?: boolean): LContainer {
|
||||
return [
|
||||
isForViewContainerRef ? null : 0, // active index
|
||||
isForViewContainerRef ? -1 : 0, // active index
|
||||
currentView, // parent
|
||||
null, // next
|
||||
null, // queries
|
||||
hostLNode, // host native
|
||||
native, // native
|
||||
[], // views
|
||||
null // renderParent, set after node creation
|
||||
getRenderParent(hostTNode, currentView) // renderParent
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -2042,12 +2057,13 @@ function containerInternal(
|
|||
viewData[BINDING_INDEX], tView.bindingStartIndex,
|
||||
'container nodes should be created before any bindings');
|
||||
|
||||
const lContainer = createLContainer(viewData);
|
||||
ngDevMode && ngDevMode.rendererCreateComment++;
|
||||
const adjustedIndex = index + HEADER_OFFSET;
|
||||
const comment = renderer.createComment(ngDevMode ? 'container' : '');
|
||||
const tNode = createNodeAtIndex(index, TNodeType.Container, comment, tagName, attrs, lContainer);
|
||||
ngDevMode && ngDevMode.rendererCreateComment++;
|
||||
const tNode = createNodeAtIndex(index, TNodeType.Container, comment, tagName, attrs, null);
|
||||
const lContainer = viewData[adjustedIndex] =
|
||||
createLContainer(viewData[adjustedIndex], tNode, viewData, comment);
|
||||
|
||||
lContainer[RENDER_PARENT] = getRenderParent(tNode, viewData);
|
||||
appendChild(comment, tNode, viewData);
|
||||
|
||||
// Containers are added to the current view tree instead of their embedded views
|
||||
|
@ -2073,8 +2089,8 @@ export function containerRefreshStart(index: number): void {
|
|||
|
||||
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Container);
|
||||
isParent = true;
|
||||
// Inline containers cannot have style bindings, so we can read the value directly
|
||||
(viewData[previousOrParentTNode.index] as LContainerNode).data[ACTIVE_INDEX] = 0;
|
||||
|
||||
viewData[index + HEADER_OFFSET][ACTIVE_INDEX] = 0;
|
||||
|
||||
if (!checkNoChangesMode) {
|
||||
// We need to execute init hooks here so ngOnInit hooks are called in top level views
|
||||
|
@ -2099,8 +2115,7 @@ export function containerRefreshEnd(): void {
|
|||
|
||||
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Container);
|
||||
|
||||
// Inline containers cannot have style bindings, so we can read the value directly
|
||||
const lContainer = viewData[previousOrParentTNode.index].data;
|
||||
const lContainer = viewData[previousOrParentTNode.index];
|
||||
const nextIndex = lContainer[ACTIVE_INDEX];
|
||||
|
||||
// remove extra views at the end of the container
|
||||
|
@ -2118,7 +2133,7 @@ function refreshDynamicEmbeddedViews(lViewData: LViewData) {
|
|||
// Note: current can be an LViewData or an LContainer instance, but here we are only interested
|
||||
// in LContainer. We can tell it's an LContainer because its length is less than the LViewData
|
||||
// header.
|
||||
if (current.length < HEADER_OFFSET && current[ACTIVE_INDEX] === null) {
|
||||
if (current.length < HEADER_OFFSET && current[ACTIVE_INDEX] === -1) {
|
||||
const container = current as LContainer;
|
||||
for (let i = 0; i < container[VIEWS].length; i++) {
|
||||
const dynamicViewData = container[VIEWS][i];
|
||||
|
@ -2175,12 +2190,10 @@ export function embeddedViewStart(viewBlockId: number, consts: number, vars: num
|
|||
const containerTNode = previousOrParentTNode.type === TNodeType.View ?
|
||||
previousOrParentTNode.parent ! :
|
||||
previousOrParentTNode;
|
||||
// Inline containers cannot have style bindings, so we can read the value directly
|
||||
const container = viewData[containerTNode.index] as LContainerNode;
|
||||
const lContainer = viewData[containerTNode.index] as LContainer;
|
||||
const currentView = viewData;
|
||||
|
||||
ngDevMode && assertNodeType(containerTNode, TNodeType.Container);
|
||||
const lContainer = container.data;
|
||||
let viewToRender = scanForView(
|
||||
lContainer, containerTNode as TContainerNode, lContainer[ACTIVE_INDEX] !, viewBlockId);
|
||||
|
||||
|
@ -2201,7 +2214,7 @@ export function embeddedViewStart(viewBlockId: number, consts: number, vars: num
|
|||
createNodeAtIndex(viewBlockId, TNodeType.View, null, null, null, viewToRender);
|
||||
enterView(viewToRender, viewToRender[TVIEW].node);
|
||||
}
|
||||
if (container) {
|
||||
if (lContainer) {
|
||||
if (creationMode) {
|
||||
// it is a new view, insert it into collection of views for a given container
|
||||
insertView(viewToRender, lContainer, currentView, lContainer[ACTIVE_INDEX] !, -1);
|
||||
|
@ -2409,12 +2422,10 @@ export function projection(nodeIndex: number, selectorIndex: number = 0, attrs?:
|
|||
continue;
|
||||
}
|
||||
} else {
|
||||
const lNode = projectedView[nodeToProject.index] as LTextNode | LElementNode | LContainerNode;
|
||||
// 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(lNode, nodeToProject, tProjectionNode, viewData, projectedView);
|
||||
appendProjectedNode(nodeToProject, tProjectionNode, viewData, projectedView);
|
||||
}
|
||||
|
||||
// If we are finished with a list of re-projected nodes, we need to get
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {LElementNode} from './node';
|
||||
import {LContainerNode, LElementContainerNode, LElementNode} from './node';
|
||||
import {LQueries} from './query';
|
||||
import {RComment} from './renderer';
|
||||
import {StylingContext} from './styling';
|
||||
import {LViewData, NEXT, PARENT, QUERIES} from './view';
|
||||
|
||||
/**
|
||||
|
@ -18,8 +20,10 @@ import {LViewData, NEXT, PARENT, QUERIES} from './view';
|
|||
export const ACTIVE_INDEX = 0;
|
||||
// PARENT, NEXT, and QUERIES are indices 1, 2, and 3.
|
||||
// As we already have these constants in LViewData, we don't need to re-create them.
|
||||
export const VIEWS = 4;
|
||||
export const RENDER_PARENT = 5;
|
||||
export const HOST_NATIVE = 4;
|
||||
export const NATIVE = 5;
|
||||
export const VIEWS = 6;
|
||||
export const RENDER_PARENT = 7;
|
||||
|
||||
/**
|
||||
* The state associated with an LContainerNode.
|
||||
|
@ -37,7 +41,7 @@ export interface LContainer extends Array<any> {
|
|||
* it is set to null to identify this scenario, as indices are "absolute" in that case,
|
||||
* i.e. provided directly by the user of the ViewContainerRef API.
|
||||
*/
|
||||
[ACTIVE_INDEX]: number|null;
|
||||
[ACTIVE_INDEX]: number;
|
||||
|
||||
/**
|
||||
* Access to the parent view is necessary so we can propagate back
|
||||
|
@ -57,6 +61,13 @@ export interface LContainer extends Array<any> {
|
|||
*/
|
||||
[QUERIES]: LQueries|null;
|
||||
|
||||
/** The host node of this LContainer. */
|
||||
// TODO: Should contain just the native element once LNode is removed.
|
||||
[HOST_NATIVE]: LElementNode|LContainerNode|LElementContainerNode|StylingContext;
|
||||
|
||||
/** The comment element that serves as an anchor for this LContainer. */
|
||||
[NATIVE]: RComment;
|
||||
|
||||
/**
|
||||
* A list of the container's currently active child views. Views will be inserted
|
||||
* here as they are added and spliced from here when they are removed. We need
|
||||
|
|
|
@ -71,18 +71,12 @@ export interface LNode {
|
|||
readonly native: RComment|RElement|RText|null;
|
||||
|
||||
/**
|
||||
* If regular LElementNode, LTextNode, and LProjectionNode then `data` will be null.
|
||||
* If regular LElementNode, LTextNode, LContainerNode, and LProjectionNode then `data` will be
|
||||
* null.
|
||||
* If LElementNode with component, then `data` contains LViewData.
|
||||
* If LViewNode, then `data` contains the LViewData.
|
||||
* If LContainerNode, then `data` contains LContainer.
|
||||
*/
|
||||
readonly data: LViewData|LContainer|null;
|
||||
|
||||
/**
|
||||
* A pointer to an LContainerNode created by directives requesting ViewContainerRef
|
||||
*/
|
||||
// TODO(kara): Remove when removing LNodes
|
||||
dynamicLContainerNode: LContainerNode|null;
|
||||
readonly data: LViewData|null;
|
||||
}
|
||||
|
||||
|
||||
|
@ -107,14 +101,12 @@ export interface LTextNode extends LNode {
|
|||
/** The text node associated with this node. */
|
||||
native: RText;
|
||||
readonly data: null;
|
||||
dynamicLContainerNode: null;
|
||||
}
|
||||
|
||||
/** Abstract node which contains root nodes of a view. */
|
||||
export interface LViewNode extends LNode {
|
||||
readonly native: null;
|
||||
readonly data: LViewData;
|
||||
dynamicLContainerNode: null;
|
||||
}
|
||||
|
||||
/** Abstract node container which contains other views. */
|
||||
|
@ -127,14 +119,13 @@ export interface LContainerNode extends LNode {
|
|||
* until the parent view is processed.
|
||||
*/
|
||||
native: RComment;
|
||||
readonly data: LContainer;
|
||||
readonly data: null;
|
||||
}
|
||||
|
||||
|
||||
export interface LProjectionNode extends LNode {
|
||||
readonly native: null;
|
||||
readonly data: null;
|
||||
dynamicLContainerNode: null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,13 +9,14 @@
|
|||
import {assertDefined} from './assert';
|
||||
import {attachPatchData, readElementValue} from './context_discovery';
|
||||
import {callHooks} from './hooks';
|
||||
import {LContainer, RENDER_PARENT, VIEWS, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
|
||||
import {HOST_NATIVE, LContainer, NATIVE, RENDER_PARENT, VIEWS, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
|
||||
import {LContainerNode, LElementContainerNode, LElementNode, LTextNode, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
|
||||
import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
|
||||
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
|
||||
import {StylingIndex} from './interfaces/styling';
|
||||
import {CLEANUP, CONTAINER_INDEX, FLAGS, HEADER_OFFSET, HOST_NODE, HookData, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
|
||||
import {assertNodeType} from './node_assert';
|
||||
import {getLNode, stringify} from './util';
|
||||
import {getLNode, isLContainer, stringify} from './util';
|
||||
|
||||
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
|
||||
|
||||
|
@ -37,17 +38,15 @@ export function getHostElementNode(currentView: LViewData): LElementNode|null {
|
|||
null;
|
||||
}
|
||||
|
||||
export function getContainerNode(tNode: TNode, embeddedView: LViewData): LContainerNode|null {
|
||||
export function getLContainer(tNode: TViewNode, embeddedView: LViewData): LContainer|null {
|
||||
if (tNode.index === -1) {
|
||||
// This is a dynamically created view inside a dynamic container.
|
||||
// If the host index is -1, the view has not yet been inserted, so it has no parent.
|
||||
const containerHostIndex = embeddedView[CONTAINER_INDEX];
|
||||
return containerHostIndex > -1 ?
|
||||
embeddedView[PARENT] ![containerHostIndex].dynamicLContainerNode :
|
||||
null;
|
||||
return containerHostIndex > -1 ? embeddedView[PARENT] ![containerHostIndex] : null;
|
||||
} else {
|
||||
// This is a inline view node (e.g. embeddedViewStart)
|
||||
return getParentLNode(tNode, embeddedView[PARENT] !) as LContainerNode;
|
||||
return embeddedView[PARENT] ![tNode.parent !.index] as LContainer;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,8 +56,8 @@ export function getContainerNode(tNode: TNode, embeddedView: LViewData): LContai
|
|||
* Might be null if a view is not yet attached to any container.
|
||||
*/
|
||||
export function getContainerRenderParent(tViewNode: TViewNode, view: LViewData): LElementNode|null {
|
||||
const container = getContainerNode(tViewNode, view);
|
||||
return container ? container.data[RENDER_PARENT] : null;
|
||||
const container = getLContainer(tViewNode, view);
|
||||
return container ? container[RENDER_PARENT] : null;
|
||||
}
|
||||
|
||||
const enum WalkTNodeTreeAction {
|
||||
|
@ -107,29 +106,24 @@ function walkTNodeTree(
|
|||
if (tNode.type === TNodeType.Element) {
|
||||
const elementNode = getLNode(tNode, currentView);
|
||||
executeNodeAction(action, renderer, parent, elementNode.native !, beforeNode);
|
||||
if (elementNode.dynamicLContainerNode) {
|
||||
executeNodeAction(
|
||||
action, renderer, parent, elementNode.dynamicLContainerNode.native !, beforeNode);
|
||||
const nodeOrContainer = currentView[tNode.index];
|
||||
if (isLContainer(nodeOrContainer)) {
|
||||
// This element has an LContainer, and its comment needs to be handled
|
||||
executeNodeAction(action, renderer, parent, nodeOrContainer[NATIVE], beforeNode);
|
||||
}
|
||||
} else if (tNode.type === TNodeType.Container) {
|
||||
const lContainerNode: LContainerNode = currentView ![tNode.index] as LContainerNode;
|
||||
executeNodeAction(action, renderer, parent, lContainerNode.native !, beforeNode);
|
||||
const childContainerData: LContainer = lContainerNode.dynamicLContainerNode ?
|
||||
lContainerNode.dynamicLContainerNode.data :
|
||||
lContainerNode.data;
|
||||
if (renderParentNode) {
|
||||
childContainerData[RENDER_PARENT] = renderParentNode;
|
||||
}
|
||||
const lContainer = currentView ![tNode.index] as LContainer;
|
||||
executeNodeAction(action, renderer, parent, lContainer[NATIVE], beforeNode);
|
||||
|
||||
if (childContainerData[VIEWS].length) {
|
||||
currentView = childContainerData[VIEWS][0];
|
||||
if (renderParentNode) lContainer[RENDER_PARENT] = renderParentNode;
|
||||
|
||||
if (lContainer[VIEWS].length) {
|
||||
currentView = lContainer[VIEWS][0];
|
||||
nextTNode = currentView[TVIEW].node;
|
||||
|
||||
// When the walker enters a container, then the beforeNode has to become the local native
|
||||
// comment node.
|
||||
beforeNode = lContainerNode.dynamicLContainerNode ?
|
||||
lContainerNode.dynamicLContainerNode.native :
|
||||
lContainerNode.native;
|
||||
beforeNode = lContainer[NATIVE];
|
||||
}
|
||||
} else if (tNode.type === TNodeType.Projection) {
|
||||
const componentView = findComponentView(currentView !);
|
||||
|
@ -174,7 +168,7 @@ function walkTNodeTree(
|
|||
// When exiting a container, the beforeNode must be restored to the previous value
|
||||
if (tNode.type === TNodeType.Container) {
|
||||
currentView = currentView[PARENT] !;
|
||||
beforeNode = currentView[tNode.index].native;
|
||||
beforeNode = currentView[tNode.index][NATIVE];
|
||||
}
|
||||
|
||||
if (tNode.type === TNodeType.View && currentView[NEXT]) {
|
||||
|
@ -402,11 +396,14 @@ export function removeView(
|
|||
|
||||
/** Gets the child of the given LViewData */
|
||||
export function getLViewChild(viewData: LViewData): LViewData|LContainer|null {
|
||||
if (viewData[TVIEW].childIndex === -1) return null;
|
||||
const childIndex = viewData[TVIEW].childIndex;
|
||||
if (childIndex === -1) return null;
|
||||
|
||||
const hostNode: LElementNode|LContainerNode = viewData[viewData[TVIEW].childIndex];
|
||||
const value: LElementNode|LContainerNode|LContainer = viewData[childIndex];
|
||||
|
||||
return hostNode.data ? hostNode.data : (hostNode.dynamicLContainerNode as LContainerNode).data;
|
||||
// If it's an array, it's an LContainer. Otherwise, it's a component node, so LViewData
|
||||
// is stored in data.
|
||||
return Array.isArray(value) ? value : value.data;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -444,7 +441,7 @@ export function getParentState(state: LViewData | LContainer, rootView: LViewDat
|
|||
tNode.type === TNodeType.View) {
|
||||
// if it's an embedded view, the state needs to go up to the container, in case the
|
||||
// container has a next
|
||||
return getContainerNode(tNode, state as LViewData) !.data as any;
|
||||
return getLContainer(tNode as TViewNode, state as LViewData) as LContainer;
|
||||
} else {
|
||||
// otherwise, use parent view for containers or component views
|
||||
return state[PARENT] === rootView ? null : state[PARENT];
|
||||
|
@ -457,7 +454,7 @@ export function getParentState(state: LViewData | LContainer, rootView: LViewDat
|
|||
* @param view The LViewData to clean up
|
||||
*/
|
||||
function cleanUpView(viewOrContainer: LViewData | LContainer): void {
|
||||
if ((viewOrContainer as LViewData)[TVIEW]) {
|
||||
if ((viewOrContainer as LViewData).length >= HEADER_OFFSET) {
|
||||
const view = viewOrContainer as LViewData;
|
||||
removeListeners(view);
|
||||
executeOnDestroys(view);
|
||||
|
@ -552,8 +549,8 @@ function canInsertNativeChildOfElement(tNode: TNode): boolean {
|
|||
*/
|
||||
function canInsertNativeChildOfView(viewTNode: TViewNode, view: LViewData): boolean {
|
||||
// Because we are inserting into a `View` the `View` may be disconnected.
|
||||
const container = getContainerNode(viewTNode, view) !;
|
||||
if (container == null || container.data[RENDER_PARENT] == null) {
|
||||
const container = getLContainer(viewTNode, view) !;
|
||||
if (container == null || container[RENDER_PARENT] == null) {
|
||||
// The `View` is not inserted into a `Container` or the parent `Container`
|
||||
// itself is disconnected. So we have to delay.
|
||||
return false;
|
||||
|
@ -634,12 +631,13 @@ export function appendChild(
|
|||
const parentTNode: TNode = childTNode.parent || currentView[HOST_NODE] !;
|
||||
|
||||
if (parentTNode.type === TNodeType.View) {
|
||||
const container = getContainerNode(parentTNode, currentView) as LContainerNode;
|
||||
const renderParent = container.data[RENDER_PARENT];
|
||||
const views = container.data[VIEWS];
|
||||
const lContainer = getLContainer(parentTNode as TViewNode, currentView) as LContainer;
|
||||
const renderParent = lContainer[RENDER_PARENT];
|
||||
const views = lContainer[VIEWS];
|
||||
const index = views.indexOf(currentView);
|
||||
nativeInsertBefore(
|
||||
renderer, renderParent !.native, childEl, getBeforeNodeForView(index, views, container));
|
||||
renderer, renderParent !.native, childEl,
|
||||
getBeforeNodeForView(index, views, lContainer[NATIVE]));
|
||||
} else if (parentTNode.type === TNodeType.ElementContainer) {
|
||||
let elementContainer = getHighestElementContainer(childTNode);
|
||||
let node: LElementNode = getRenderParent(elementContainer, currentView) !;
|
||||
|
@ -666,13 +664,13 @@ function getHighestElementContainer(ngContainer: TNode): TNode {
|
|||
return ngContainer;
|
||||
}
|
||||
|
||||
export function getBeforeNodeForView(index: number, views: LViewData[], container: LContainerNode) {
|
||||
export function getBeforeNodeForView(index: number, views: LViewData[], containerNative: RComment) {
|
||||
if (index + 1 < views.length) {
|
||||
const view = views[index + 1] as LViewData;
|
||||
const viewTNode = view[HOST_NODE] as TViewNode;
|
||||
return viewTNode.child ? getLNode(viewTNode.child, view).native : container.native;
|
||||
return viewTNode.child ? getLNode(viewTNode.child, view).native : containerNative;
|
||||
} else {
|
||||
return container.native;
|
||||
return containerNative;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -700,49 +698,48 @@ export function removeChild(tNode: TNode, child: RNode | null, currentView: LVie
|
|||
* 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 projectedLNode The node to process
|
||||
* @param parentNode The last parent element to be processed
|
||||
* @param tProjectionNode
|
||||
* @param projectedTNode The TNode to be projected
|
||||
* @param tProjectionNode The projection (ng-content) TNode
|
||||
* @param currentView Current LView
|
||||
* @param projectionView Projection view
|
||||
* @param projectionView Projection view (view above current)
|
||||
*/
|
||||
export function appendProjectedNode(
|
||||
projectedLNode: LElementNode | LElementContainerNode | LTextNode | LContainerNode,
|
||||
projectedTNode: TNode, tProjectionNode: TNode, currentView: LViewData,
|
||||
projectionView: LViewData): void {
|
||||
appendChild(projectedLNode.native, tProjectionNode, currentView);
|
||||
const native = getLNode(projectedTNode, projectionView).native;
|
||||
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(projectedLNode.native, projectionView);
|
||||
attachPatchData(native, projectionView);
|
||||
|
||||
const renderParent = getRenderParent(tProjectionNode, currentView);
|
||||
|
||||
const nodeOrContainer = projectionView[projectedTNode.index];
|
||||
if (projectedTNode.type === TNodeType.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.
|
||||
const lContainer = (projectedLNode as LContainerNode).data;
|
||||
lContainer[RENDER_PARENT] = renderParent;
|
||||
const views = lContainer[VIEWS];
|
||||
nodeOrContainer[RENDER_PARENT] = renderParent;
|
||||
const views = nodeOrContainer[VIEWS];
|
||||
for (let i = 0; i < views.length; i++) {
|
||||
addRemoveViewFromContainer(views[i], true, projectedLNode.native);
|
||||
addRemoveViewFromContainer(views[i], true, nodeOrContainer[NATIVE]);
|
||||
}
|
||||
} else if (projectedTNode.type === TNodeType.ElementContainer) {
|
||||
} else {
|
||||
if (projectedTNode.type === TNodeType.ElementContainer) {
|
||||
let ngContainerChildTNode: TNode|null = projectedTNode.child as TNode;
|
||||
while (ngContainerChildTNode) {
|
||||
let ngContainerChild = getLNode(ngContainerChildTNode, projectionView);
|
||||
appendProjectedNode(
|
||||
ngContainerChild as LElementNode | LElementContainerNode | LTextNode | LContainerNode,
|
||||
ngContainerChildTNode, tProjectionNode, currentView, projectionView);
|
||||
appendProjectedNode(ngContainerChildTNode, tProjectionNode, currentView, projectionView);
|
||||
ngContainerChildTNode = ngContainerChildTNode.next;
|
||||
}
|
||||
}
|
||||
if (projectedLNode.dynamicLContainerNode) {
|
||||
projectedLNode.dynamicLContainerNode.data[RENDER_PARENT] = renderParent;
|
||||
appendChild(projectedLNode.dynamicLContainerNode.native, tProjectionNode, currentView);
|
||||
|
||||
if (isLContainer(nodeOrContainer)) {
|
||||
nodeOrContainer[RENDER_PARENT] = renderParent;
|
||||
appendChild(nodeOrContainer[NATIVE], tProjectionNode, currentView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,12 @@ import {devModeEqual} from '../change_detection/change_detection_util';
|
|||
|
||||
import {assertDefined, assertLessThan} from './assert';
|
||||
import {readElementValue, readPatchedLViewData} from './context_discovery';
|
||||
import {LContainerNode, LElementContainerNode, LElementNode, TNode, TNodeFlags} from './interfaces/node';
|
||||
import {ACTIVE_INDEX, LContainer} from './interfaces/container';
|
||||
import {LContainerNode, LElementContainerNode, LElementNode, LNode, TNode, TNodeFlags} from './interfaces/node';
|
||||
import {StylingContext} from './interfaces/styling';
|
||||
import {CONTEXT, FLAGS, HEADER_OFFSET, LViewData, LViewFlags, PARENT, RootContext, TData, TVIEW} from './interfaces/view';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the values are different from a change detection stand point.
|
||||
*
|
||||
|
@ -103,6 +104,12 @@ export function isContentQueryHost(tNode: TNode): boolean {
|
|||
export function isComponent(tNode: TNode): boolean {
|
||||
return (tNode.flags & TNodeFlags.isComponent) === TNodeFlags.isComponent;
|
||||
}
|
||||
|
||||
export function isLContainer(value: LNode | LContainer | StylingContext): boolean {
|
||||
// Styling contexts are also arrays, but their first index contains an element node
|
||||
return Array.isArray(value) && typeof value[ACTIVE_INDEX] === 'number';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the root view from any component by walking the parent `LViewData` until
|
||||
* reaching the root `LViewData`.
|
||||
|
|
|
@ -18,17 +18,17 @@ import {Renderer2} from '../render/api';
|
|||
|
||||
import {assertDefined, assertGreaterThan, assertLessThan} from './assert';
|
||||
import {NodeInjector, getParentInjectorLocation, getParentInjectorView} from './di';
|
||||
import {_getViewData, addToViewTree, createEmbeddedViewAndNode, createLContainer, createLNodeObject, createTNode, getPreviousOrParentTNode, getRenderer, renderEmbeddedTemplate} from './instructions';
|
||||
import {LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
|
||||
import {_getViewData, addToViewTree, createEmbeddedViewAndNode, createLContainer, getPreviousOrParentTNode, getRenderer, renderEmbeddedTemplate} from './instructions';
|
||||
import {ACTIVE_INDEX, LContainer, NATIVE, RENDER_PARENT, VIEWS} from './interfaces/container';
|
||||
import {RenderFlags} from './interfaces/definition';
|
||||
import {InjectorLocationFlags} from './interfaces/injector';
|
||||
import {LContainerNode, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode} from './interfaces/node';
|
||||
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode} from './interfaces/node';
|
||||
import {LQueries} from './interfaces/query';
|
||||
import {RComment, RElement, Renderer3, isProceduralRenderer} from './interfaces/renderer';
|
||||
import {CONTEXT, HOST_NODE, LViewData, QUERIES, RENDERER, TVIEW, TView} from './interfaces/view';
|
||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||
import {addRemoveViewFromContainer, appendChild, detachView, findComponentView, getBeforeNodeForView, getRenderParent, insertView, removeView} from './node_manipulation';
|
||||
import {getLNode, isComponent} from './util';
|
||||
import {getLNode, isComponent, isLContainer} from './util';
|
||||
import {ViewRef} from './view_ref';
|
||||
|
||||
|
||||
|
@ -122,13 +122,12 @@ export function createTemplateRef<T>(
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
const hostNode = getLNode(hostTNode, hostView);
|
||||
const hostContainer: LContainer = hostView[hostTNode.index];
|
||||
ngDevMode && assertNodeType(hostTNode, TNodeType.Container);
|
||||
ngDevMode && assertDefined(hostTNode.tViews, 'TView must be allocated');
|
||||
return new R3TemplateRef(
|
||||
hostView, createElementRef(ElementRefToken, hostTNode, hostView), hostTNode.tViews as TView,
|
||||
getRenderer(), hostNode.data ![QUERIES], hostTNode.injectorIndex);
|
||||
getRenderer(), hostContainer[QUERIES], hostTNode.injectorIndex);
|
||||
}
|
||||
|
||||
let R3ViewContainerRef: {
|
||||
|
@ -241,8 +240,8 @@ export function createContainerRef(
|
|||
|
||||
insertView(lView, this._lContainer, this._hostView, adjustedIdx, this._hostTNode.index);
|
||||
|
||||
const container = this._getHostNode().dynamicLContainerNode !;
|
||||
const beforeNode = getBeforeNodeForView(adjustedIdx, this._lContainer[VIEWS], container);
|
||||
const beforeNode =
|
||||
getBeforeNodeForView(adjustedIdx, this._lContainer[VIEWS], this._lContainer[NATIVE]);
|
||||
addRemoveViewFromContainer(lView, true, beforeNode);
|
||||
|
||||
(viewRef as ViewRef<any>).attachToViewContainerRef(this);
|
||||
|
@ -283,25 +282,27 @@ export function createContainerRef(
|
|||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
private _getHostNode() { return getLNode(this._hostTNode, this._hostView); }
|
||||
};
|
||||
}
|
||||
|
||||
const hostLNode = getLNode(hostTNode, hostView);
|
||||
ngDevMode && assertNodeOfPossibleTypes(
|
||||
hostTNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer);
|
||||
|
||||
const lContainer = createLContainer(hostView, true);
|
||||
let lContainer: LContainer;
|
||||
const slotValue = hostView[hostTNode.index];
|
||||
if (isLContainer(slotValue)) {
|
||||
// If the host is a container, we don't need to create a new LContainer
|
||||
lContainer = slotValue;
|
||||
lContainer[ACTIVE_INDEX] = -1;
|
||||
} else {
|
||||
const comment = hostView[RENDERER].createComment(ngDevMode ? 'container' : '');
|
||||
const lContainerNode: LContainerNode =
|
||||
createLNodeObject(TNodeType.Container, comment, lContainer);
|
||||
|
||||
lContainer[RENDER_PARENT] = getRenderParent(hostTNode, hostView);
|
||||
ngDevMode && ngDevMode.rendererCreateComment++;
|
||||
hostView[hostTNode.index] = lContainer =
|
||||
createLContainer(slotValue, hostTNode, hostView, comment, true);
|
||||
|
||||
appendChild(comment, hostTNode, hostView);
|
||||
hostLNode.dynamicLContainerNode = lContainerNode;
|
||||
addToViewTree(hostView, hostTNode.index as number, lContainer);
|
||||
}
|
||||
|
||||
return new R3ViewContainerRef(lContainer, hostTNode, hostView);
|
||||
}
|
||||
|
|
|
@ -68,6 +68,9 @@
|
|||
{
|
||||
"name": "HEADER_OFFSET"
|
||||
},
|
||||
{
|
||||
"name": "HOST_NATIVE"
|
||||
},
|
||||
{
|
||||
"name": "HOST_NODE"
|
||||
},
|
||||
|
@ -86,6 +89,9 @@
|
|||
{
|
||||
"name": "MONKEY_PATCH_KEY_NAME"
|
||||
},
|
||||
{
|
||||
"name": "NATIVE"
|
||||
},
|
||||
{
|
||||
"name": "NEXT"
|
||||
},
|
||||
|
@ -545,9 +551,6 @@
|
|||
{
|
||||
"name": "getComponentDef"
|
||||
},
|
||||
{
|
||||
"name": "getContainerNode"
|
||||
},
|
||||
{
|
||||
"name": "getContainerRenderParent"
|
||||
},
|
||||
|
@ -584,6 +587,9 @@
|
|||
{
|
||||
"name": "getInjectorIndex"
|
||||
},
|
||||
{
|
||||
"name": "getLContainer"
|
||||
},
|
||||
{
|
||||
"name": "getLElementFromComponent"
|
||||
},
|
||||
|
@ -755,6 +761,9 @@
|
|||
{
|
||||
"name": "isJsObject"
|
||||
},
|
||||
{
|
||||
"name": "isLContainer"
|
||||
},
|
||||
{
|
||||
"name": "isListLikeIterable"
|
||||
},
|
||||
|
@ -782,9 +791,6 @@
|
|||
{
|
||||
"name": "listener"
|
||||
},
|
||||
{
|
||||
"name": "load"
|
||||
},
|
||||
{
|
||||
"name": "loadElement"
|
||||
},
|
||||
|
|
|
@ -38,6 +38,9 @@
|
|||
{
|
||||
"name": "HEADER_OFFSET"
|
||||
},
|
||||
{
|
||||
"name": "HOST_NATIVE"
|
||||
},
|
||||
{
|
||||
"name": "HOST_NODE"
|
||||
},
|
||||
|
@ -50,6 +53,9 @@
|
|||
{
|
||||
"name": "MONKEY_PATCH_KEY_NAME"
|
||||
},
|
||||
{
|
||||
"name": "NATIVE"
|
||||
},
|
||||
{
|
||||
"name": "NEXT"
|
||||
},
|
||||
|
@ -224,9 +230,6 @@
|
|||
{
|
||||
"name": "getComponentDef"
|
||||
},
|
||||
{
|
||||
"name": "getContainerNode"
|
||||
},
|
||||
{
|
||||
"name": "getContainerRenderParent"
|
||||
},
|
||||
|
@ -242,6 +245,9 @@
|
|||
{
|
||||
"name": "getInjectorIndex"
|
||||
},
|
||||
{
|
||||
"name": "getLContainer"
|
||||
},
|
||||
{
|
||||
"name": "getLNode"
|
||||
},
|
||||
|
|
|
@ -53,6 +53,9 @@
|
|||
{
|
||||
"name": "HEADER_OFFSET"
|
||||
},
|
||||
{
|
||||
"name": "HOST_NATIVE"
|
||||
},
|
||||
{
|
||||
"name": "HOST_NODE"
|
||||
},
|
||||
|
@ -71,6 +74,9 @@
|
|||
{
|
||||
"name": "MONKEY_PATCH_KEY_NAME"
|
||||
},
|
||||
{
|
||||
"name": "NATIVE"
|
||||
},
|
||||
{
|
||||
"name": "NEXT"
|
||||
},
|
||||
|
@ -596,9 +602,6 @@
|
|||
{
|
||||
"name": "getComponentDef"
|
||||
},
|
||||
{
|
||||
"name": "getContainerNode"
|
||||
},
|
||||
{
|
||||
"name": "getContainerRenderParent"
|
||||
},
|
||||
|
@ -629,6 +632,9 @@
|
|||
{
|
||||
"name": "getInjectorIndex"
|
||||
},
|
||||
{
|
||||
"name": "getLContainer"
|
||||
},
|
||||
{
|
||||
"name": "getLElementFromComponent"
|
||||
},
|
||||
|
@ -776,6 +782,9 @@
|
|||
{
|
||||
"name": "isJsObject"
|
||||
},
|
||||
{
|
||||
"name": "isLContainer"
|
||||
},
|
||||
{
|
||||
"name": "isListLikeIterable"
|
||||
},
|
||||
|
@ -800,9 +809,6 @@
|
|||
{
|
||||
"name": "listener"
|
||||
},
|
||||
{
|
||||
"name": "load"
|
||||
},
|
||||
{
|
||||
"name": "loadElement"
|
||||
},
|
||||
|
|
|
@ -323,6 +323,9 @@
|
|||
{
|
||||
"name": "HOST_ATTR$1"
|
||||
},
|
||||
{
|
||||
"name": "HOST_NATIVE"
|
||||
},
|
||||
{
|
||||
"name": "HOST_NODE"
|
||||
},
|
||||
|
@ -443,6 +446,9 @@
|
|||
{
|
||||
"name": "NAMESPACE_URIS"
|
||||
},
|
||||
{
|
||||
"name": "NATIVE"
|
||||
},
|
||||
{
|
||||
"name": "NATIVE_ADD_LISTENER"
|
||||
},
|
||||
|
@ -1604,9 +1610,6 @@
|
|||
{
|
||||
"name": "getComponentDef"
|
||||
},
|
||||
{
|
||||
"name": "getContainerNode"
|
||||
},
|
||||
{
|
||||
"name": "getContainerRenderParent"
|
||||
},
|
||||
|
@ -1667,6 +1670,9 @@
|
|||
{
|
||||
"name": "getInjectorIndex"
|
||||
},
|
||||
{
|
||||
"name": "getLContainer"
|
||||
},
|
||||
{
|
||||
"name": "getLElementFromComponent"
|
||||
},
|
||||
|
@ -1976,6 +1982,9 @@
|
|||
{
|
||||
"name": "isJsObject"
|
||||
},
|
||||
{
|
||||
"name": "isLContainer"
|
||||
},
|
||||
{
|
||||
"name": "isListLikeIterable"
|
||||
},
|
||||
|
@ -2039,9 +2048,6 @@
|
|||
{
|
||||
"name": "listener"
|
||||
},
|
||||
{
|
||||
"name": "load"
|
||||
},
|
||||
{
|
||||
"name": "loadElement"
|
||||
},
|
||||
|
|
|
@ -13,7 +13,7 @@ import {defineComponent} from '../../src/render3/definition';
|
|||
import {bloomAdd, bloomHashBitOrFactory as bloomHash, getOrCreateNodeInjector, injectAttribute, injectorHasToken} from '../../src/render3/di';
|
||||
import {PublicFeature, defineDirective, directiveInject, elementProperty, load, templateRefExtractor} from '../../src/render3/index';
|
||||
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, createNodeAtIndex, createLViewData, createTView, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, projection, projectionDef, reference, template, text, textBinding, elementContainerStart, elementContainerEnd} from '../../src/render3/instructions';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, createNodeAtIndex, createLViewData, createTView, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, projection, projectionDef, reference, template, text, textBinding, elementContainerStart, elementContainerEnd, loadElement} from '../../src/render3/instructions';
|
||||
import {isProceduralRenderer} from '../../src/render3/interfaces/renderer';
|
||||
import {AttributeMarker, LContainerNode, LElementNode, TNodeType} from '../../src/render3/interfaces/node';
|
||||
|
||||
|
@ -24,6 +24,7 @@ import {getRendererFactory2} from './imported_renderer2';
|
|||
import {ComponentFixture, createComponent, createDirective, getDirectiveOnNode, renderComponent, toHtml} from './render_util';
|
||||
import {NgIf} from './common_with_def';
|
||||
import {TNODE} from '../../src/render3/interfaces/injector';
|
||||
import {LContainer, NATIVE} from '../../src/render3/interfaces/container';
|
||||
|
||||
describe('di', () => {
|
||||
describe('no dependencies', () => {
|
||||
|
@ -1136,7 +1137,7 @@ describe('di', () => {
|
|||
it('should create ElementRef with comment if requesting directive is on <ng-template> node',
|
||||
() => {
|
||||
let dir !: Directive;
|
||||
let commentNode !: LContainerNode;
|
||||
let lContainer !: LContainer;
|
||||
|
||||
class Directive {
|
||||
value: string;
|
||||
|
@ -1156,13 +1157,13 @@ describe('di', () => {
|
|||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
template(0, () => {}, 0, 0, null, ['dir', '']);
|
||||
commentNode = load(0);
|
||||
lContainer = load(0) as any;
|
||||
}
|
||||
}, 1, 0, [Directive]);
|
||||
|
||||
const fixture = new ComponentFixture(App);
|
||||
expect(dir.value).toContain('ElementRef');
|
||||
expect(dir.elementRef.nativeElement).toEqual(commentNode.native);
|
||||
expect(dir.elementRef.nativeElement).toEqual(lContainer[NATIVE]);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {RendererStyleFlags2, RendererType2} from '../../src/render/api';
|
||||
import {AttributeMarker, defineComponent, defineDirective} from '../../src/render3/index';
|
||||
import {AttributeMarker, defineComponent, defineDirective, templateRefExtractor} from '../../src/render3/index';
|
||||
|
||||
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, enableBindings, disableBindings, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, load, projection, projectionDef, reference, text, textBinding, template} from '../../src/render3/instructions';
|
||||
import {InitialStylingFlags, RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
|
@ -1349,6 +1349,7 @@ describe('render3 integration test', () => {
|
|||
describe('elementClass', () => {
|
||||
|
||||
it('should support CSS class toggle', () => {
|
||||
/** <span [class.active]="class"></span> */
|
||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'span');
|
||||
|
@ -1412,6 +1413,72 @@ describe('render3 integration test', () => {
|
|||
fixture.update();
|
||||
expect(fixture.html).toEqual('<span class="existing"></span>');
|
||||
});
|
||||
|
||||
it('should apply classes properly when nodes have LContainers', () => {
|
||||
let structuralComp !: StructuralComp;
|
||||
|
||||
class StructuralComp {
|
||||
tmp !: TemplateRef<any>;
|
||||
|
||||
constructor(public vcr: ViewContainerRef) {}
|
||||
|
||||
create() { this.vcr.createEmbeddedView(this.tmp); }
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: StructuralComp,
|
||||
selectors: [['structural-comp']],
|
||||
factory: () => structuralComp =
|
||||
new StructuralComp(directiveInject(ViewContainerRef as any)),
|
||||
inputs: {tmp: 'tmp'},
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: (rf: RenderFlags, ctx: StructuralComp) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
text(0, 'Comp Content');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function FooTemplate(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
text(0, 'Temp Content');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <ng-template #foo>
|
||||
* Content
|
||||
* </ng-template>
|
||||
* <structural-comp [class.active]="class" [tmp]="foo"></structural-comp>
|
||||
*/
|
||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
template(0, FooTemplate, 1, 0, '', null, ['foo', ''], templateRefExtractor);
|
||||
elementStart(2, 'structural-comp');
|
||||
elementStyling(['active']);
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
const foo = reference(1) as any;
|
||||
elementClassProp(2, 0, ctx.class);
|
||||
elementStylingApply(2);
|
||||
elementProperty(2, 'tmp', bind(foo));
|
||||
}
|
||||
}, 3, 1, [StructuralComp]);
|
||||
|
||||
const fixture = new ComponentFixture(App);
|
||||
fixture.component.class = true;
|
||||
fixture.update();
|
||||
expect(fixture.html)
|
||||
.toEqual('<structural-comp class="active">Comp Content</structural-comp>');
|
||||
|
||||
structuralComp.create();
|
||||
fixture.update();
|
||||
expect(fixture.html)
|
||||
.toEqual('<structural-comp class="active">Comp Content</structural-comp>Temp Content');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue