refactor(ivy): replace pNextOrParent with TNode props (#24752)
PR Close #24752
This commit is contained in:
parent
dc1f1295ee
commit
3a19f70d1c
|
@ -62,7 +62,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
private _bindingCode: o.Statement[] = [];
|
||||
private _postfixCode: o.Statement[] = [];
|
||||
private _temporary = temporaryAllocator(this._prefixCode, TEMPORARY_NAME);
|
||||
private _projectionDefinitionIndex = -1;
|
||||
private _valueConverter: ValueConverter;
|
||||
private _unsupported = unsupported;
|
||||
private _bindingScope: BindingScope;
|
||||
|
@ -121,8 +120,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
|
||||
// Output a `ProjectionDef` instruction when some `<ng-content>` are present
|
||||
if (hasNgContent) {
|
||||
this._projectionDefinitionIndex = this.allocateDataSlot();
|
||||
const parameters: o.Expression[] = [o.literal(this._projectionDefinitionIndex)];
|
||||
const parameters: o.Expression[] = [];
|
||||
|
||||
// Only selectors with a non-default value are generated
|
||||
if (ngContentSelectors.length > 1) {
|
||||
|
@ -217,10 +215,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
visitContent(ngContent: t.Content) {
|
||||
const slot = this.allocateDataSlot();
|
||||
const selectorIndex = ngContent.selectorIndex;
|
||||
const parameters: o.Expression[] = [
|
||||
o.literal(slot),
|
||||
o.literal(this._projectionDefinitionIndex),
|
||||
];
|
||||
const parameters: o.Expression[] = [o.literal(slot)];
|
||||
|
||||
const attributeAsList: string[] = [];
|
||||
|
||||
|
|
|
@ -871,9 +871,9 @@ describe('compiler compliance', () => {
|
|||
factory: function SimpleComponent_Factory() { return new SimpleComponent(); },
|
||||
template: function SimpleComponent_Template(rf: IDENT, ctx: IDENT) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵpD(0);
|
||||
$r3$.ɵE(1, 'div');
|
||||
$r3$.ɵP(2, 0);
|
||||
$r3$.ɵpD();
|
||||
$r3$.ɵE(0, 'div');
|
||||
$r3$.ɵP(1);
|
||||
$r3$.ɵe();
|
||||
}
|
||||
}
|
||||
|
@ -891,12 +891,12 @@ describe('compiler compliance', () => {
|
|||
factory: function ComplexComponent_Factory() { return new ComplexComponent(); },
|
||||
template: function ComplexComponent_Template(rf: IDENT, ctx: IDENT) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵpD(0, $c1$, $c2$);
|
||||
$r3$.ɵE(1, 'div', $c3$);
|
||||
$r3$.ɵP(2, 0, 1);
|
||||
$r3$.ɵpD($c1$, $c2$);
|
||||
$r3$.ɵE(0, 'div', $c3$);
|
||||
$r3$.ɵP(1, 1);
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵE(3, 'div', $c4$);
|
||||
$r3$.ɵP(4, 0, 2);
|
||||
$r3$.ɵE(2, 'div', $c4$);
|
||||
$r3$.ɵP(3, 2);
|
||||
$r3$.ɵe();
|
||||
}
|
||||
}
|
||||
|
@ -1022,9 +1022,9 @@ describe('compiler compliance', () => {
|
|||
template: function ContentQueryComponent_Template(
|
||||
rf: $RenderFlags$, ctx: $ContentQueryComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵpD(0);
|
||||
$r3$.ɵE(1, 'div');
|
||||
$r3$.ɵP(2, 0);
|
||||
$r3$.ɵpD();
|
||||
$r3$.ɵE(0, 'div');
|
||||
$r3$.ɵP(1);
|
||||
$r3$.ɵe();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import {assertEqual, assertLessThan} from './assert';
|
||||
import {NO_CHANGE, bindingUpdated, createLNode, getPreviousOrParentNode, getRenderer, getViewData, load, resetApplicationState} from './instructions';
|
||||
import {RENDER_PARENT} from './interfaces/container';
|
||||
import {LContainerNode, LElementNode, LNode, TContainerNode, TNodeType} from './interfaces/node';
|
||||
import {LContainerNode, LElementNode, LNode, TContainerNode, TElementNode, TNodeType} from './interfaces/node';
|
||||
import {BINDING_INDEX, HEADER_OFFSET, TVIEW} from './interfaces/view';
|
||||
import {appendChild, createTextNode, getParentLNode, removeChild} from './node_manipulation';
|
||||
import {stringify} from './util';
|
||||
|
@ -243,14 +243,17 @@ function appendI18nNode(node: LNode, parentNode: LNode, previousNode: LNode) {
|
|||
// On first pass, re-organize node tree to put this node in the correct position.
|
||||
const firstTemplatePass = node.view[TVIEW].firstTemplatePass;
|
||||
if (firstTemplatePass) {
|
||||
node.tNode.next = null;
|
||||
if (previousNode === parentNode && node.tNode !== parentNode.tNode.child) {
|
||||
node.tNode.next = parentNode.tNode.child;
|
||||
parentNode.tNode.child = node.tNode;
|
||||
} else if (previousNode !== parentNode && node.tNode !== previousNode.tNode.next) {
|
||||
node.tNode.next = previousNode.tNode.next;
|
||||
previousNode.tNode.next = node.tNode;
|
||||
} else {
|
||||
node.tNode.next = null;
|
||||
}
|
||||
|
||||
if (parentNode.view === node.view) node.tNode.parent = parentNode.tNode as TElementNode;
|
||||
}
|
||||
|
||||
// Template containers also have a comment node for the `ViewContainerRef` that should be moved
|
||||
|
|
|
@ -14,20 +14,20 @@ import {assertDefined, assertEqual, assertLessThan, assertNotDefined, assertNotE
|
|||
import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors';
|
||||
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
|
||||
import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
|
||||
import {ComponentDefInternal, ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
||||
import {LInjector} from './interfaces/injector';
|
||||
import {CssSelectorList, LProjection, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
||||
import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
|
||||
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
||||
import {LQueries} from './interfaces/query';
|
||||
import {ProceduralRenderer3, RComment, RElement, RText, Renderer3, RendererFactory3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
|
||||
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTEXT, CurrentMatchesList, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TData, TVIEW, TView} from './interfaces/view';
|
||||
|
||||
import {AttributeMarker, TAttributes, LContainerNode, LElementNode, LNode, TNodeType, TNodeFlags, LProjectionNode, LTextNode, LViewNode, TNode, TContainerNode, InitialInputData, InitialInputs, PropertyAliases, PropertyAliasValue, TElementNode,} from './interfaces/node';
|
||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||
import {appendChild, insertView, appendProjectedNode, removeView, canInsertNativeNode, createTextNode, getNextLNode, getChildLNode, getParentLNode, getLViewChild} from './node_manipulation';
|
||||
import {appendChild, appendProjectedNode, canInsertNativeNode, createTextNode, findComponentHost, getChildLNode, getLViewChild, getNextLNode, getParentLNode, insertView, removeView} from './node_manipulation';
|
||||
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
||||
import {ComponentDefInternal, ComponentTemplate, ComponentQuery, DirectiveDefInternal, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
||||
import {RComment, RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
|
||||
import {StylingContext, allocStylingContext, createStylingContextTemplate, renderStyles as renderElementStyles, updateStyleMap as updateElementStyleMap, updateStyleProp as updateElementStyleProp} from './styling';
|
||||
import {isDifferent, stringify} from './util';
|
||||
import {ViewRef} from './view_ref';
|
||||
import {StylingContext, allocStylingContext, createStylingContextTemplate, updateStyleMap as updateElementStyleMap, updateStyleProp as updateElementStyleProp, renderStyles as renderElementStyles} from './styling';
|
||||
|
||||
|
||||
/**
|
||||
* Directive (D) sets a property on all component instances using this constant as a key and the
|
||||
|
@ -335,7 +335,6 @@ export function createLNodeObject(
|
|||
data: state,
|
||||
queries: queries,
|
||||
tNode: null !,
|
||||
pNextOrParent: null,
|
||||
dynamicLContainerNode: null
|
||||
};
|
||||
}
|
||||
|
@ -363,11 +362,11 @@ export function createLNode(
|
|||
attrs: TAttributes | null, lContainer: LContainer): LContainerNode;
|
||||
export function createLNode(
|
||||
index: number, type: TNodeType.Projection, native: null, name: null, attrs: TAttributes | null,
|
||||
lProjection: LProjection): LProjectionNode;
|
||||
lProjection: null): LProjectionNode;
|
||||
export function createLNode(
|
||||
index: number, type: TNodeType, native: RText | RElement | RComment | null, name: string | null,
|
||||
attrs: TAttributes | null, state?: null | LViewData | LContainer | LProjection): LElementNode&
|
||||
LTextNode&LViewNode&LContainerNode&LProjectionNode {
|
||||
attrs: TAttributes | null, state?: null | LViewData | LContainer): LElementNode<extNode&
|
||||
LViewNode&LContainerNode&LProjectionNode {
|
||||
const parent = isParent ? previousOrParentNode :
|
||||
previousOrParentNode && getParentLNode(previousOrParentNode) !as LNode;
|
||||
// Parents cannot cross component boundaries because components will be used in multiple places,
|
||||
|
@ -1189,7 +1188,8 @@ export function createTNode(
|
|||
parent: parent,
|
||||
dynamicContainerNode: null,
|
||||
detached: null,
|
||||
stylingTemplate: null
|
||||
stylingTemplate: null,
|
||||
projection: null
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1948,136 +1948,107 @@ export function viewAttached(view: LViewData): boolean {
|
|||
* @param selectors A collection of parsed CSS selectors
|
||||
* @param rawSelectors A collection of CSS selectors in the raw, un-parsed form
|
||||
*/
|
||||
export function projectionDef(
|
||||
index: number, selectors?: CssSelectorList[], textSelectors?: string[]): void {
|
||||
const noOfNodeBuckets = selectors ? selectors.length + 1 : 1;
|
||||
const distributedNodes = new Array<LNode[]>(noOfNodeBuckets);
|
||||
for (let i = 0; i < noOfNodeBuckets; i++) {
|
||||
distributedNodes[i] = [];
|
||||
}
|
||||
|
||||
export function projectionDef(selectors?: CssSelectorList[], textSelectors?: string[]): void {
|
||||
const componentNode: LElementNode = findComponentHost(viewData);
|
||||
let componentChild = getChildLNode(componentNode);
|
||||
|
||||
if (!componentNode.tNode.projection) {
|
||||
const noOfNodeBuckets = selectors ? selectors.length + 1 : 1;
|
||||
const pData: (TNode | null)[] = componentNode.tNode.projection =
|
||||
new Array(noOfNodeBuckets).fill(null);
|
||||
const tails: (TNode | null)[] = pData.slice();
|
||||
|
||||
let componentChild = componentNode.tNode.child;
|
||||
|
||||
while (componentChild !== null) {
|
||||
// execute selector matching logic if and only if:
|
||||
// - there are selectors defined
|
||||
// - a node has a tag name / attributes that can be matched
|
||||
const bucketIndex =
|
||||
selectors ? matchingSelectorIndex(componentChild.tNode, selectors, textSelectors !) : 0;
|
||||
distributedNodes[bucketIndex].push(componentChild);
|
||||
selectors ? matchingSelectorIndex(componentChild, selectors, textSelectors !) : 0;
|
||||
const nextNode = componentChild.next;
|
||||
|
||||
componentChild = getNextLNode(componentChild);
|
||||
if (tails[bucketIndex]) {
|
||||
tails[bucketIndex] !.next = componentChild;
|
||||
} else {
|
||||
pData[bucketIndex] = componentChild;
|
||||
componentChild.next = null;
|
||||
}
|
||||
tails[bucketIndex] = componentChild;
|
||||
|
||||
ngDevMode && assertDataNext(index + HEADER_OFFSET);
|
||||
store(index, distributedNodes);
|
||||
componentChild = nextNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the linked list of a projection node, by appending another linked list.
|
||||
* Stack used to keep track of projection nodes in projection() instruction.
|
||||
*
|
||||
* @param projectionNode Projection node whose projected nodes linked list has to be updated
|
||||
* @param appendedFirst First node of the linked list to append.
|
||||
* @param appendedLast Last node of the linked list to append.
|
||||
* This is deliberately created outside of projection() to avoid allocating
|
||||
* a new array each time the function is called. Instead the array will be
|
||||
* re-used by each invocation. This works because the function is not reentrant.
|
||||
*/
|
||||
function addToProjectionList(
|
||||
projectionNode: LProjectionNode,
|
||||
appendedFirst: LElementNode | LTextNode | LContainerNode | null,
|
||||
appendedLast: LElementNode | LTextNode | LContainerNode | null) {
|
||||
ngDevMode && assertEqual(
|
||||
!!appendedFirst, !!appendedLast,
|
||||
'appendedFirst can be null if and only if appendedLast is also null');
|
||||
if (!appendedLast) {
|
||||
// nothing to append
|
||||
return;
|
||||
}
|
||||
const projectionNodeData = projectionNode.data;
|
||||
if (projectionNodeData.tail) {
|
||||
projectionNodeData.tail.pNextOrParent = appendedFirst;
|
||||
} else {
|
||||
projectionNodeData.head = appendedFirst;
|
||||
}
|
||||
projectionNodeData.tail = appendedLast;
|
||||
appendedLast.pNextOrParent = projectionNode;
|
||||
}
|
||||
const projectionNodeStack: LProjectionNode[] = [];
|
||||
|
||||
/**
|
||||
* Inserts previously re-distributed projected nodes. This instruction must be preceded by a call
|
||||
* to the projectionDef instruction.
|
||||
*
|
||||
* @param nodeIndex
|
||||
* @param localIndex - index under which distribution of projected nodes was memorized
|
||||
* @param selectorIndex:
|
||||
* - 0 when the selector is `*` (or unspecified as this is the default value),
|
||||
* - 1 based index of the selector from the {@link projectionDef}
|
||||
*/
|
||||
export function projection(
|
||||
nodeIndex: number, localIndex: number, selectorIndex: number = 0, attrs?: string[]): void {
|
||||
const node = createLNode(
|
||||
nodeIndex, TNodeType.Projection, null, null, attrs || null, {head: null, tail: null});
|
||||
export function projection(nodeIndex: number, selectorIndex: number = 0, attrs?: string[]): void {
|
||||
const node = createLNode(nodeIndex, TNodeType.Projection, null, null, attrs || null, null);
|
||||
|
||||
// We can't use viewData[HOST_NODE] because projection nodes can be nested in embedded views.
|
||||
if (node.tNode.projection === null) node.tNode.projection = selectorIndex;
|
||||
|
||||
// `<ng-content>` has no content
|
||||
isParent = false;
|
||||
|
||||
// re-distribution of projectable nodes is memorized on a component's view level
|
||||
// re-distribution of projectable nodes is stored on a component's view level
|
||||
const parent = getParentLNode(node);
|
||||
|
||||
if (canInsertNativeNode(parent, viewData)) {
|
||||
const componentNode = findComponentHost(viewData);
|
||||
const componentLView = componentNode.data as LViewData;
|
||||
const distributedNodes = loadInternal(localIndex, componentLView) as Array<LNode[]>;
|
||||
const nodesForSelector = distributedNodes[selectorIndex];
|
||||
|
||||
const currentParent = getParentLNode(node);
|
||||
const canInsert = canInsertNativeNode(currentParent, viewData);
|
||||
let nodeToProject = (componentNode.tNode.projection as(TNode | null)[])[selectorIndex];
|
||||
let projectedView = componentNode.view;
|
||||
let projectionNodeIndex = -1;
|
||||
let grandparent: LContainerNode;
|
||||
const renderParent = currentParent.tNode.type === TNodeType.View ?
|
||||
(grandparent = getParentLNode(currentParent) as LContainerNode) &&
|
||||
const renderParent = parent.tNode.type === TNodeType.View ?
|
||||
(grandparent = getParentLNode(parent) as LContainerNode) &&
|
||||
grandparent.data[RENDER_PARENT] ! :
|
||||
currentParent as LElementNode;
|
||||
parent as LElementNode;
|
||||
|
||||
for (let i = 0; i < nodesForSelector.length; i++) {
|
||||
const nodeToProject = nodesForSelector[i];
|
||||
let head = nodeToProject as LTextNode | LElementNode | LContainerNode | null;
|
||||
let tail = nodeToProject as LTextNode | LElementNode | LContainerNode | null;
|
||||
while (nodeToProject) {
|
||||
if (nodeToProject.type === TNodeType.Projection) {
|
||||
// This node is re-projected, so we must go up the tree to get its projected nodes.
|
||||
const currentComponentHost = findComponentHost(projectedView);
|
||||
const firstProjectedNode = (currentComponentHost.tNode.projection as(
|
||||
TNode | null)[])[nodeToProject.projection as number];
|
||||
|
||||
if (nodeToProject.tNode.type === TNodeType.Projection) {
|
||||
const previouslyProjected = (nodeToProject as LProjectionNode).data;
|
||||
head = previouslyProjected.head;
|
||||
tail = previouslyProjected.tail;
|
||||
if (firstProjectedNode) {
|
||||
projectionNodeStack[++projectionNodeIndex] = projectedView[nodeToProject.index];
|
||||
nodeToProject = firstProjectedNode;
|
||||
projectedView = currentComponentHost.view;
|
||||
continue;
|
||||
}
|
||||
|
||||
addToProjectionList(node, head, tail);
|
||||
|
||||
if (canInsert) {
|
||||
let currentNode: LNode|null = head;
|
||||
while (currentNode) {
|
||||
} else {
|
||||
const lNode = projectedView[nodeToProject.index];
|
||||
lNode.tNode.flags |= TNodeFlags.isProjected;
|
||||
appendProjectedNode(
|
||||
currentNode as LTextNode | LElementNode | LContainerNode, currentParent, viewData,
|
||||
renderParent);
|
||||
currentNode = currentNode === tail ? null : currentNode.pNextOrParent;
|
||||
}
|
||||
}
|
||||
}
|
||||
lNode as LTextNode | LElementNode | LContainerNode, parent, viewData, renderParent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a current view, finds the nearest component's host (LElement).
|
||||
*
|
||||
* @param lViewData LViewData for which we want a host element node
|
||||
* @returns The host node
|
||||
*/
|
||||
function findComponentHost(lViewData: LViewData): LElementNode {
|
||||
let viewRootLNode = lViewData[HOST_NODE];
|
||||
|
||||
while (viewRootLNode.tNode.type === TNodeType.View) {
|
||||
ngDevMode && assertDefined(lViewData[PARENT], 'lViewData.parent');
|
||||
lViewData = lViewData[PARENT] !;
|
||||
viewRootLNode = lViewData[HOST_NODE];
|
||||
// If we are finished with a list of re-projected nodes, we need to get
|
||||
// back to the root projection node that was re-projected.
|
||||
if (nodeToProject.next === null && projectedView !== componentNode.view) {
|
||||
// move down into the view of the component we're projecting right now
|
||||
const lNode = projectionNodeStack[projectionNodeIndex--];
|
||||
nodeToProject = lNode.tNode;
|
||||
projectedView = lNode.view;
|
||||
}
|
||||
nodeToProject = nodeToProject.next;
|
||||
}
|
||||
}
|
||||
|
||||
ngDevMode && assertNodeType(viewRootLNode, TNodeType.Element);
|
||||
ngDevMode && assertDefined(viewRootLNode.data, 'node.data');
|
||||
|
||||
return viewRootLNode as LElementNode;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,7 +10,6 @@ import {StylingContext} from '../styling';
|
|||
|
||||
import {LContainer} from './container';
|
||||
import {LInjector} from './injector';
|
||||
import {LProjection} from './projection';
|
||||
import {LQueries} from './query';
|
||||
import {RComment, RElement, RText} from './renderer';
|
||||
import {LViewData, TView} from './view';
|
||||
|
@ -36,11 +35,14 @@ export const enum TNodeFlags {
|
|||
/** The number of directives on this node is encoded on the least significant bits */
|
||||
DirectiveCountMask = 0b00000000000000000000111111111111,
|
||||
|
||||
/** Then this bit is set when the node is a component */
|
||||
isComponent = 0b1000000000000,
|
||||
/** This bit is set if the node is a component */
|
||||
isComponent = 0b00000000000000000001000000000000,
|
||||
|
||||
/** This bit is set if the node has been projected */
|
||||
isProjected = 0b00000000000000000010000000000000,
|
||||
|
||||
/** The index of the first directive on this node is encoded on the most significant bits */
|
||||
DirectiveStartingIndexShift = 13,
|
||||
DirectiveStartingIndexShift = 14,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -74,7 +76,7 @@ export interface LNode {
|
|||
* If LContainerNode, then `data` contains LContainer.
|
||||
* If LProjectionNode, then `data` contains LProjection.
|
||||
*/
|
||||
readonly data: LViewData|LContainer|LProjection|null;
|
||||
readonly data: LViewData|LContainer|null;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -94,14 +96,6 @@ export interface LNode {
|
|||
*/
|
||||
queries: LQueries|null;
|
||||
|
||||
/**
|
||||
* If this node is projected, pointer to the next node in the same projection parent
|
||||
* (which is a container, an element, or a text node), or to the parent projection node
|
||||
* if this is the last node in the projection.
|
||||
* If this node is not projected, this field is null.
|
||||
*/
|
||||
pNextOrParent: LNode|null;
|
||||
|
||||
/**
|
||||
* Pointer to the corresponding TNode object, which stores static
|
||||
* data about this node.
|
||||
|
@ -156,7 +150,7 @@ export interface LContainerNode extends LNode {
|
|||
|
||||
export interface LProjectionNode extends LNode {
|
||||
readonly native: null;
|
||||
readonly data: LProjection;
|
||||
readonly data: null;
|
||||
dynamicLContainerNode: null;
|
||||
}
|
||||
|
||||
|
@ -345,6 +339,43 @@ export interface TNode {
|
|||
detached: boolean|null;
|
||||
|
||||
stylingTemplate: StylingContext|null;
|
||||
/**
|
||||
* List of projected TNodes for a given component host element OR index into the said nodes.
|
||||
*
|
||||
* For easier discussion assume this example:
|
||||
* `<parent>`'s view definition:
|
||||
* ```
|
||||
* <child id="c1">content1</child>
|
||||
* <child id="c2"><span>content2</span></child>
|
||||
* ```
|
||||
* `<child>`'s view definition:
|
||||
* ```
|
||||
* <ng-content id="cont1"></ng-content>
|
||||
* ```
|
||||
*
|
||||
* If `Array.isArray(projection)` then `TNode` is a host element:
|
||||
* - `projection` stores the content nodes which are to be projected.
|
||||
* - The nodes represent categories defined by the selector: For example:
|
||||
* `<ng-content/><ng-content select="abc"/>` would represent the heads for `<ng-content/>`
|
||||
* and `<ng-content select="abc"/>` respectively.
|
||||
* - The nodes we store in `projection` are heads only, we used `.next` to get their
|
||||
* siblings.
|
||||
* - The nodes `.next` is sorted/rewritten as part of the projection setup.
|
||||
* - `projection` size is equal to the number of projections `<ng-content>`. The size of
|
||||
* `c1` will be `1` because `<child>` has only one `<ng-content>`.
|
||||
* - we store `projection` with the host (`c1`, `c2`) rather than the `<ng-content>` (`cont1`)
|
||||
* because the same component (`<child>`) can be used in multiple locations (`c1`, `c2`) and as
|
||||
* a result have different set of nodes to project.
|
||||
* - without `projection` it would be difficult to efficiently traverse nodes to be projected.
|
||||
*
|
||||
* If `typeof projection == 'number'` then `TNode` is a `<ng-content>` element:
|
||||
* - `projection` is an index of the host's `projection`Nodes.
|
||||
* - This would return the first head node to project:
|
||||
* `getHost(currentTNode).projection[currentTNode.projection]`.
|
||||
* - When projecting nodes the parent node retrieved may be a `<ng-content>` node, in which case
|
||||
* the process is recursive in nature (not implementation).
|
||||
*/
|
||||
projection: (TNode|null)[]|number|null;
|
||||
}
|
||||
|
||||
/** Static data for an LElementNode */
|
||||
|
@ -359,6 +390,13 @@ export interface TElementNode extends TNode {
|
|||
*/
|
||||
parent: TElementNode|null;
|
||||
tViews: null;
|
||||
|
||||
/**
|
||||
* If this is a component TNode with projection, this will be an array of projected
|
||||
* TNodes (see TNode.projection for more info). If it's a regular element node or a
|
||||
* component without projection, it will be null.
|
||||
*/
|
||||
projection: (TNode|null)[]|null;
|
||||
}
|
||||
|
||||
/** Static data for an LTextNode */
|
||||
|
@ -373,6 +411,7 @@ export interface TTextNode extends TNode {
|
|||
*/
|
||||
parent: TElementNode|null;
|
||||
tViews: null;
|
||||
projection: null;
|
||||
}
|
||||
|
||||
/** Static data for an LContainerNode */
|
||||
|
@ -394,6 +433,7 @@ export interface TContainerNode extends TNode {
|
|||
*/
|
||||
parent: TElementNode|null;
|
||||
tViews: TView|TView[]|null;
|
||||
projection: null;
|
||||
}
|
||||
|
||||
/** Static data for an LViewNode */
|
||||
|
@ -403,6 +443,7 @@ export interface TViewNode extends TNode {
|
|||
child: TElementNode|TTextNode|TContainerNode|TProjectionNode|null;
|
||||
parent: TContainerNode|null;
|
||||
tViews: null;
|
||||
projection: null;
|
||||
}
|
||||
|
||||
/** Static data for an LProjectionNode */
|
||||
|
@ -416,6 +457,9 @@ export interface TProjectionNode extends TNode {
|
|||
*/
|
||||
parent: TElementNode|null;
|
||||
tViews: null;
|
||||
|
||||
/** Index of the projection node. (See TNode.projection for more info.) */
|
||||
projection: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
|
@ -6,15 +7,6 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {LContainerNode, LElementNode, LTextNode} from './node';
|
||||
|
||||
/**
|
||||
* Linked list of projected nodes (using the pNextOrParent property).
|
||||
*/
|
||||
export interface LProjection {
|
||||
head: LElementNode|LTextNode|LContainerNode|null;
|
||||
tail: LElementNode|LTextNode|LContainerNode|null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expresses a single CSS Selector.
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {assertDefined} from './assert';
|
||||
import {callHooks} from './hooks';
|
||||
import {LContainer, RENDER_PARENT, VIEWS, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
|
||||
import {LContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, TNodeType, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
|
||||
import {LContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, TNode, TNodeFlags, TNodeType, 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 {CLEANUP, CONTAINER_INDEX, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, HookData, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
|
||||
|
@ -24,7 +25,7 @@ export function getNextLNode(node: LNode): LNode|null {
|
|||
const viewData = node.data as LViewData;
|
||||
return viewData[NEXT] ? (viewData[NEXT] as LViewData)[HOST_NODE] : null;
|
||||
}
|
||||
return node.tNode.next ? node.view[node.tNode.next !.index] : null;
|
||||
return node.tNode.next ? node.view[node.tNode.next.index] : null;
|
||||
}
|
||||
|
||||
/** Retrieves the first child of a given node */
|
||||
|
@ -52,27 +53,6 @@ export function getParentLNode(node: LNode): LElementNode|LContainerNode|LViewNo
|
|||
return parent ? node.view[parent.index] : node.view[HOST_NODE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next node in the LNode tree, taking into account the place where a node is
|
||||
* projected (in the shadow DOM) rather than where it comes from (in the light DOM).
|
||||
*
|
||||
* @param node The node whose next node in the LNode tree must be found.
|
||||
* @return LNode|null The next sibling in the LNode tree.
|
||||
*/
|
||||
function getNextLNodeWithProjection(node: LNode): LNode|null {
|
||||
const pNextOrParent = node.pNextOrParent;
|
||||
|
||||
if (pNextOrParent) {
|
||||
// The node is projected
|
||||
const isLastProjectedNode = pNextOrParent.tNode.type === TNodeType.Projection;
|
||||
// returns pNextOrParent if we are not at the end of the list, null otherwise
|
||||
return isLastProjectedNode ? null : pNextOrParent;
|
||||
}
|
||||
|
||||
// returns node.next because the the node is not projected
|
||||
return getNextLNode(node);
|
||||
}
|
||||
|
||||
const enum WalkLNodeTreeAction {
|
||||
/** node insert in the native environment */
|
||||
Insert = 0,
|
||||
|
@ -84,12 +64,22 @@ const enum WalkLNodeTreeAction {
|
|||
Destroy = 2,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stack used to keep track of projection nodes in walkLNodeTree.
|
||||
*
|
||||
* This is deliberately created outside of walkLNodeTree to avoid allocating
|
||||
* a new array each time the function is called. Instead the array will be
|
||||
* re-used by each invocation. This works because the function is not reentrant.
|
||||
*/
|
||||
const projectionNodeStack: LProjectionNode[] = [];
|
||||
|
||||
/**
|
||||
* Walks a tree of LNodes, applying a transformation on the LElement nodes, either only on the first
|
||||
* one found, or on all of them.
|
||||
*
|
||||
* @param startingNode the node from which the walk is started.
|
||||
* @param rootNode the root node considered.
|
||||
* @param rootNode the root node considered. This prevents walking past that node.
|
||||
* @param action identifies the action to be performed on the LElement nodes.
|
||||
* @param renderer the current renderer.
|
||||
* @param renderParentNode Optional the render parent node to be set in all LContainerNodes found,
|
||||
|
@ -101,18 +91,19 @@ function walkLNodeTree(
|
|||
startingNode: LNode | null, rootNode: LNode, action: WalkLNodeTreeAction, renderer: Renderer3,
|
||||
renderParentNode?: LElementNode | null, beforeNode?: RNode | null) {
|
||||
let node: LNode|null = startingNode;
|
||||
let projectionNodeIndex = -1;
|
||||
while (node) {
|
||||
let nextNode: LNode|null = null;
|
||||
const parent = renderParentNode ? renderParentNode.native : null;
|
||||
if (node.tNode.type === TNodeType.Element) {
|
||||
const nodeType = node.tNode.type;
|
||||
if (nodeType === TNodeType.Element) {
|
||||
// Execute the action
|
||||
executeNodeAction(action, renderer, parent, node.native !, beforeNode);
|
||||
if (node.dynamicLContainerNode) {
|
||||
executeNodeAction(
|
||||
action, renderer, parent, node.dynamicLContainerNode.native !, beforeNode);
|
||||
}
|
||||
nextNode = getNextLNode(node);
|
||||
} else if (node.tNode.type === TNodeType.Container) {
|
||||
} else if (nodeType === TNodeType.Container) {
|
||||
executeNodeAction(action, renderer, parent, node.native !, beforeNode);
|
||||
const lContainerNode: LContainerNode = (node as LContainerNode);
|
||||
const childContainerData: LContainer = lContainerNode.dynamicLContainerNode ?
|
||||
|
@ -130,15 +121,26 @@ function walkLNodeTree(
|
|||
lContainerNode.dynamicLContainerNode.native :
|
||||
lContainerNode.native;
|
||||
}
|
||||
} else if (node.tNode.type === TNodeType.Projection) {
|
||||
// For Projection look at the first projected node
|
||||
nextNode = (node as LProjectionNode).data.head;
|
||||
} else if (nodeType === TNodeType.Projection) {
|
||||
const componentHost = findComponentHost(node.view);
|
||||
const head =
|
||||
(componentHost.tNode.projection as(TNode | null)[])[node.tNode.projection as number];
|
||||
|
||||
projectionNodeStack[++projectionNodeIndex] = node as LProjectionNode;
|
||||
|
||||
nextNode = head ? (componentHost.data as LViewData)[PARENT] ![head.index] : null;
|
||||
} else {
|
||||
// Otherwise look at the first child
|
||||
nextNode = getChildLNode(node as LViewNode);
|
||||
}
|
||||
|
||||
if (nextNode == null) {
|
||||
if (nextNode === null) {
|
||||
nextNode = getNextLNode(node);
|
||||
|
||||
// this last node was projected, we need to get back down to its projection node
|
||||
if (nextNode === null && (node.tNode.flags & TNodeFlags.isProjected)) {
|
||||
nextNode = getNextLNode(projectionNodeStack[projectionNodeIndex--] as LNode);
|
||||
}
|
||||
/**
|
||||
* Find the next node in the LNode tree, taking into account the place where a node is
|
||||
* projected (in the shadow DOM) rather than where it comes from (in the light DOM).
|
||||
|
@ -146,27 +148,41 @@ function walkLNodeTree(
|
|||
* If there is no sibling node, then it goes to the next sibling of the parent node...
|
||||
* until it reaches rootNode (at which point null is returned).
|
||||
*/
|
||||
let currentNode: LNode|null = node;
|
||||
node = getNextLNodeWithProjection(currentNode);
|
||||
while (currentNode && !node) {
|
||||
// if node.pNextOrParent is not null here, it is not the next node
|
||||
// (because, at this point, nextNode is null, so it is the parent)
|
||||
currentNode = currentNode.pNextOrParent || getParentLNode(currentNode);
|
||||
if (currentNode === rootNode) {
|
||||
return null;
|
||||
while (node && !nextNode) {
|
||||
node = getParentLNode(node);
|
||||
if (node === null || node === rootNode) return null;
|
||||
|
||||
// When exiting a container, the beforeNode must be restored to the previous value
|
||||
if (!node.tNode.next && nodeType === TNodeType.Container) {
|
||||
beforeNode = node.native;
|
||||
}
|
||||
// When the walker exits a container, the beforeNode has to be restored to the previous
|
||||
// value.
|
||||
if (currentNode && !currentNode.pNextOrParent &&
|
||||
currentNode.tNode.type === TNodeType.Container) {
|
||||
beforeNode = currentNode.native;
|
||||
nextNode = getNextLNode(node);
|
||||
}
|
||||
node = currentNode && getNextLNodeWithProjection(currentNode);
|
||||
}
|
||||
} else {
|
||||
node = nextNode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given a current view, finds the nearest component's host (LElement).
|
||||
*
|
||||
* @param lViewData LViewData for which we want a host element node
|
||||
* @returns The host node
|
||||
*/
|
||||
export function findComponentHost(lViewData: LViewData): LElementNode {
|
||||
let viewRootLNode = lViewData[HOST_NODE];
|
||||
|
||||
while (viewRootLNode.tNode.type === TNodeType.View) {
|
||||
ngDevMode && assertDefined(lViewData[PARENT], 'lViewData.parent');
|
||||
lViewData = lViewData[PARENT] !;
|
||||
viewRootLNode = lViewData[HOST_NODE];
|
||||
}
|
||||
|
||||
ngDevMode && assertNodeType(viewRootLNode, TNodeType.Element);
|
||||
ngDevMode && assertDefined(viewRootLNode.data, 'node.data');
|
||||
|
||||
return viewRootLNode as LElementNode;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -422,6 +422,9 @@
|
|||
{
|
||||
"name": "findAttrIndexInNode"
|
||||
},
|
||||
{
|
||||
"name": "findComponentHost"
|
||||
},
|
||||
{
|
||||
"name": "findDirectiveMatches"
|
||||
},
|
||||
|
@ -452,9 +455,6 @@
|
|||
{
|
||||
"name": "getNextLNode"
|
||||
},
|
||||
{
|
||||
"name": "getNextLNodeWithProjection"
|
||||
},
|
||||
{
|
||||
"name": "getOrCreateContainerRef"
|
||||
},
|
||||
|
@ -605,6 +605,9 @@
|
|||
{
|
||||
"name": "notImplemented"
|
||||
},
|
||||
{
|
||||
"name": "projectionNodeStack"
|
||||
},
|
||||
{
|
||||
"name": "queueComponentIndexForCheck"
|
||||
},
|
||||
|
|
|
@ -27,9 +27,9 @@ describe('content projection', () => {
|
|||
factory: () => new SimpleComponent(),
|
||||
template: function(rf: $RenderFlags$, ctx: $SimpleComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵpD(0);
|
||||
$r3$.ɵEe(1, 'div');
|
||||
$r3$.ɵP(2, 0);
|
||||
$r3$.ɵpD();
|
||||
$r3$.ɵEe(0, 'div');
|
||||
$r3$.ɵP(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -56,11 +56,11 @@ describe('content projection', () => {
|
|||
factory: () => new ComplexComponent(),
|
||||
template: function(rf: $RenderFlags$, ctx: $ComplexComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵpD(0, $pD_0P$, $pD_0R$);
|
||||
$r3$.ɵEe(1, 'div', ['id', 'first']);
|
||||
$r3$.ɵP(2, 0, 1);
|
||||
$r3$.ɵEe(3, 'div', ['id', 'second']);
|
||||
$r3$.ɵP(4, 0, 2);
|
||||
$r3$.ɵpD($pD_0P$, $pD_0R$);
|
||||
$r3$.ɵEe(0, 'div', ['id', 'first']);
|
||||
$r3$.ɵP(1, 1);
|
||||
$r3$.ɵEe(2, 'div', ['id', 'second']);
|
||||
$r3$.ɵP(3, 2);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -124,9 +124,9 @@ describe('queries', () => {
|
|||
template: function ContentQueryComponent_Template(
|
||||
rf: $number$, ctx: $ContentQueryComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵpD(0);
|
||||
$r3$.ɵE(1, 'div');
|
||||
$r3$.ɵP(2, 0);
|
||||
$r3$.ɵpD();
|
||||
$r3$.ɵE(0, 'div');
|
||||
$r3$.ɵP(1);
|
||||
$r3$.ɵe();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
import {SelectorFlags} from '@angular/core/src/render3/interfaces/projection';
|
||||
|
||||
import {Input, TemplateRef, ViewContainerRef, ViewRef} from '../../src/core';
|
||||
import {defineDirective} from '../../src/render3/definition';
|
||||
import {injectTemplateRef, injectViewContainerRef} from '../../src/render3/di';
|
||||
import {AttributeMarker, detectChanges} from '../../src/render3/index';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, loadDirective, projection, projectionDef, text} from '../../src/render3/instructions';
|
||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
|
@ -22,9 +25,9 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -48,8 +51,8 @@ describe('content projection', () => {
|
|||
/** <ng-content></ng-content> */
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
projection(1, 0);
|
||||
projectionDef();
|
||||
projection(0);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -66,13 +69,47 @@ describe('content projection', () => {
|
|||
expect(toHtml(parent)).toEqual('<child>content</child>');
|
||||
});
|
||||
|
||||
it('should project content with siblings', () => {
|
||||
/** <ng-content></ng-content> */
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef();
|
||||
projection(0);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* <child>
|
||||
* before
|
||||
* <div>content</div>
|
||||
* after
|
||||
* </child>
|
||||
*/
|
||||
const Parent = createComponent('parent', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'child');
|
||||
{
|
||||
text(1, 'before');
|
||||
elementStart(2, 'div');
|
||||
{ text(3, 'content'); }
|
||||
elementEnd();
|
||||
text(4, 'after');
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
}, [Child]);
|
||||
|
||||
const parent = renderComponent(Parent);
|
||||
expect(toHtml(parent)).toEqual('<child>before<div>content</div>after</child>');
|
||||
});
|
||||
|
||||
it('should re-project content when root.', () => {
|
||||
/** <div><ng-content></ng-content></div> */
|
||||
const GrandChild = createComponent('grand-child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -80,9 +117,9 @@ describe('content projection', () => {
|
|||
/** <grand-child><ng-content></ng-content></grand-child> */
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'grand-child');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'grand-child');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
}
|
||||
}, [GrandChild]);
|
||||
|
@ -111,9 +148,9 @@ describe('content projection', () => {
|
|||
/** <div><ng-content></ng-content></div> */
|
||||
const Child = createComponent('child', (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -149,9 +186,9 @@ describe('content projection', () => {
|
|||
/** <div><ng-content></ng-content></div> */
|
||||
const Child = createComponent('child', (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -159,9 +196,9 @@ describe('content projection', () => {
|
|||
/** <p><ng-content></ng-content></p> */
|
||||
const ProjectedComp = createComponent('projected-comp', (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'p');
|
||||
projection(2, 0);
|
||||
projectionDef();
|
||||
elementStart(0, 'p');
|
||||
projection(1);
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -198,13 +235,13 @@ describe('content projection', () => {
|
|||
'<child><div><projected-comp><p><div>Some content</div>Other content</p></projected-comp></div></child>');
|
||||
});
|
||||
|
||||
it('should project content with container.', () => {
|
||||
it('should project containers', () => {
|
||||
/** <div> <ng-content></ng-content></div> */
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -247,18 +284,20 @@ describe('content projection', () => {
|
|||
expect(toHtml(parent)).toEqual('<child><div>()</div></child>');
|
||||
parent.value = true;
|
||||
detectChanges(parent);
|
||||
|
||||
expect(toHtml(parent)).toEqual('<child><div>(content)</div></child>');
|
||||
parent.value = false;
|
||||
detectChanges(parent);
|
||||
|
||||
expect(toHtml(parent)).toEqual('<child><div>()</div></child>');
|
||||
});
|
||||
|
||||
it('should project content with container into root', () => {
|
||||
it('should project containers into root', () => {
|
||||
/** <ng-content></ng-content> */
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
projection(1, 0);
|
||||
projectionDef();
|
||||
projection(0);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -302,13 +341,13 @@ describe('content projection', () => {
|
|||
expect(toHtml(parent)).toEqual('<child></child>');
|
||||
});
|
||||
|
||||
it('should project content with container and if-else.', () => {
|
||||
it('should project containers with if-else.', () => {
|
||||
/** <div><ng-content></ng-content></div> */
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -378,19 +417,19 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
{ container(2); }
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ container(1); }
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
containerRefreshStart(2);
|
||||
containerRefreshStart(1);
|
||||
{
|
||||
if (!ctx.skipContent) {
|
||||
let rf0 = embeddedViewStart(0);
|
||||
if (rf0 & RenderFlags.Create) {
|
||||
elementStart(0, 'span');
|
||||
projection(1, 0);
|
||||
projection(1);
|
||||
elementEnd();
|
||||
}
|
||||
embeddedViewEnd();
|
||||
|
@ -400,27 +439,90 @@ describe('content projection', () => {
|
|||
}
|
||||
});
|
||||
|
||||
/** <child>content</child> */
|
||||
/**
|
||||
* <child>
|
||||
* <div>text</div>
|
||||
* content
|
||||
* </child>
|
||||
*/
|
||||
const Parent = createComponent('parent', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'child');
|
||||
{
|
||||
childCmptInstance = loadDirective(0);
|
||||
text(1, 'content');
|
||||
elementStart(1, 'div');
|
||||
{ text(2, 'text'); }
|
||||
elementEnd();
|
||||
text(3, 'content');
|
||||
}
|
||||
elementEnd();
|
||||
|
||||
// testing
|
||||
childCmptInstance = loadDirective(0);
|
||||
}
|
||||
}, [Child]);
|
||||
|
||||
const parent = renderComponent(Parent);
|
||||
expect(toHtml(parent)).toEqual('<child><div><span>content</span></div></child>');
|
||||
expect(toHtml(parent)).toEqual('<child><div><span><div>text</div>content</span></div></child>');
|
||||
|
||||
childCmptInstance.skipContent = true;
|
||||
detectChanges(parent);
|
||||
expect(toHtml(parent)).toEqual('<child><div></div></child>');
|
||||
});
|
||||
|
||||
it('should support projection in embedded views when ng-content is a root node of an embedded view',
|
||||
it('should support projection into embedded views when no projected nodes', () => {
|
||||
let childCmptInstance: any;
|
||||
|
||||
/**
|
||||
* <div>
|
||||
* % if (!skipContent) {
|
||||
* <ng-content></ng-content>
|
||||
* text
|
||||
* % }
|
||||
* </div>
|
||||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ container(1); }
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
containerRefreshStart(1);
|
||||
{
|
||||
if (!ctx.skipContent) {
|
||||
let rf0 = embeddedViewStart(0);
|
||||
if (rf0 & RenderFlags.Create) {
|
||||
projection(0);
|
||||
text(1, 'text');
|
||||
}
|
||||
embeddedViewEnd();
|
||||
}
|
||||
}
|
||||
containerRefreshEnd();
|
||||
}
|
||||
});
|
||||
|
||||
/** <child></child> */
|
||||
const Parent = createComponent('parent', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'child');
|
||||
elementEnd();
|
||||
|
||||
// testing
|
||||
childCmptInstance = loadDirective(0);
|
||||
}
|
||||
}, [Child]);
|
||||
|
||||
const parent = renderComponent(Parent);
|
||||
expect(toHtml(parent)).toEqual('<child><div>text</div></child>');
|
||||
|
||||
childCmptInstance.skipContent = true;
|
||||
detectChanges(parent);
|
||||
expect(toHtml(parent)).toEqual('<child><div></div></child>');
|
||||
});
|
||||
|
||||
it('should support projection into embedded views when ng-content is a root node of an embedded view',
|
||||
() => {
|
||||
let childCmptInstance: any;
|
||||
|
||||
|
@ -433,18 +535,18 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
{ container(2); }
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ container(1); }
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
containerRefreshStart(2);
|
||||
containerRefreshStart(1);
|
||||
{
|
||||
if (!ctx.skipContent) {
|
||||
let rf0 = embeddedViewStart(0);
|
||||
if (rf0 & RenderFlags.Create) {
|
||||
projection(0, 0);
|
||||
projection(0);
|
||||
}
|
||||
embeddedViewEnd();
|
||||
}
|
||||
|
@ -487,22 +589,22 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{
|
||||
text(2, 'Before (inside)-');
|
||||
container(3);
|
||||
text(4, '-After (inside)');
|
||||
text(1, 'Before (inside)-');
|
||||
container(2);
|
||||
text(3, '-After (inside)');
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
containerRefreshStart(3);
|
||||
containerRefreshStart(2);
|
||||
{
|
||||
if (!ctx.skipContent) {
|
||||
let rf0 = embeddedViewStart(0);
|
||||
if (rf0 & RenderFlags.Create) {
|
||||
projection(0, 0);
|
||||
projection(0);
|
||||
}
|
||||
embeddedViewEnd();
|
||||
}
|
||||
|
@ -567,18 +669,18 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
{ container(2); }
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ container(1); }
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
containerRefreshStart(2);
|
||||
containerRefreshStart(1);
|
||||
{
|
||||
if (!ctx.skipContent) {
|
||||
let rf0 = embeddedViewStart(0);
|
||||
if (rf0 & RenderFlags.Create) {
|
||||
projection(0, 0);
|
||||
projection(0);
|
||||
}
|
||||
embeddedViewEnd();
|
||||
}
|
||||
|
@ -598,22 +700,22 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Parent = createComponent('parent', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'child');
|
||||
projectionDef();
|
||||
elementStart(0, 'child');
|
||||
{
|
||||
text(2, 'Before text');
|
||||
container(3);
|
||||
text(4, '-After text');
|
||||
text(1, 'Before text');
|
||||
container(2);
|
||||
text(3, '-After text');
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
containerRefreshStart(3);
|
||||
containerRefreshStart(2);
|
||||
{
|
||||
if (!ctx.skipContent) {
|
||||
let rf0 = embeddedViewStart(0);
|
||||
if (rf0 & RenderFlags.Create) {
|
||||
projection(0, 0);
|
||||
projection(0);
|
||||
}
|
||||
embeddedViewEnd();
|
||||
}
|
||||
|
@ -648,8 +750,7 @@ describe('content projection', () => {
|
|||
.toEqual('<parent><child><div>Before text-After text</div></child></parent>');
|
||||
});
|
||||
|
||||
|
||||
it('should support projection in embedded views when ng-content is a root node of an embedded view, with other nodes after',
|
||||
it('should support projection into embedded views when ng-content is a root node of an embedded view, with other nodes after',
|
||||
() => {
|
||||
let childCmptInstance: any;
|
||||
|
||||
|
@ -662,19 +763,19 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
{ container(2); }
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ container(1); }
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
containerRefreshStart(2);
|
||||
containerRefreshStart(1);
|
||||
{
|
||||
if (!ctx.skipContent) {
|
||||
let rf0 = embeddedViewStart(0);
|
||||
if (rf0 & RenderFlags.Create) {
|
||||
text(0, 'before-');
|
||||
projection(1, 0);
|
||||
projection(1);
|
||||
text(2, '-after');
|
||||
}
|
||||
embeddedViewEnd();
|
||||
|
@ -732,19 +833,19 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
text(1, 'Before-');
|
||||
container(2, IfTemplate, '', [AttributeMarker.SelectOnly, 'ngIf']);
|
||||
text(3, '-After');
|
||||
projectionDef();
|
||||
text(0, 'Before-');
|
||||
container(1, IfTemplate, '', [AttributeMarker.SelectOnly, 'ngIf']);
|
||||
text(2, '-After');
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(2, 'ngIf', bind(ctx.showing));
|
||||
elementProperty(1, 'ngIf', bind(ctx.showing));
|
||||
}
|
||||
|
||||
function IfTemplate(rf1: RenderFlags, ctx1: any) {
|
||||
if (rf1 & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
projection(1, 0);
|
||||
projectionDef();
|
||||
projection(0);
|
||||
}
|
||||
}
|
||||
}, [NgIf]);
|
||||
|
@ -817,19 +918,19 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
text(1, 'Before-');
|
||||
container(2, IfTemplate, '', [AttributeMarker.SelectOnly, 'ngIf']);
|
||||
text(3, '-After');
|
||||
projectionDef();
|
||||
text(0, 'Before-');
|
||||
container(1, IfTemplate, '', [AttributeMarker.SelectOnly, 'ngIf']);
|
||||
text(2, '-After');
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(2, 'ngIf', bind(ctx.showing));
|
||||
elementProperty(1, 'ngIf', bind(ctx.showing));
|
||||
}
|
||||
|
||||
function IfTemplate(rf1: RenderFlags, ctx1: any) {
|
||||
if (rf1 & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
projection(1, 0);
|
||||
projectionDef();
|
||||
projection(0);
|
||||
}
|
||||
}
|
||||
}, [NgIf]);
|
||||
|
@ -878,12 +979,12 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
elementStart(3, 'span');
|
||||
{ projection(4, 0); }
|
||||
elementStart(2, 'span');
|
||||
{ projection(3); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -924,19 +1025,19 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
projection(1, 0);
|
||||
elementStart(2, 'div');
|
||||
{ container(3); }
|
||||
projectionDef();
|
||||
projection(0);
|
||||
elementStart(1, 'div');
|
||||
{ container(2); }
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
containerRefreshStart(3);
|
||||
containerRefreshStart(2);
|
||||
{
|
||||
if (ctx.show) {
|
||||
let rf0 = embeddedViewStart(0);
|
||||
if (rf0 & RenderFlags.Create) {
|
||||
projection(0, 0);
|
||||
projection(0);
|
||||
}
|
||||
embeddedViewEnd();
|
||||
}
|
||||
|
@ -970,10 +1071,10 @@ describe('content projection', () => {
|
|||
it('should project with multiple instances of a component with projection', () => {
|
||||
const ProjectionComp = createComponent('projection-comp', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
text(1, 'Before');
|
||||
projection(2, 0);
|
||||
text(3, 'After');
|
||||
projectionDef();
|
||||
text(0, 'Before');
|
||||
projection(1);
|
||||
text(2, 'After');
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -990,20 +1091,24 @@ describe('content projection', () => {
|
|||
const AppComp = createComponent('app-comp', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'projection-comp');
|
||||
{
|
||||
elementStart(1, 'div');
|
||||
text(2, 'A');
|
||||
{ text(2, 'A'); }
|
||||
elementEnd();
|
||||
elementStart(3, 'p');
|
||||
text(4, '123');
|
||||
{ text(4, '123'); }
|
||||
elementEnd();
|
||||
}
|
||||
elementEnd();
|
||||
elementStart(5, 'projection-comp');
|
||||
{
|
||||
elementStart(6, 'div');
|
||||
text(7, 'B');
|
||||
{ text(7, 'B'); }
|
||||
elementEnd();
|
||||
elementStart(8, 'p');
|
||||
text(9, '456');
|
||||
{ text(9, '456'); }
|
||||
elementEnd();
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
}, [ProjectionComp]);
|
||||
|
@ -1012,7 +1117,8 @@ describe('content projection', () => {
|
|||
fixture.update();
|
||||
expect(fixture.html)
|
||||
.toEqual(
|
||||
'<projection-comp>Before<div>A</div><p>123</p>After</projection-comp><projection-comp>Before<div>B</div><p>456</p>After</projection-comp>');
|
||||
'<projection-comp>Before<div>A</div><p>123</p>After</projection-comp>' +
|
||||
'<projection-comp>Before<div>B</div><p>456</p>After</projection-comp>');
|
||||
});
|
||||
|
||||
it('should re-project with multiple instances of a component with projection', () => {
|
||||
|
@ -1023,10 +1129,10 @@ describe('content projection', () => {
|
|||
*/
|
||||
const ProjectionComp = createComponent('projection-comp', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
text(1, 'Before');
|
||||
projection(2, 0);
|
||||
text(3, 'After');
|
||||
projectionDef();
|
||||
text(0, 'Before');
|
||||
projection(1);
|
||||
text(2, 'After');
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1043,23 +1149,27 @@ describe('content projection', () => {
|
|||
*/
|
||||
const ProjectionParent = createComponent('parent-comp', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'projection-comp');
|
||||
elementStart(2, 'div');
|
||||
text(3, 'A');
|
||||
projectionDef();
|
||||
elementStart(0, 'projection-comp');
|
||||
{
|
||||
elementStart(1, 'div');
|
||||
{ text(2, 'A'); }
|
||||
elementEnd();
|
||||
projection(4, 0);
|
||||
elementStart(5, 'p');
|
||||
text(6, '123');
|
||||
projection(3, 0);
|
||||
elementStart(4, 'p');
|
||||
{ text(5, '123'); }
|
||||
elementEnd();
|
||||
}
|
||||
elementEnd();
|
||||
elementStart(7, 'projection-comp');
|
||||
elementStart(8, 'div');
|
||||
text(9, 'B');
|
||||
elementStart(6, 'projection-comp');
|
||||
{
|
||||
elementStart(7, 'div');
|
||||
{ text(8, 'B'); }
|
||||
elementEnd();
|
||||
elementStart(10, 'p');
|
||||
text(11, '456');
|
||||
elementStart(9, 'p');
|
||||
{ text(10, '456'); }
|
||||
elementEnd();
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
}, [ProjectionComp]);
|
||||
|
@ -1075,10 +1185,10 @@ describe('content projection', () => {
|
|||
const AppComp = createComponent('app-comp', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'parent-comp');
|
||||
text(1, '**ABC**');
|
||||
{ text(1, '**ABC**'); }
|
||||
elementEnd();
|
||||
elementStart(2, 'parent-comp');
|
||||
text(3, '**DEF**');
|
||||
{ text(3, '**DEF**'); }
|
||||
elementEnd();
|
||||
}
|
||||
}, [ProjectionParent]);
|
||||
|
@ -1087,7 +1197,12 @@ describe('content projection', () => {
|
|||
fixture.update();
|
||||
expect(fixture.html)
|
||||
.toEqual(
|
||||
'<parent-comp><projection-comp>Before<div>A</div>**ABC**<p>123</p>After</projection-comp><projection-comp>Before<div>B</div><p>456</p>After</projection-comp></parent-comp><parent-comp><projection-comp>Before<div>A</div>**DEF**<p>123</p>After</projection-comp><projection-comp>Before<div>B</div><p>456</p>After</projection-comp></parent-comp>');
|
||||
'<parent-comp>' +
|
||||
'<projection-comp>Before<div>A</div>**ABC**<p>123</p>After</projection-comp>' +
|
||||
'<projection-comp>Before<div>B</div><p>456</p>After</projection-comp></parent-comp>' +
|
||||
'<parent-comp>' +
|
||||
'<projection-comp>Before<div>A</div>**DEF**<p>123</p>After</projection-comp>' +
|
||||
'<projection-comp>Before<div>B</div><p>456</p>After</projection-comp></parent-comp>');
|
||||
});
|
||||
|
||||
describe('with selectors', () => {
|
||||
|
@ -1100,13 +1215,13 @@ describe('content projection', () => {
|
|||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(
|
||||
0, [[['span', 'title', 'toFirst']], [['span', 'title', 'toSecond']]],
|
||||
[[['span', 'title', 'toFirst']], [['span', 'title', 'toSecond']]],
|
||||
['span[title=toFirst]', 'span[title=toSecond]']);
|
||||
elementStart(1, 'div', ['id', 'first']);
|
||||
{ projection(2, 0, 1); }
|
||||
elementStart(0, 'div', ['id', 'first']);
|
||||
{ projection(1, 1); }
|
||||
elementEnd();
|
||||
elementStart(3, 'div', ['id', 'second']);
|
||||
{ projection(4, 0, 2); }
|
||||
elementStart(2, 'div', ['id', 'second']);
|
||||
{ projection(3, 2); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -1145,8 +1260,8 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0, [[['', 'title', '']]], ['[title]']);
|
||||
{ projection(1, 0, 1); }
|
||||
projectionDef([[['', 'title', '']]], ['[title]']);
|
||||
{ projection(0, 1); }
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1183,17 +1298,16 @@ describe('content projection', () => {
|
|||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(
|
||||
0,
|
||||
[
|
||||
[['span', SelectorFlags.CLASS, 'toFirst']],
|
||||
[['span', SelectorFlags.CLASS, 'toSecond']]
|
||||
],
|
||||
['span.toFirst', 'span.toSecond']);
|
||||
elementStart(1, 'div', ['id', 'first']);
|
||||
{ projection(2, 0, 1); }
|
||||
elementStart(0, 'div', ['id', 'first']);
|
||||
{ projection(1, 1); }
|
||||
elementEnd();
|
||||
elementStart(3, 'div', ['id', 'second']);
|
||||
{ projection(4, 0, 2); }
|
||||
elementStart(2, 'div', ['id', 'second']);
|
||||
{ projection(3, 2); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -1233,17 +1347,16 @@ describe('content projection', () => {
|
|||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(
|
||||
0,
|
||||
[
|
||||
[['span', SelectorFlags.CLASS, 'toFirst']],
|
||||
[['span', SelectorFlags.CLASS, 'toSecond']]
|
||||
],
|
||||
['span.toFirst', 'span.toSecond']);
|
||||
elementStart(1, 'div', ['id', 'first']);
|
||||
{ projection(2, 0, 1); }
|
||||
elementStart(0, 'div', ['id', 'first']);
|
||||
{ projection(1, 1); }
|
||||
elementEnd();
|
||||
elementStart(3, 'div', ['id', 'second']);
|
||||
{ projection(4, 0, 2); }
|
||||
elementStart(2, 'div', ['id', 'second']);
|
||||
{ projection(3, 2); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -1283,13 +1396,12 @@ describe('content projection', () => {
|
|||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(
|
||||
0, [[['span']], [['span', SelectorFlags.CLASS, 'toSecond']]],
|
||||
['span', 'span.toSecond']);
|
||||
elementStart(1, 'div', ['id', 'first']);
|
||||
{ projection(2, 0, 1); }
|
||||
[[['span']], [['span', SelectorFlags.CLASS, 'toSecond']]], ['span', 'span.toSecond']);
|
||||
elementStart(0, 'div', ['id', 'first']);
|
||||
{ projection(1, 1); }
|
||||
elementEnd();
|
||||
elementStart(3, 'div', ['id', 'second']);
|
||||
{ projection(4, 0, 2); }
|
||||
elementStart(2, 'div', ['id', 'second']);
|
||||
{ projection(3, 2); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -1328,12 +1440,12 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0, [[['span', SelectorFlags.CLASS, 'toFirst']]], ['span.toFirst']);
|
||||
elementStart(1, 'div', ['id', 'first']);
|
||||
{ projection(2, 0, 1); }
|
||||
projectionDef([[['span', SelectorFlags.CLASS, 'toFirst']]], ['span.toFirst']);
|
||||
elementStart(0, 'div', ['id', 'first']);
|
||||
{ projection(1, 1); }
|
||||
elementEnd();
|
||||
elementStart(3, 'div', ['id', 'second']);
|
||||
{ projection(4, 0); }
|
||||
elementStart(2, 'div', ['id', 'second']);
|
||||
{ projection(3); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -1373,12 +1485,12 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0, [[['span', SelectorFlags.CLASS, 'toSecond']]], ['span.toSecond']);
|
||||
elementStart(1, 'div', ['id', 'first']);
|
||||
{ projection(2, 0); }
|
||||
projectionDef([[['span', SelectorFlags.CLASS, 'toSecond']]], ['span.toSecond']);
|
||||
elementStart(0, 'div', ['id', 'first']);
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
elementStart(3, 'div', ['id', 'second']);
|
||||
{ projection(4, 0, 1); }
|
||||
elementStart(2, 'div', ['id', 'second']);
|
||||
{ projection(3, 1); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -1425,11 +1537,11 @@ describe('content projection', () => {
|
|||
*/
|
||||
const GrandChild = createComponent('grand-child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0, [[['span']]], ['span']);
|
||||
projection(1, 0, 1);
|
||||
elementStart(2, 'hr');
|
||||
projectionDef([[['span']]], ['span']);
|
||||
projection(0, 1);
|
||||
elementStart(1, 'hr');
|
||||
elementEnd();
|
||||
projection(3, 0, 0);
|
||||
projection(2);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1441,12 +1553,12 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'grand-child');
|
||||
projectionDef();
|
||||
elementStart(0, 'grand-child');
|
||||
{
|
||||
projection(2, 0);
|
||||
elementStart(3, 'span');
|
||||
{ text(4, 'in child template'); }
|
||||
projection(1);
|
||||
elementStart(2, 'span');
|
||||
{ text(3, 'in child template'); }
|
||||
elementEnd();
|
||||
}
|
||||
elementEnd();
|
||||
|
@ -1488,12 +1600,12 @@ describe('content projection', () => {
|
|||
const Card = createComponent('card', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(
|
||||
0, [[['', 'card-title', '']], [['', 'card-content', '']]],
|
||||
[[['', 'card-title', '']], [['', 'card-content', '']]],
|
||||
['[card-title]', '[card-content]']);
|
||||
projection(1, 0, 1);
|
||||
elementStart(2, 'hr');
|
||||
projection(0, 1);
|
||||
elementStart(1, 'hr');
|
||||
elementEnd();
|
||||
projection(3, 0, 2);
|
||||
projection(2, 2);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1505,13 +1617,13 @@ describe('content projection', () => {
|
|||
*/
|
||||
const CardWithTitle = createComponent('card-with-title', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'card');
|
||||
projectionDef();
|
||||
elementStart(0, 'card');
|
||||
{
|
||||
elementStart(2, 'h1', ['card-title', '']);
|
||||
{ text(3, 'Title'); }
|
||||
elementStart(1, 'h1', ['card-title', '']);
|
||||
{ text(2, 'Title'); }
|
||||
elementEnd();
|
||||
projection(4, 0, 0, ['card-content', '']);
|
||||
projection(3, 0, ['card-content', '']);
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
|
@ -1547,12 +1659,12 @@ describe('content projection', () => {
|
|||
const Card = createComponent('card', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(
|
||||
0, [[['', 'card-title', '']], [['', 'card-content', '']]],
|
||||
[[['', 'card-title', '']], [['', 'card-content', '']]],
|
||||
['[card-title]', '[card-content]']);
|
||||
projection(1, 0, 1);
|
||||
elementStart(2, 'hr');
|
||||
projection(0, 1);
|
||||
elementStart(1, 'hr');
|
||||
elementEnd();
|
||||
projection(3, 0, 2);
|
||||
projection(2, 2);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1564,13 +1676,13 @@ describe('content projection', () => {
|
|||
*/
|
||||
const CardWithTitle = createComponent('card-with-title', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'card');
|
||||
projectionDef();
|
||||
elementStart(0, 'card');
|
||||
{
|
||||
elementStart(2, 'h1', ['ngProjectAs', '[card-title]']);
|
||||
{ text(3, 'Title'); }
|
||||
elementStart(1, 'h1', ['ngProjectAs', '[card-title]']);
|
||||
{ text(2, 'Title'); }
|
||||
elementEnd();
|
||||
projection(4, 0, 0, ['ngProjectAs', '[card-content]']);
|
||||
projection(3, 0, ['ngProjectAs', '[card-content]']);
|
||||
}
|
||||
elementEnd();
|
||||
}
|
||||
|
@ -1602,8 +1714,8 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0, [[['div']]], ['div']);
|
||||
projection(1, 0, 1);
|
||||
projectionDef([[['div']]], ['div']);
|
||||
projection(0, 1);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1641,9 +1753,9 @@ describe('content projection', () => {
|
|||
*/
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0, [[['div']]], ['div']);
|
||||
elementStart(1, 'span');
|
||||
{ projection(2, 0, 1); }
|
||||
projectionDef([[['div']]], ['div']);
|
||||
elementStart(0, 'span');
|
||||
{ projection(1, 1); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -947,8 +947,8 @@ describe('di', () => {
|
|||
factory: () => comp = new MyComp(injectChangeDetectorRef()),
|
||||
template: function(rf: RenderFlags, ctx: MyComp) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
projection(1, 0);
|
||||
projectionDef();
|
||||
projection(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -971,9 +971,9 @@ describe('Runtime i18n', () => {
|
|||
factory: () => new Child(),
|
||||
template: (rf: RenderFlags, cmp: Child) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'p');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'p');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
}
|
||||
}
|
||||
|
@ -1063,9 +1063,9 @@ describe('Runtime i18n', () => {
|
|||
factory: () => new Child(),
|
||||
template: (rf: RenderFlags, cmp: Child) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'p');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'p');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
}
|
||||
}
|
||||
|
@ -1145,9 +1145,9 @@ describe('Runtime i18n', () => {
|
|||
factory: () => new GrandChild(),
|
||||
template: (rf: RenderFlags, cmp: Child) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
}
|
||||
}
|
||||
|
@ -1164,9 +1164,9 @@ describe('Runtime i18n', () => {
|
|||
factory: () => new Child(),
|
||||
template: (rf: RenderFlags, cmp: Child) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'grand-child');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'grand-child');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
}
|
||||
}
|
||||
|
@ -1224,8 +1224,8 @@ describe('Runtime i18n', () => {
|
|||
factory: () => new Child(),
|
||||
template: (rf: RenderFlags, cmp: Child) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0, [[['span']]], ['span']);
|
||||
projection(1, 0, 1);
|
||||
projectionDef([[['span']]], ['span']);
|
||||
projection(0, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -502,19 +502,19 @@ describe('render3 integration test', () => {
|
|||
template: function ChildComponentTemplate(
|
||||
rf: RenderFlags, ctx: {beforeTree: Tree, afterTree: Tree}) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
container(1);
|
||||
projection(2, 0);
|
||||
container(3);
|
||||
projectionDef();
|
||||
container(0);
|
||||
projection(1);
|
||||
container(2);
|
||||
}
|
||||
containerRefreshStart(1);
|
||||
containerRefreshStart(0);
|
||||
{
|
||||
const rf0 = embeddedViewStart(0);
|
||||
{ showTree(rf0, {tree: ctx.beforeTree}); }
|
||||
embeddedViewEnd();
|
||||
}
|
||||
containerRefreshEnd();
|
||||
containerRefreshStart(3);
|
||||
containerRefreshStart(2);
|
||||
{
|
||||
const rf0 = embeddedViewStart(0);
|
||||
{ showTree(rf0, {tree: ctx.afterTree}); }
|
||||
|
|
|
@ -33,9 +33,9 @@ describe('lifecycles', () => {
|
|||
|
||||
let Comp = createOnInitComponent('comp', (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -500,27 +500,27 @@ describe('lifecycles', () => {
|
|||
|
||||
let Comp = createAfterContentInitComp('comp', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
projection(1, 0);
|
||||
projectionDef();
|
||||
projection(0);
|
||||
}
|
||||
});
|
||||
|
||||
let Parent = createAfterContentInitComp('parent', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'comp');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'comp');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(1, 'val', bind(ctx.val));
|
||||
elementProperty(0, 'val', bind(ctx.val));
|
||||
}
|
||||
}, [Comp]);
|
||||
|
||||
let ProjectedComp = createAfterContentInitComp('projected', (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
projection(1, 0);
|
||||
projectionDef();
|
||||
projection(0);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -893,9 +893,9 @@ describe('lifecycles', () => {
|
|||
|
||||
let Comp = createAfterViewInitComponent('comp', (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -1356,8 +1356,8 @@ describe('lifecycles', () => {
|
|||
|
||||
let Comp = createOnDestroyComponent('comp', (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
projection(1, 0);
|
||||
projectionDef();
|
||||
projection(0);
|
||||
}
|
||||
});
|
||||
let Parent = createOnDestroyComponent('parent', getParentTemplate('comp'), [Comp]);
|
||||
|
@ -1893,9 +1893,9 @@ describe('lifecycles', () => {
|
|||
|
||||
const Comp = createOnChangesComponent('comp', (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
}
|
||||
});
|
||||
|
@ -2444,13 +2444,13 @@ describe('lifecycles', () => {
|
|||
/** <ng-content></ng-content><view [val]="val"></view> */
|
||||
const Parent = createAllHooksComponent('parent', (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
projection(1, 0);
|
||||
elementStart(2, 'view');
|
||||
projectionDef();
|
||||
projection(0);
|
||||
elementStart(1, 'view');
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(2, 'val', bind(ctx.val));
|
||||
elementProperty(1, 'val', bind(ctx.val));
|
||||
}
|
||||
}, [View]);
|
||||
|
||||
|
|
|
@ -10,29 +10,16 @@ import {AttributeMarker, TAttributes, TNode, TNodeType} from '../../src/render3/
|
|||
|
||||
import {CssSelector, CssSelectorList, NG_PROJECT_AS_ATTR_NAME, SelectorFlags,} from '../../src/render3/interfaces/projection';
|
||||
import {getProjectAsAttrValue, isNodeMatchingSelectorList, isNodeMatchingSelector} from '../../src/render3/node_selector_matcher';
|
||||
import {createTNode} from '@angular/core/src/render3/instructions';
|
||||
|
||||
function testLStaticData(tagName: string, attrs: TAttributes | null): TNode {
|
||||
return {
|
||||
type: TNodeType.Element,
|
||||
index: 0,
|
||||
flags: 0, tagName, attrs,
|
||||
localNames: null,
|
||||
initialInputs: undefined,
|
||||
inputs: undefined,
|
||||
outputs: undefined,
|
||||
tViews: null,
|
||||
next: null,
|
||||
child: null,
|
||||
parent: null,
|
||||
dynamicContainerNode: null,
|
||||
detached: null,
|
||||
stylingTemplate: null
|
||||
};
|
||||
return createTNode(TNodeType.Element, 0, tagName, attrs, null, null);
|
||||
}
|
||||
|
||||
describe('css selector matching', () => {
|
||||
function isMatching(tagName: string, attrs: TAttributes | null, selector: CssSelector): boolean {
|
||||
return isNodeMatchingSelector(testLStaticData(tagName, attrs), selector);
|
||||
return isNodeMatchingSelector(
|
||||
createTNode(TNodeType.Element, 0, tagName, attrs, null, null), selector);
|
||||
}
|
||||
|
||||
describe('isNodeMatchingSimpleSelector', () => {
|
||||
|
|
|
@ -671,9 +671,9 @@ describe('ViewContainerRef', () => {
|
|||
factory: () => new Child(),
|
||||
template: (rf: RenderFlags, cmp: Child) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
elementStart(1, 'div');
|
||||
{ projection(2, 0); }
|
||||
projectionDef();
|
||||
elementStart(0, 'div');
|
||||
{ projection(1); }
|
||||
elementEnd();
|
||||
}
|
||||
}
|
||||
|
@ -742,17 +742,17 @@ describe('ViewContainerRef', () => {
|
|||
factory: () => new ChildWithView(),
|
||||
template: (rf: RenderFlags, cmp: ChildWithView) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0);
|
||||
text(1, 'Before (inside)-');
|
||||
container(2);
|
||||
text(3, 'After (inside)');
|
||||
projectionDef();
|
||||
text(0, 'Before (inside)-');
|
||||
container(1);
|
||||
text(2, 'After (inside)');
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
containerRefreshStart(2);
|
||||
containerRefreshStart(1);
|
||||
if (cmp.show) {
|
||||
let rf0 = embeddedViewStart(0);
|
||||
if (rf0 & RenderFlags.Create) {
|
||||
projection(0, 0);
|
||||
projection(0);
|
||||
}
|
||||
embeddedViewEnd();
|
||||
}
|
||||
|
@ -827,12 +827,12 @@ describe('ViewContainerRef', () => {
|
|||
factory: () => new ChildWithSelector(),
|
||||
template: (rf: RenderFlags, cmp: ChildWithSelector) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
projectionDef(0, [[['header']]], ['header']);
|
||||
elementStart(1, 'first');
|
||||
{ projection(2, 0, 1); }
|
||||
projectionDef([[['header']]], ['header']);
|
||||
elementStart(0, 'first');
|
||||
{ projection(1, 1); }
|
||||
elementEnd();
|
||||
elementStart(3, 'second');
|
||||
{ projection(4, 0); }
|
||||
elementStart(2, 'second');
|
||||
{ projection(3); }
|
||||
elementEnd();
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue