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 './ng_dev_mode';
|
||||||
|
|
||||||
import {assertEqual} from './assert';
|
import {assertEqual} from './assert';
|
||||||
|
import {ACTIVE_INDEX, HOST_NATIVE, LContainer} from './interfaces/container';
|
||||||
import {LElementNode, TNode, TNodeFlags} from './interfaces/node';
|
import {LElementNode, TNode, TNodeFlags} from './interfaces/node';
|
||||||
import {RElement} from './interfaces/renderer';
|
import {RElement} from './interfaces/renderer';
|
||||||
|
import {StylingContext, StylingIndex} from './interfaces/styling';
|
||||||
import {CONTEXT, HEADER_OFFSET, LViewData, TVIEW} from './interfaces/view';
|
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;
|
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 {assertEqual, assertLessThan} from './assert';
|
||||||
import {NO_CHANGE, _getViewData, adjustBlueprintForNewNode, bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4, createNodeAtIndex, getRenderer, getTNode, load, loadElement, resetComponentState} from './instructions';
|
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 {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 {BINDING_INDEX, HEADER_OFFSET, HOST_NODE, TVIEW} from './interfaces/view';
|
||||||
import {appendChild, createTextNode, removeChild} from './node_manipulation';
|
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.
|
* A list of flags to encode the i18n instructions used to translate the template.
|
||||||
|
@ -245,9 +247,7 @@ function generateMappingInstructions(
|
||||||
return partIndex;
|
return partIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove LNode arg when we remove dynamicContainerNode
|
function appendI18nNode(tNode: TNode, parentTNode: TNode, previousTNode: TNode): TNode {
|
||||||
function appendI18nNode(
|
|
||||||
node: LNode, tNode: TNode, parentTNode: TNode, previousTNode: TNode): TNode {
|
|
||||||
if (ngDevMode) {
|
if (ngDevMode) {
|
||||||
ngDevMode.rendererMoveNode++;
|
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
|
const slotValue = viewData[tNode.index];
|
||||||
if (tNode.type === TNodeType.Container && node.dynamicLContainerNode) {
|
if (tNode.type !== TNodeType.Container && isLContainer(slotValue)) {
|
||||||
appendChild(node.dynamicLContainerNode.native, tNode, viewData);
|
// Nodes that inject ViewContainerRef also have a comment node that should be moved
|
||||||
|
appendChild(slotValue[NATIVE], tNode, viewData);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tNode;
|
return tNode;
|
||||||
|
@ -327,20 +329,16 @@ export function i18nApply(startIndex: number, instructions: I18nInstruction[]):
|
||||||
const instruction = instructions[i] as number;
|
const instruction = instructions[i] as number;
|
||||||
switch (instruction & I18nInstructions.InstructionMask) {
|
switch (instruction & I18nInstructions.InstructionMask) {
|
||||||
case I18nInstructions.Element:
|
case I18nInstructions.Element:
|
||||||
const elementIndex = instruction & I18nInstructions.IndexMask;
|
const elementTNode = getTNode(instruction & I18nInstructions.IndexMask);
|
||||||
const element: LNode = load(elementIndex);
|
localPreviousTNode = appendI18nNode(elementTNode, localParentTNode, localPreviousTNode);
|
||||||
const elementTNode = getTNode(elementIndex);
|
|
||||||
localPreviousTNode =
|
|
||||||
appendI18nNode(element, elementTNode, localParentTNode, localPreviousTNode);
|
|
||||||
localParentTNode = elementTNode;
|
localParentTNode = elementTNode;
|
||||||
break;
|
break;
|
||||||
case I18nInstructions.Expression:
|
case I18nInstructions.Expression:
|
||||||
case I18nInstructions.TemplateRoot:
|
case I18nInstructions.TemplateRoot:
|
||||||
case I18nInstructions.Any:
|
case I18nInstructions.Any:
|
||||||
const nodeIndex = instruction & I18nInstructions.IndexMask;
|
const nodeIndex = instruction & I18nInstructions.IndexMask;
|
||||||
const node: LNode = load(nodeIndex);
|
|
||||||
localPreviousTNode =
|
localPreviousTNode =
|
||||||
appendI18nNode(node, getTNode(nodeIndex), localParentTNode, localPreviousTNode);
|
appendI18nNode(getTNode(nodeIndex), localParentTNode, localPreviousTNode);
|
||||||
break;
|
break;
|
||||||
case I18nInstructions.Text:
|
case I18nInstructions.Text:
|
||||||
if (ngDevMode) {
|
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
|
// Create text node at the current end of viewData. Must subtract header offset because
|
||||||
// createNodeAtIndex takes a raw index (not adjusted by header offset).
|
// createNodeAtIndex takes a raw index (not adjusted by header offset).
|
||||||
adjustBlueprintForNewNode(viewData);
|
adjustBlueprintForNewNode(viewData);
|
||||||
const lastNodeIndex = viewData.length - 1 - HEADER_OFFSET;
|
const textTNode = createNodeAtIndex(
|
||||||
const textTNode =
|
viewData.length - 1 - HEADER_OFFSET, TNodeType.Element, textRNode, null, null);
|
||||||
createNodeAtIndex(lastNodeIndex, TNodeType.Element, textRNode, null, null);
|
localPreviousTNode = appendI18nNode(textTNode, localParentTNode, localPreviousTNode);
|
||||||
localPreviousTNode = appendI18nNode(
|
|
||||||
loadElement(lastNodeIndex), textTNode, localParentTNode, localPreviousTNode);
|
|
||||||
resetComponentState();
|
resetComponentState();
|
||||||
break;
|
break;
|
||||||
case I18nInstructions.CloseNode:
|
case I18nInstructions.CloseNode:
|
||||||
|
@ -368,15 +364,18 @@ export function i18nApply(startIndex: number, instructions: I18nInstruction[]):
|
||||||
ngDevMode.rendererRemoveNode++;
|
ngDevMode.rendererRemoveNode++;
|
||||||
}
|
}
|
||||||
const removeIndex = instruction & I18nInstructions.IndexMask;
|
const removeIndex = instruction & I18nInstructions.IndexMask;
|
||||||
const removedNode: LNode|LContainerNode = load(removeIndex);
|
const removedNode: LNode|LContainerNode = loadElement(removeIndex);
|
||||||
const removedTNode = getTNode(removeIndex);
|
const removedTNode = getTNode(removeIndex);
|
||||||
removeChild(removedTNode, removedNode.native || null, viewData);
|
removeChild(removedTNode, removedNode.native || null, viewData);
|
||||||
|
|
||||||
// For template containers we also need to remove their `ViewContainerRef` from the DOM
|
const slotValue = load(removeIndex) as LNode | LContainer | StylingContext;
|
||||||
if (removedTNode.type === TNodeType.Container && removedNode.dynamicLContainerNode) {
|
if (isLContainer(slotValue)) {
|
||||||
removeChild(removedTNode, removedNode.dynamicLContainerNode.native || null, viewData);
|
const lContainer = slotValue as LContainer;
|
||||||
|
if (removedTNode.type !== TNodeType.Container) {
|
||||||
|
removeChild(removedTNode, lContainer[NATIVE] || null, viewData);
|
||||||
|
}
|
||||||
removedTNode.detached = true;
|
removedTNode.detached = true;
|
||||||
removedNode.dynamicLContainerNode.data[RENDER_PARENT] = null;
|
lContainer[RENDER_PARENT] = null;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {assertDefined, assertEqual, assertLessThan, assertNotEqual} from './asse
|
||||||
import {attachPatchData, getLElementFromComponent, readElementValue, readPatchedLViewData} from './context_discovery';
|
import {attachPatchData, getLElementFromComponent, readElementValue, readPatchedLViewData} from './context_discovery';
|
||||||
import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors';
|
import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors';
|
||||||
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
|
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 {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
||||||
import {INJECTOR_SIZE} from './interfaces/injector';
|
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';
|
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 {appendChild, appendProjectedNode, createTextNode, findComponentView, getHostElementNode, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation';
|
||||||
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
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 {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(
|
export function createLNodeObject(
|
||||||
type: TNodeType, native: RText | RElement | RComment | null, state: any): LElementNode&
|
type: TNodeType, native: RText | RElement | RComment | null, state: any): LElementNode&
|
||||||
LTextNode&LViewNode&LContainerNode&LProjectionNode {
|
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;
|
lViewData: LViewData): TViewNode;
|
||||||
export function createNodeAtIndex(
|
export function createNodeAtIndex(
|
||||||
index: number, type: TNodeType.Container, native: RComment, name: string | null,
|
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(
|
export function createNodeAtIndex(
|
||||||
index: number, type: TNodeType.Projection, native: null, name: null, attrs: TAttributes | null,
|
index: number, type: TNodeType.Projection, native: null, name: null, attrs: TAttributes | null,
|
||||||
lProjection: null): TProjectionNode;
|
lProjection: null): TProjectionNode;
|
||||||
|
@ -935,9 +935,11 @@ function cacheMatchingDirectivesForNode(
|
||||||
function generateExpandoBlock(tNode: TNode, matches: CurrentMatchesList | null): void {
|
function generateExpandoBlock(tNode: TNode, matches: CurrentMatchesList | null): void {
|
||||||
const directiveCount = matches ? matches.length / 2 : 0;
|
const directiveCount = matches ? matches.length / 2 : 0;
|
||||||
const elementIndex = -(tNode.index - HEADER_OFFSET);
|
const elementIndex = -(tNode.index - HEADER_OFFSET);
|
||||||
|
if (directiveCount > 0) {
|
||||||
(tView.expandoInstructions || (tView.expandoInstructions = [
|
(tView.expandoInstructions || (tView.expandoInstructions = [
|
||||||
])).push(elementIndex, directiveCount);
|
])).push(elementIndex, directiveCount);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On the first template pass, we need to reserve space for host binding values
|
* 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>(
|
export function elementProperty<T>(
|
||||||
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn): void {
|
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn): void {
|
||||||
if (value === NO_CHANGE) return;
|
if (value === NO_CHANGE) return;
|
||||||
const node = loadElement(index) as LElementNode;
|
const node = loadElement(index) as LElementNode | LContainerNode | LElementContainerNode;
|
||||||
const tNode = getTNode(index);
|
const tNode = getTNode(index);
|
||||||
// if tNode.inputs is undefined, a listener has created outputs, but inputs haven't
|
// if tNode.inputs is undefined, a listener has created outputs, but inputs haven't
|
||||||
// yet been checked
|
// yet been checked
|
||||||
|
@ -1431,12 +1433,12 @@ export function elementProperty<T>(
|
||||||
let dataValue: PropertyAliasValue|undefined;
|
let dataValue: PropertyAliasValue|undefined;
|
||||||
if (inputData && (dataValue = inputData[propName])) {
|
if (inputData && (dataValue = inputData[propName])) {
|
||||||
setInputsForProperty(dataValue, value);
|
setInputsForProperty(dataValue, value);
|
||||||
markDirtyIfOnPush(node);
|
if (tNode.type === TNodeType.Element) markDirtyIfOnPush(node as LElementNode);
|
||||||
} else {
|
} else if (tNode.type === TNodeType.Element) {
|
||||||
// It is assumed that the sanitizer is only added when the compiler determines that the property
|
// 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.
|
// is risky, so sanitization can be done without further checks.
|
||||||
value = sanitizer != null ? (sanitizer(value) as any) : value;
|
value = sanitizer != null ? (sanitizer(value) as any) : value;
|
||||||
const native = node.native;
|
const native = node.native as RElement;
|
||||||
ngDevMode && ngDevMode.rendererSetProperty++;
|
ngDevMode && ngDevMode.rendererSetProperty++;
|
||||||
isProceduralRenderer(renderer) ? renderer.setProperty(native, propName, value) :
|
isProceduralRenderer(renderer) ? renderer.setProperty(native, propName, value) :
|
||||||
(native.setProperty ? native.setProperty(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`.
|
* @param index Index of the style allocation. See: `elementStyling`.
|
||||||
*/
|
*/
|
||||||
function getStylingContext(index: number): StylingContext {
|
function getStylingContext(index: number): StylingContext {
|
||||||
let stylingContext = load<StylingContext>(index);
|
let slotValue = viewData[index + HEADER_OFFSET];
|
||||||
if (!Array.isArray(stylingContext)) {
|
|
||||||
const lElement = stylingContext as any as LElementNode;
|
if (isLContainer(slotValue)) {
|
||||||
const tNode = getTNode(index);
|
const lContainer = slotValue;
|
||||||
ngDevMode &&
|
slotValue = lContainer[HOST_NATIVE];
|
||||||
assertDefined(tNode.stylingTemplate, 'getStylingContext() called before elementStyling()');
|
if (!Array.isArray(slotValue)) {
|
||||||
stylingContext = viewData[index + HEADER_OFFSET] =
|
return lContainer[HOST_NATIVE] =
|
||||||
allocStylingContext(lElement, tNode.stylingTemplate !);
|
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.
|
* 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 currentView The parent view of the LContainer
|
||||||
|
* @param native The native comment element
|
||||||
* @param isForViewContainerRef Optional a flag indicating the ViewContainerRef case
|
* @param isForViewContainerRef Optional a flag indicating the ViewContainerRef case
|
||||||
* @returns LContainer
|
* @returns LContainer
|
||||||
*/
|
*/
|
||||||
export function createLContainer(
|
export function createLContainer(
|
||||||
currentView: LViewData, isForViewContainerRef?: boolean): LContainer {
|
hostLNode: LElementNode | LContainerNode | LElementContainerNode,
|
||||||
|
hostTNode: TElementNode | TContainerNode | TElementContainerNode, currentView: LViewData,
|
||||||
|
native: RComment, isForViewContainerRef?: boolean): LContainer {
|
||||||
return [
|
return [
|
||||||
isForViewContainerRef ? null : 0, // active index
|
isForViewContainerRef ? -1 : 0, // active index
|
||||||
currentView, // parent
|
currentView, // parent
|
||||||
null, // next
|
null, // next
|
||||||
null, // queries
|
null, // queries
|
||||||
|
hostLNode, // host native
|
||||||
|
native, // native
|
||||||
[], // views
|
[], // views
|
||||||
null // renderParent, set after node creation
|
getRenderParent(hostTNode, currentView) // renderParent
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2042,12 +2057,13 @@ function containerInternal(
|
||||||
viewData[BINDING_INDEX], tView.bindingStartIndex,
|
viewData[BINDING_INDEX], tView.bindingStartIndex,
|
||||||
'container nodes should be created before any bindings');
|
'container nodes should be created before any bindings');
|
||||||
|
|
||||||
const lContainer = createLContainer(viewData);
|
const adjustedIndex = index + HEADER_OFFSET;
|
||||||
ngDevMode && ngDevMode.rendererCreateComment++;
|
|
||||||
const comment = renderer.createComment(ngDevMode ? 'container' : '');
|
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);
|
appendChild(comment, tNode, viewData);
|
||||||
|
|
||||||
// Containers are added to the current view tree instead of their embedded views
|
// 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);
|
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Container);
|
||||||
isParent = true;
|
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) {
|
if (!checkNoChangesMode) {
|
||||||
// We need to execute init hooks here so ngOnInit hooks are called in top level views
|
// 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);
|
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Container);
|
||||||
|
|
||||||
// Inline containers cannot have style bindings, so we can read the value directly
|
const lContainer = viewData[previousOrParentTNode.index];
|
||||||
const lContainer = viewData[previousOrParentTNode.index].data;
|
|
||||||
const nextIndex = lContainer[ACTIVE_INDEX];
|
const nextIndex = lContainer[ACTIVE_INDEX];
|
||||||
|
|
||||||
// remove extra views at the end of the container
|
// 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
|
// 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
|
// in LContainer. We can tell it's an LContainer because its length is less than the LViewData
|
||||||
// header.
|
// header.
|
||||||
if (current.length < HEADER_OFFSET && current[ACTIVE_INDEX] === null) {
|
if (current.length < HEADER_OFFSET && current[ACTIVE_INDEX] === -1) {
|
||||||
const container = current as LContainer;
|
const container = current as LContainer;
|
||||||
for (let i = 0; i < container[VIEWS].length; i++) {
|
for (let i = 0; i < container[VIEWS].length; i++) {
|
||||||
const dynamicViewData = container[VIEWS][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 ?
|
const containerTNode = previousOrParentTNode.type === TNodeType.View ?
|
||||||
previousOrParentTNode.parent ! :
|
previousOrParentTNode.parent ! :
|
||||||
previousOrParentTNode;
|
previousOrParentTNode;
|
||||||
// Inline containers cannot have style bindings, so we can read the value directly
|
const lContainer = viewData[containerTNode.index] as LContainer;
|
||||||
const container = viewData[containerTNode.index] as LContainerNode;
|
|
||||||
const currentView = viewData;
|
const currentView = viewData;
|
||||||
|
|
||||||
ngDevMode && assertNodeType(containerTNode, TNodeType.Container);
|
ngDevMode && assertNodeType(containerTNode, TNodeType.Container);
|
||||||
const lContainer = container.data;
|
|
||||||
let viewToRender = scanForView(
|
let viewToRender = scanForView(
|
||||||
lContainer, containerTNode as TContainerNode, lContainer[ACTIVE_INDEX] !, viewBlockId);
|
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);
|
createNodeAtIndex(viewBlockId, TNodeType.View, null, null, null, viewToRender);
|
||||||
enterView(viewToRender, viewToRender[TVIEW].node);
|
enterView(viewToRender, viewToRender[TVIEW].node);
|
||||||
}
|
}
|
||||||
if (container) {
|
if (lContainer) {
|
||||||
if (creationMode) {
|
if (creationMode) {
|
||||||
// it is a new view, insert it into collection of views for a given container
|
// it is a new view, insert it into collection of views for a given container
|
||||||
insertView(viewToRender, lContainer, currentView, lContainer[ACTIVE_INDEX] !, -1);
|
insertView(viewToRender, lContainer, currentView, lContainer[ACTIVE_INDEX] !, -1);
|
||||||
|
@ -2409,12 +2422,10 @@ export function projection(nodeIndex: number, selectorIndex: number = 0, attrs?:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
// 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.
|
// if the nodes are inserted into a container later.
|
||||||
nodeToProject.flags |= TNodeFlags.isProjected;
|
nodeToProject.flags |= TNodeFlags.isProjected;
|
||||||
|
appendProjectedNode(nodeToProject, tProjectionNode, viewData, projectedView);
|
||||||
appendProjectedNode(lNode, nodeToProject, tProjectionNode, viewData, projectedView);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are finished with a list of re-projected nodes, we need to get
|
// 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
|
* 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 {LQueries} from './query';
|
||||||
|
import {RComment} from './renderer';
|
||||||
|
import {StylingContext} from './styling';
|
||||||
import {LViewData, NEXT, PARENT, QUERIES} from './view';
|
import {LViewData, NEXT, PARENT, QUERIES} from './view';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,8 +20,10 @@ import {LViewData, NEXT, PARENT, QUERIES} from './view';
|
||||||
export const ACTIVE_INDEX = 0;
|
export const ACTIVE_INDEX = 0;
|
||||||
// PARENT, NEXT, and QUERIES are indices 1, 2, and 3.
|
// 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.
|
// As we already have these constants in LViewData, we don't need to re-create them.
|
||||||
export const VIEWS = 4;
|
export const HOST_NATIVE = 4;
|
||||||
export const RENDER_PARENT = 5;
|
export const NATIVE = 5;
|
||||||
|
export const VIEWS = 6;
|
||||||
|
export const RENDER_PARENT = 7;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The state associated with an LContainerNode.
|
* 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,
|
* 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.
|
* 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
|
* 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;
|
[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
|
* 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
|
* 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;
|
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 LElementNode with component, then `data` contains LViewData.
|
||||||
* If LViewNode, then `data` contains the LViewData.
|
* If LViewNode, then `data` contains the LViewData.
|
||||||
* If LContainerNode, then `data` contains LContainer.
|
|
||||||
*/
|
*/
|
||||||
readonly data: LViewData|LContainer|null;
|
readonly data: LViewData|null;
|
||||||
|
|
||||||
/**
|
|
||||||
* A pointer to an LContainerNode created by directives requesting ViewContainerRef
|
|
||||||
*/
|
|
||||||
// TODO(kara): Remove when removing LNodes
|
|
||||||
dynamicLContainerNode: LContainerNode|null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -107,14 +101,12 @@ export interface LTextNode extends LNode {
|
||||||
/** The text node associated with this node. */
|
/** The text node associated with this node. */
|
||||||
native: RText;
|
native: RText;
|
||||||
readonly data: null;
|
readonly data: null;
|
||||||
dynamicLContainerNode: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Abstract node which contains root nodes of a view. */
|
/** Abstract node which contains root nodes of a view. */
|
||||||
export interface LViewNode extends LNode {
|
export interface LViewNode extends LNode {
|
||||||
readonly native: null;
|
readonly native: null;
|
||||||
readonly data: LViewData;
|
readonly data: LViewData;
|
||||||
dynamicLContainerNode: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Abstract node container which contains other views. */
|
/** Abstract node container which contains other views. */
|
||||||
|
@ -127,14 +119,13 @@ export interface LContainerNode extends LNode {
|
||||||
* until the parent view is processed.
|
* until the parent view is processed.
|
||||||
*/
|
*/
|
||||||
native: RComment;
|
native: RComment;
|
||||||
readonly data: LContainer;
|
readonly data: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface LProjectionNode extends LNode {
|
export interface LProjectionNode extends LNode {
|
||||||
readonly native: null;
|
readonly native: null;
|
||||||
readonly data: null;
|
readonly data: null;
|
||||||
dynamicLContainerNode: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,13 +9,14 @@
|
||||||
import {assertDefined} from './assert';
|
import {assertDefined} from './assert';
|
||||||
import {attachPatchData, readElementValue} from './context_discovery';
|
import {attachPatchData, readElementValue} from './context_discovery';
|
||||||
import {callHooks} from './hooks';
|
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 {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 {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 {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 {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 {assertNodeType} from './node_assert';
|
||||||
import {getLNode, stringify} from './util';
|
import {getLNode, isLContainer, stringify} from './util';
|
||||||
|
|
||||||
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
|
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
|
||||||
|
|
||||||
|
@ -37,17 +38,15 @@ export function getHostElementNode(currentView: LViewData): LElementNode|null {
|
||||||
null;
|
null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getContainerNode(tNode: TNode, embeddedView: LViewData): LContainerNode|null {
|
export function getLContainer(tNode: TViewNode, embeddedView: LViewData): LContainer|null {
|
||||||
if (tNode.index === -1) {
|
if (tNode.index === -1) {
|
||||||
// This is a dynamically created view inside a dynamic container.
|
// 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.
|
// If the host index is -1, the view has not yet been inserted, so it has no parent.
|
||||||
const containerHostIndex = embeddedView[CONTAINER_INDEX];
|
const containerHostIndex = embeddedView[CONTAINER_INDEX];
|
||||||
return containerHostIndex > -1 ?
|
return containerHostIndex > -1 ? embeddedView[PARENT] ![containerHostIndex] : null;
|
||||||
embeddedView[PARENT] ![containerHostIndex].dynamicLContainerNode :
|
|
||||||
null;
|
|
||||||
} else {
|
} else {
|
||||||
// This is a inline view node (e.g. embeddedViewStart)
|
// 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.
|
* Might be null if a view is not yet attached to any container.
|
||||||
*/
|
*/
|
||||||
export function getContainerRenderParent(tViewNode: TViewNode, view: LViewData): LElementNode|null {
|
export function getContainerRenderParent(tViewNode: TViewNode, view: LViewData): LElementNode|null {
|
||||||
const container = getContainerNode(tViewNode, view);
|
const container = getLContainer(tViewNode, view);
|
||||||
return container ? container.data[RENDER_PARENT] : null;
|
return container ? container[RENDER_PARENT] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const enum WalkTNodeTreeAction {
|
const enum WalkTNodeTreeAction {
|
||||||
|
@ -107,29 +106,24 @@ function walkTNodeTree(
|
||||||
if (tNode.type === TNodeType.Element) {
|
if (tNode.type === TNodeType.Element) {
|
||||||
const elementNode = getLNode(tNode, currentView);
|
const elementNode = getLNode(tNode, currentView);
|
||||||
executeNodeAction(action, renderer, parent, elementNode.native !, beforeNode);
|
executeNodeAction(action, renderer, parent, elementNode.native !, beforeNode);
|
||||||
if (elementNode.dynamicLContainerNode) {
|
const nodeOrContainer = currentView[tNode.index];
|
||||||
executeNodeAction(
|
if (isLContainer(nodeOrContainer)) {
|
||||||
action, renderer, parent, elementNode.dynamicLContainerNode.native !, beforeNode);
|
// 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) {
|
} else if (tNode.type === TNodeType.Container) {
|
||||||
const lContainerNode: LContainerNode = currentView ![tNode.index] as LContainerNode;
|
const lContainer = currentView ![tNode.index] as LContainer;
|
||||||
executeNodeAction(action, renderer, parent, lContainerNode.native !, beforeNode);
|
executeNodeAction(action, renderer, parent, lContainer[NATIVE], beforeNode);
|
||||||
const childContainerData: LContainer = lContainerNode.dynamicLContainerNode ?
|
|
||||||
lContainerNode.dynamicLContainerNode.data :
|
|
||||||
lContainerNode.data;
|
|
||||||
if (renderParentNode) {
|
|
||||||
childContainerData[RENDER_PARENT] = renderParentNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (childContainerData[VIEWS].length) {
|
if (renderParentNode) lContainer[RENDER_PARENT] = renderParentNode;
|
||||||
currentView = childContainerData[VIEWS][0];
|
|
||||||
|
if (lContainer[VIEWS].length) {
|
||||||
|
currentView = lContainer[VIEWS][0];
|
||||||
nextTNode = currentView[TVIEW].node;
|
nextTNode = currentView[TVIEW].node;
|
||||||
|
|
||||||
// When the walker enters a container, then the beforeNode has to become the local native
|
// When the walker enters a container, then the beforeNode has to become the local native
|
||||||
// comment node.
|
// comment node.
|
||||||
beforeNode = lContainerNode.dynamicLContainerNode ?
|
beforeNode = lContainer[NATIVE];
|
||||||
lContainerNode.dynamicLContainerNode.native :
|
|
||||||
lContainerNode.native;
|
|
||||||
}
|
}
|
||||||
} else if (tNode.type === TNodeType.Projection) {
|
} else if (tNode.type === TNodeType.Projection) {
|
||||||
const componentView = findComponentView(currentView !);
|
const componentView = findComponentView(currentView !);
|
||||||
|
@ -174,7 +168,7 @@ function walkTNodeTree(
|
||||||
// When exiting a container, the beforeNode must be restored to the previous value
|
// When exiting a container, the beforeNode must be restored to the previous value
|
||||||
if (tNode.type === TNodeType.Container) {
|
if (tNode.type === TNodeType.Container) {
|
||||||
currentView = currentView[PARENT] !;
|
currentView = currentView[PARENT] !;
|
||||||
beforeNode = currentView[tNode.index].native;
|
beforeNode = currentView[tNode.index][NATIVE];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tNode.type === TNodeType.View && currentView[NEXT]) {
|
if (tNode.type === TNodeType.View && currentView[NEXT]) {
|
||||||
|
@ -402,11 +396,14 @@ export function removeView(
|
||||||
|
|
||||||
/** Gets the child of the given LViewData */
|
/** Gets the child of the given LViewData */
|
||||||
export function getLViewChild(viewData: LViewData): LViewData|LContainer|null {
|
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) {
|
tNode.type === TNodeType.View) {
|
||||||
// if it's an embedded view, the state needs to go up to the container, in case the
|
// if it's an embedded view, the state needs to go up to the container, in case the
|
||||||
// container has a next
|
// container has a next
|
||||||
return getContainerNode(tNode, state as LViewData) !.data as any;
|
return getLContainer(tNode as TViewNode, state as LViewData) as LContainer;
|
||||||
} else {
|
} else {
|
||||||
// otherwise, use parent view for containers or component views
|
// otherwise, use parent view for containers or component views
|
||||||
return state[PARENT] === rootView ? null : state[PARENT];
|
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
|
* @param view The LViewData to clean up
|
||||||
*/
|
*/
|
||||||
function cleanUpView(viewOrContainer: LViewData | LContainer): void {
|
function cleanUpView(viewOrContainer: LViewData | LContainer): void {
|
||||||
if ((viewOrContainer as LViewData)[TVIEW]) {
|
if ((viewOrContainer as LViewData).length >= HEADER_OFFSET) {
|
||||||
const view = viewOrContainer as LViewData;
|
const view = viewOrContainer as LViewData;
|
||||||
removeListeners(view);
|
removeListeners(view);
|
||||||
executeOnDestroys(view);
|
executeOnDestroys(view);
|
||||||
|
@ -552,8 +549,8 @@ function canInsertNativeChildOfElement(tNode: TNode): boolean {
|
||||||
*/
|
*/
|
||||||
function canInsertNativeChildOfView(viewTNode: TViewNode, view: LViewData): boolean {
|
function canInsertNativeChildOfView(viewTNode: TViewNode, view: LViewData): boolean {
|
||||||
// Because we are inserting into a `View` the `View` may be disconnected.
|
// Because we are inserting into a `View` the `View` may be disconnected.
|
||||||
const container = getContainerNode(viewTNode, view) !;
|
const container = getLContainer(viewTNode, view) !;
|
||||||
if (container == null || container.data[RENDER_PARENT] == null) {
|
if (container == null || container[RENDER_PARENT] == null) {
|
||||||
// The `View` is not inserted into a `Container` or the parent `Container`
|
// The `View` is not inserted into a `Container` or the parent `Container`
|
||||||
// itself is disconnected. So we have to delay.
|
// itself is disconnected. So we have to delay.
|
||||||
return false;
|
return false;
|
||||||
|
@ -634,12 +631,13 @@ export function appendChild(
|
||||||
const parentTNode: TNode = childTNode.parent || currentView[HOST_NODE] !;
|
const parentTNode: TNode = childTNode.parent || currentView[HOST_NODE] !;
|
||||||
|
|
||||||
if (parentTNode.type === TNodeType.View) {
|
if (parentTNode.type === TNodeType.View) {
|
||||||
const container = getContainerNode(parentTNode, currentView) as LContainerNode;
|
const lContainer = getLContainer(parentTNode as TViewNode, currentView) as LContainer;
|
||||||
const renderParent = container.data[RENDER_PARENT];
|
const renderParent = lContainer[RENDER_PARENT];
|
||||||
const views = container.data[VIEWS];
|
const views = lContainer[VIEWS];
|
||||||
const index = views.indexOf(currentView);
|
const index = views.indexOf(currentView);
|
||||||
nativeInsertBefore(
|
nativeInsertBefore(
|
||||||
renderer, renderParent !.native, childEl, getBeforeNodeForView(index, views, container));
|
renderer, renderParent !.native, childEl,
|
||||||
|
getBeforeNodeForView(index, views, lContainer[NATIVE]));
|
||||||
} else if (parentTNode.type === TNodeType.ElementContainer) {
|
} else if (parentTNode.type === TNodeType.ElementContainer) {
|
||||||
let elementContainer = getHighestElementContainer(childTNode);
|
let elementContainer = getHighestElementContainer(childTNode);
|
||||||
let node: LElementNode = getRenderParent(elementContainer, currentView) !;
|
let node: LElementNode = getRenderParent(elementContainer, currentView) !;
|
||||||
|
@ -666,13 +664,13 @@ function getHighestElementContainer(ngContainer: TNode): TNode {
|
||||||
return ngContainer;
|
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) {
|
if (index + 1 < views.length) {
|
||||||
const view = views[index + 1] as LViewData;
|
const view = views[index + 1] as LViewData;
|
||||||
const viewTNode = view[HOST_NODE] as TViewNode;
|
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 {
|
} 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 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.
|
||||||
*
|
*
|
||||||
* @param projectedLNode The node to process
|
* @param projectedTNode The TNode to be projected
|
||||||
* @param parentNode The last parent element to be processed
|
* @param tProjectionNode The projection (ng-content) TNode
|
||||||
* @param tProjectionNode
|
|
||||||
* @param currentView Current LView
|
* @param currentView Current LView
|
||||||
* @param projectionView Projection view
|
* @param projectionView Projection view (view above current)
|
||||||
*/
|
*/
|
||||||
export function appendProjectedNode(
|
export function appendProjectedNode(
|
||||||
projectedLNode: LElementNode | LElementContainerNode | LTextNode | LContainerNode,
|
|
||||||
projectedTNode: TNode, tProjectionNode: TNode, currentView: LViewData,
|
projectedTNode: TNode, tProjectionNode: TNode, currentView: LViewData,
|
||||||
projectionView: LViewData): void {
|
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)
|
// 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
|
// therefore we need to extract the view where the host element lives since it's the
|
||||||
// logical container of the content projected views
|
// logical container of the content projected views
|
||||||
attachPatchData(projectedLNode.native, projectionView);
|
attachPatchData(native, projectionView);
|
||||||
|
|
||||||
const renderParent = getRenderParent(tProjectionNode, currentView);
|
const renderParent = getRenderParent(tProjectionNode, currentView);
|
||||||
|
|
||||||
|
const nodeOrContainer = projectionView[projectedTNode.index];
|
||||||
if (projectedTNode.type === TNodeType.Container) {
|
if (projectedTNode.type === TNodeType.Container) {
|
||||||
// The node we are adding is a container and we are adding it to an element which
|
// 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).
|
// is not a component (no more re-projection).
|
||||||
// Alternatively a container is projected at the root of a component's template
|
// 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).
|
// and can't be re-projected (as not content of any component).
|
||||||
// Assign the final projection location in those cases.
|
// Assign the final projection location in those cases.
|
||||||
const lContainer = (projectedLNode as LContainerNode).data;
|
nodeOrContainer[RENDER_PARENT] = renderParent;
|
||||||
lContainer[RENDER_PARENT] = renderParent;
|
const views = nodeOrContainer[VIEWS];
|
||||||
const views = lContainer[VIEWS];
|
|
||||||
for (let i = 0; i < views.length; i++) {
|
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;
|
let ngContainerChildTNode: TNode|null = projectedTNode.child as TNode;
|
||||||
while (ngContainerChildTNode) {
|
while (ngContainerChildTNode) {
|
||||||
let ngContainerChild = getLNode(ngContainerChildTNode, projectionView);
|
appendProjectedNode(ngContainerChildTNode, tProjectionNode, currentView, projectionView);
|
||||||
appendProjectedNode(
|
|
||||||
ngContainerChild as LElementNode | LElementContainerNode | LTextNode | LContainerNode,
|
|
||||||
ngContainerChildTNode, tProjectionNode, currentView, projectionView);
|
|
||||||
ngContainerChildTNode = ngContainerChildTNode.next;
|
ngContainerChildTNode = ngContainerChildTNode.next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (projectedLNode.dynamicLContainerNode) {
|
|
||||||
projectedLNode.dynamicLContainerNode.data[RENDER_PARENT] = renderParent;
|
if (isLContainer(nodeOrContainer)) {
|
||||||
appendChild(projectedLNode.dynamicLContainerNode.native, tProjectionNode, currentView);
|
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 {assertDefined, assertLessThan} from './assert';
|
||||||
import {readElementValue, readPatchedLViewData} from './context_discovery';
|
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';
|
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.
|
* 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 {
|
export function isComponent(tNode: TNode): boolean {
|
||||||
return (tNode.flags & TNodeFlags.isComponent) === TNodeFlags.isComponent;
|
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
|
* Retrieve the root view from any component by walking the parent `LViewData` until
|
||||||
* reaching the root `LViewData`.
|
* reaching the root `LViewData`.
|
||||||
|
|
|
@ -18,17 +18,17 @@ import {Renderer2} from '../render/api';
|
||||||
|
|
||||||
import {assertDefined, assertGreaterThan, assertLessThan} from './assert';
|
import {assertDefined, assertGreaterThan, assertLessThan} from './assert';
|
||||||
import {NodeInjector, getParentInjectorLocation, getParentInjectorView} from './di';
|
import {NodeInjector, getParentInjectorLocation, getParentInjectorView} from './di';
|
||||||
import {_getViewData, addToViewTree, createEmbeddedViewAndNode, createLContainer, createLNodeObject, createTNode, getPreviousOrParentTNode, getRenderer, renderEmbeddedTemplate} from './instructions';
|
import {_getViewData, addToViewTree, createEmbeddedViewAndNode, createLContainer, getPreviousOrParentTNode, getRenderer, renderEmbeddedTemplate} from './instructions';
|
||||||
import {LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
|
import {ACTIVE_INDEX, LContainer, NATIVE, RENDER_PARENT, VIEWS} from './interfaces/container';
|
||||||
import {RenderFlags} from './interfaces/definition';
|
import {RenderFlags} from './interfaces/definition';
|
||||||
import {InjectorLocationFlags} from './interfaces/injector';
|
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 {LQueries} from './interfaces/query';
|
||||||
import {RComment, RElement, Renderer3, isProceduralRenderer} from './interfaces/renderer';
|
import {RComment, RElement, Renderer3, isProceduralRenderer} from './interfaces/renderer';
|
||||||
import {CONTEXT, HOST_NODE, LViewData, QUERIES, RENDERER, TVIEW, TView} from './interfaces/view';
|
import {CONTEXT, HOST_NODE, LViewData, QUERIES, RENDERER, TVIEW, TView} from './interfaces/view';
|
||||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||||
import {addRemoveViewFromContainer, appendChild, detachView, findComponentView, getBeforeNodeForView, getRenderParent, insertView, removeView} from './node_manipulation';
|
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';
|
import {ViewRef} from './view_ref';
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,13 +122,12 @@ export function createTemplateRef<T>(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hostContainer: LContainer = hostView[hostTNode.index];
|
||||||
const hostNode = getLNode(hostTNode, hostView);
|
|
||||||
ngDevMode && assertNodeType(hostTNode, TNodeType.Container);
|
ngDevMode && assertNodeType(hostTNode, TNodeType.Container);
|
||||||
ngDevMode && assertDefined(hostTNode.tViews, 'TView must be allocated');
|
ngDevMode && assertDefined(hostTNode.tViews, 'TView must be allocated');
|
||||||
return new R3TemplateRef(
|
return new R3TemplateRef(
|
||||||
hostView, createElementRef(ElementRefToken, hostTNode, hostView), hostTNode.tViews as TView,
|
hostView, createElementRef(ElementRefToken, hostTNode, hostView), hostTNode.tViews as TView,
|
||||||
getRenderer(), hostNode.data ![QUERIES], hostTNode.injectorIndex);
|
getRenderer(), hostContainer[QUERIES], hostTNode.injectorIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
let R3ViewContainerRef: {
|
let R3ViewContainerRef: {
|
||||||
|
@ -241,8 +240,8 @@ export function createContainerRef(
|
||||||
|
|
||||||
insertView(lView, this._lContainer, this._hostView, adjustedIdx, this._hostTNode.index);
|
insertView(lView, this._lContainer, this._hostView, adjustedIdx, this._hostTNode.index);
|
||||||
|
|
||||||
const container = this._getHostNode().dynamicLContainerNode !;
|
const beforeNode =
|
||||||
const beforeNode = getBeforeNodeForView(adjustedIdx, this._lContainer[VIEWS], container);
|
getBeforeNodeForView(adjustedIdx, this._lContainer[VIEWS], this._lContainer[NATIVE]);
|
||||||
addRemoveViewFromContainer(lView, true, beforeNode);
|
addRemoveViewFromContainer(lView, true, beforeNode);
|
||||||
|
|
||||||
(viewRef as ViewRef<any>).attachToViewContainerRef(this);
|
(viewRef as ViewRef<any>).attachToViewContainerRef(this);
|
||||||
|
@ -283,25 +282,27 @@ export function createContainerRef(
|
||||||
}
|
}
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getHostNode() { return getLNode(this._hostTNode, this._hostView); }
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const hostLNode = getLNode(hostTNode, hostView);
|
|
||||||
ngDevMode && assertNodeOfPossibleTypes(
|
ngDevMode && assertNodeOfPossibleTypes(
|
||||||
hostTNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer);
|
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 comment = hostView[RENDERER].createComment(ngDevMode ? 'container' : '');
|
||||||
const lContainerNode: LContainerNode =
|
ngDevMode && ngDevMode.rendererCreateComment++;
|
||||||
createLNodeObject(TNodeType.Container, comment, lContainer);
|
hostView[hostTNode.index] = lContainer =
|
||||||
|
createLContainer(slotValue, hostTNode, hostView, comment, true);
|
||||||
lContainer[RENDER_PARENT] = getRenderParent(hostTNode, hostView);
|
|
||||||
|
|
||||||
appendChild(comment, hostTNode, hostView);
|
appendChild(comment, hostTNode, hostView);
|
||||||
hostLNode.dynamicLContainerNode = lContainerNode;
|
|
||||||
addToViewTree(hostView, hostTNode.index as number, lContainer);
|
addToViewTree(hostView, hostTNode.index as number, lContainer);
|
||||||
|
}
|
||||||
|
|
||||||
return new R3ViewContainerRef(lContainer, hostTNode, hostView);
|
return new R3ViewContainerRef(lContainer, hostTNode, hostView);
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,9 @@
|
||||||
{
|
{
|
||||||
"name": "HEADER_OFFSET"
|
"name": "HEADER_OFFSET"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "HOST_NATIVE"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "HOST_NODE"
|
"name": "HOST_NODE"
|
||||||
},
|
},
|
||||||
|
@ -86,6 +89,9 @@
|
||||||
{
|
{
|
||||||
"name": "MONKEY_PATCH_KEY_NAME"
|
"name": "MONKEY_PATCH_KEY_NAME"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "NATIVE"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "NEXT"
|
"name": "NEXT"
|
||||||
},
|
},
|
||||||
|
@ -545,9 +551,6 @@
|
||||||
{
|
{
|
||||||
"name": "getComponentDef"
|
"name": "getComponentDef"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "getContainerNode"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "getContainerRenderParent"
|
"name": "getContainerRenderParent"
|
||||||
},
|
},
|
||||||
|
@ -584,6 +587,9 @@
|
||||||
{
|
{
|
||||||
"name": "getInjectorIndex"
|
"name": "getInjectorIndex"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getLContainer"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getLElementFromComponent"
|
"name": "getLElementFromComponent"
|
||||||
},
|
},
|
||||||
|
@ -755,6 +761,9 @@
|
||||||
{
|
{
|
||||||
"name": "isJsObject"
|
"name": "isJsObject"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "isLContainer"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "isListLikeIterable"
|
"name": "isListLikeIterable"
|
||||||
},
|
},
|
||||||
|
@ -782,9 +791,6 @@
|
||||||
{
|
{
|
||||||
"name": "listener"
|
"name": "listener"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "load"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "loadElement"
|
"name": "loadElement"
|
||||||
},
|
},
|
||||||
|
|
|
@ -38,6 +38,9 @@
|
||||||
{
|
{
|
||||||
"name": "HEADER_OFFSET"
|
"name": "HEADER_OFFSET"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "HOST_NATIVE"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "HOST_NODE"
|
"name": "HOST_NODE"
|
||||||
},
|
},
|
||||||
|
@ -50,6 +53,9 @@
|
||||||
{
|
{
|
||||||
"name": "MONKEY_PATCH_KEY_NAME"
|
"name": "MONKEY_PATCH_KEY_NAME"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "NATIVE"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "NEXT"
|
"name": "NEXT"
|
||||||
},
|
},
|
||||||
|
@ -224,9 +230,6 @@
|
||||||
{
|
{
|
||||||
"name": "getComponentDef"
|
"name": "getComponentDef"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "getContainerNode"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "getContainerRenderParent"
|
"name": "getContainerRenderParent"
|
||||||
},
|
},
|
||||||
|
@ -242,6 +245,9 @@
|
||||||
{
|
{
|
||||||
"name": "getInjectorIndex"
|
"name": "getInjectorIndex"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getLContainer"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getLNode"
|
"name": "getLNode"
|
||||||
},
|
},
|
||||||
|
|
|
@ -53,6 +53,9 @@
|
||||||
{
|
{
|
||||||
"name": "HEADER_OFFSET"
|
"name": "HEADER_OFFSET"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "HOST_NATIVE"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "HOST_NODE"
|
"name": "HOST_NODE"
|
||||||
},
|
},
|
||||||
|
@ -71,6 +74,9 @@
|
||||||
{
|
{
|
||||||
"name": "MONKEY_PATCH_KEY_NAME"
|
"name": "MONKEY_PATCH_KEY_NAME"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "NATIVE"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "NEXT"
|
"name": "NEXT"
|
||||||
},
|
},
|
||||||
|
@ -596,9 +602,6 @@
|
||||||
{
|
{
|
||||||
"name": "getComponentDef"
|
"name": "getComponentDef"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "getContainerNode"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "getContainerRenderParent"
|
"name": "getContainerRenderParent"
|
||||||
},
|
},
|
||||||
|
@ -629,6 +632,9 @@
|
||||||
{
|
{
|
||||||
"name": "getInjectorIndex"
|
"name": "getInjectorIndex"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getLContainer"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getLElementFromComponent"
|
"name": "getLElementFromComponent"
|
||||||
},
|
},
|
||||||
|
@ -776,6 +782,9 @@
|
||||||
{
|
{
|
||||||
"name": "isJsObject"
|
"name": "isJsObject"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "isLContainer"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "isListLikeIterable"
|
"name": "isListLikeIterable"
|
||||||
},
|
},
|
||||||
|
@ -800,9 +809,6 @@
|
||||||
{
|
{
|
||||||
"name": "listener"
|
"name": "listener"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "load"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "loadElement"
|
"name": "loadElement"
|
||||||
},
|
},
|
||||||
|
|
|
@ -323,6 +323,9 @@
|
||||||
{
|
{
|
||||||
"name": "HOST_ATTR$1"
|
"name": "HOST_ATTR$1"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "HOST_NATIVE"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "HOST_NODE"
|
"name": "HOST_NODE"
|
||||||
},
|
},
|
||||||
|
@ -443,6 +446,9 @@
|
||||||
{
|
{
|
||||||
"name": "NAMESPACE_URIS"
|
"name": "NAMESPACE_URIS"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "NATIVE"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "NATIVE_ADD_LISTENER"
|
"name": "NATIVE_ADD_LISTENER"
|
||||||
},
|
},
|
||||||
|
@ -1604,9 +1610,6 @@
|
||||||
{
|
{
|
||||||
"name": "getComponentDef"
|
"name": "getComponentDef"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "getContainerNode"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "getContainerRenderParent"
|
"name": "getContainerRenderParent"
|
||||||
},
|
},
|
||||||
|
@ -1667,6 +1670,9 @@
|
||||||
{
|
{
|
||||||
"name": "getInjectorIndex"
|
"name": "getInjectorIndex"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getLContainer"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getLElementFromComponent"
|
"name": "getLElementFromComponent"
|
||||||
},
|
},
|
||||||
|
@ -1976,6 +1982,9 @@
|
||||||
{
|
{
|
||||||
"name": "isJsObject"
|
"name": "isJsObject"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "isLContainer"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "isListLikeIterable"
|
"name": "isListLikeIterable"
|
||||||
},
|
},
|
||||||
|
@ -2039,9 +2048,6 @@
|
||||||
{
|
{
|
||||||
"name": "listener"
|
"name": "listener"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "load"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "loadElement"
|
"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 {bloomAdd, bloomHashBitOrFactory as bloomHash, getOrCreateNodeInjector, injectAttribute, injectorHasToken} from '../../src/render3/di';
|
||||||
import {PublicFeature, defineDirective, directiveInject, elementProperty, load, templateRefExtractor} from '../../src/render3/index';
|
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 {isProceduralRenderer} from '../../src/render3/interfaces/renderer';
|
||||||
import {AttributeMarker, LContainerNode, LElementNode, TNodeType} from '../../src/render3/interfaces/node';
|
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 {ComponentFixture, createComponent, createDirective, getDirectiveOnNode, renderComponent, toHtml} from './render_util';
|
||||||
import {NgIf} from './common_with_def';
|
import {NgIf} from './common_with_def';
|
||||||
import {TNODE} from '../../src/render3/interfaces/injector';
|
import {TNODE} from '../../src/render3/interfaces/injector';
|
||||||
|
import {LContainer, NATIVE} from '../../src/render3/interfaces/container';
|
||||||
|
|
||||||
describe('di', () => {
|
describe('di', () => {
|
||||||
describe('no dependencies', () => {
|
describe('no dependencies', () => {
|
||||||
|
@ -1136,7 +1137,7 @@ describe('di', () => {
|
||||||
it('should create ElementRef with comment if requesting directive is on <ng-template> node',
|
it('should create ElementRef with comment if requesting directive is on <ng-template> node',
|
||||||
() => {
|
() => {
|
||||||
let dir !: Directive;
|
let dir !: Directive;
|
||||||
let commentNode !: LContainerNode;
|
let lContainer !: LContainer;
|
||||||
|
|
||||||
class Directive {
|
class Directive {
|
||||||
value: string;
|
value: string;
|
||||||
|
@ -1156,13 +1157,13 @@ describe('di', () => {
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
template(0, () => {}, 0, 0, null, ['dir', '']);
|
template(0, () => {}, 0, 0, null, ['dir', '']);
|
||||||
commentNode = load(0);
|
lContainer = load(0) as any;
|
||||||
}
|
}
|
||||||
}, 1, 0, [Directive]);
|
}, 1, 0, [Directive]);
|
||||||
|
|
||||||
const fixture = new ComponentFixture(App);
|
const fixture = new ComponentFixture(App);
|
||||||
expect(dir.value).toContain('ElementRef');
|
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 {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
|
|
||||||
import {RendererStyleFlags2, RendererType2} from '../../src/render/api';
|
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 {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';
|
import {InitialStylingFlags, RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
|
@ -1349,6 +1349,7 @@ describe('render3 integration test', () => {
|
||||||
describe('elementClass', () => {
|
describe('elementClass', () => {
|
||||||
|
|
||||||
it('should support CSS class toggle', () => {
|
it('should support CSS class toggle', () => {
|
||||||
|
/** <span [class.active]="class"></span> */
|
||||||
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'span');
|
elementStart(0, 'span');
|
||||||
|
@ -1412,6 +1413,72 @@ describe('render3 integration test', () => {
|
||||||
fixture.update();
|
fixture.update();
|
||||||
expect(fixture.html).toEqual('<span class="existing"></span>');
|
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