parent
8555a3a3cd
commit
c973830d9a
|
@ -1710,12 +1710,14 @@ export function projection(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canInsertNativeNode(currentParent, currentView)) {
|
if (canInsertNativeNode(currentParent, currentView)) {
|
||||||
|
ngDevMode && assertNodeType(currentParent, LNodeType.Element);
|
||||||
// process each node in the list of projected nodes:
|
// process each node in the list of projected nodes:
|
||||||
let nodeToProject: LNode|null = node.data.head;
|
let nodeToProject: LNode|null = node.data.head;
|
||||||
const lastNodeToProject = node.data.tail;
|
const lastNodeToProject = node.data.tail;
|
||||||
while (nodeToProject) {
|
while (nodeToProject) {
|
||||||
appendProjectedNode(
|
appendProjectedNode(
|
||||||
nodeToProject as LTextNode | LElementNode | LContainerNode, currentParent, currentView);
|
nodeToProject as LTextNode | LElementNode | LContainerNode, currentParent as LElementNode,
|
||||||
|
currentView);
|
||||||
nodeToProject = nodeToProject === lastNodeToProject ? null : nodeToProject.pNextOrParent;
|
nodeToProject = nodeToProject === lastNodeToProject ? null : nodeToProject.pNextOrParent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -473,24 +473,24 @@ export function insertChild(node: LNode, currentView: LView): void {
|
||||||
* @param currentView Current LView
|
* @param currentView Current LView
|
||||||
*/
|
*/
|
||||||
export function appendProjectedNode(
|
export function appendProjectedNode(
|
||||||
node: LElementNode | LTextNode | LContainerNode, currentParent: LViewNode | LElementNode,
|
node: LElementNode | LTextNode | LContainerNode, currentParent: LElementNode,
|
||||||
currentView: LView): void {
|
currentView: LView): void {
|
||||||
if (node.type !== LNodeType.Container) {
|
if (node.type !== LNodeType.Container) {
|
||||||
appendChild(currentParent, (node as LElementNode | LTextNode).native, currentView);
|
appendChild(currentParent, (node as LElementNode | LTextNode).native, currentView);
|
||||||
} else if (canInsertNativeNode(currentParent, currentView)) {
|
} else {
|
||||||
// The node we are adding is a Container and we are adding it to Element which
|
// The node we are adding is a Container and we are adding it to 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).
|
||||||
// Assignee the final projection location in those cases.
|
// Assignee the final projection location in those cases.
|
||||||
const lContainer = (node as LContainerNode).data;
|
const lContainer = (node as LContainerNode).data;
|
||||||
lContainer.renderParent = currentParent as LElementNode;
|
lContainer.renderParent = currentParent;
|
||||||
const views = lContainer.views;
|
const views = lContainer.views;
|
||||||
for (let i = 0; i < views.length; i++) {
|
for (let i = 0; i < views.length; i++) {
|
||||||
addRemoveViewFromContainer(node as LContainerNode, views[i], true, null);
|
addRemoveViewFromContainer(node as LContainerNode, views[i], true, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (node.dynamicLContainerNode) {
|
if (node.dynamicLContainerNode) {
|
||||||
node.dynamicLContainerNode.data.renderParent = currentParent as LElementNode;
|
node.dynamicLContainerNode.data.renderParent = currentParent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ describe('ViewContainerRef', () => {
|
||||||
|
|
||||||
tplRef: TemplateRef<{}>;
|
tplRef: TemplateRef<{}>;
|
||||||
|
|
||||||
|
// injecting a ViewContainerRef to create a dynamic container in which embedded views will be
|
||||||
|
// created
|
||||||
constructor(public vcref: ViewContainerRef) {}
|
constructor(public vcref: ViewContainerRef) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,119 +523,123 @@ describe('ViewContainerRef', () => {
|
||||||
.toEqual('<child><div><header vcref="">blah</header><span>bar</span></div></child>');
|
.toEqual('<child><div><header vcref="">blah</header><span>bar</span></div></child>');
|
||||||
});
|
});
|
||||||
|
|
||||||
@Component({
|
describe('with select', () => {
|
||||||
selector: 'child-with-selector',
|
|
||||||
template: `
|
|
||||||
<first><ng-content select="header"></ng-content></first>
|
|
||||||
<second><ng-content></ng-content></second>`
|
|
||||||
})
|
|
||||||
class ChildWithSelector {
|
|
||||||
static ngComponentDef = defineComponent({
|
|
||||||
type: ChildWithSelector,
|
|
||||||
selectors: [['child-with-selector']],
|
|
||||||
factory: () => new ChildWithSelector(),
|
|
||||||
template: (cmp: ChildWithSelector, cm: boolean) => {
|
|
||||||
if (cm) {
|
|
||||||
projectionDef(0, [[['header']]], ['header']);
|
|
||||||
elementStart(1, 'first');
|
|
||||||
{ projection(2, 0, 1); }
|
|
||||||
elementEnd();
|
|
||||||
elementStart(3, 'second');
|
|
||||||
{ projection(4, 0); }
|
|
||||||
elementEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should project the ViewContainerRef content along its host, when the host matches a selector',
|
|
||||||
() => {
|
|
||||||
@Component({
|
|
||||||
selector: 'parent',
|
|
||||||
template: `
|
|
||||||
<ng-template #foo>
|
|
||||||
<span>{{name}}</span>
|
|
||||||
</ng-template>
|
|
||||||
<child-with-selector><header vcref [tplRef]="foo" [name]="name">blah</header></child-with-selector>`
|
|
||||||
})
|
|
||||||
class Parent {
|
|
||||||
name: string = 'bar';
|
|
||||||
static ngComponentDef = defineComponent({
|
|
||||||
type: Parent,
|
|
||||||
selectors: [['parent']],
|
|
||||||
factory: () => new Parent(),
|
|
||||||
template: (cmp: Parent, cm: boolean) => {
|
|
||||||
if (cm) {
|
|
||||||
container(0, embeddedTemplate);
|
|
||||||
elementStart(1, 'child-with-selector');
|
|
||||||
elementStart(2, 'header', ['vcref', '']);
|
|
||||||
text(3, 'blah');
|
|
||||||
elementEnd();
|
|
||||||
elementEnd();
|
|
||||||
}
|
|
||||||
const tplRef = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(0)));
|
|
||||||
elementProperty(2, 'tplRef', bind(tplRef));
|
|
||||||
elementProperty(2, 'name', bind(cmp.name));
|
|
||||||
},
|
|
||||||
directives: [ChildWithSelector, DirectiveWithVCRef]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const fixture = new ComponentFixture(Parent);
|
|
||||||
expect(fixture.html)
|
|
||||||
.toEqual(
|
|
||||||
'<child-with-selector><first><header vcref="">blah</header></first><second></second></child-with-selector>');
|
|
||||||
|
|
||||||
directiveInstance !.vcref.createEmbeddedView(
|
|
||||||
directiveInstance !.tplRef, fixture.component);
|
|
||||||
fixture.update();
|
|
||||||
expect(fixture.html)
|
|
||||||
.toEqual(
|
|
||||||
'<child-with-selector><first><header vcref="">blah</header><span>bar</span></first><second></second></child-with-selector>');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not project the ViewContainerRef content, when the host does not match a selector', () => {
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'parent',
|
selector: 'child-with-selector',
|
||||||
template: `
|
template: `
|
||||||
<ng-template #foo>
|
<first><ng-content select="header"></ng-content></first>
|
||||||
<span>{{name}}</span>
|
<second><ng-content></ng-content></second>`
|
||||||
</ng-template>
|
|
||||||
<child-with-selector><footer vcref [tplRef]="foo" [name]="name">blah</footer></child-with-selector>`
|
|
||||||
})
|
})
|
||||||
class Parent {
|
class ChildWithSelector {
|
||||||
name: string = 'bar';
|
|
||||||
static ngComponentDef = defineComponent({
|
static ngComponentDef = defineComponent({
|
||||||
type: Parent,
|
type: ChildWithSelector,
|
||||||
selectors: [['parent']],
|
selectors: [['child-with-selector']],
|
||||||
factory: () => new Parent(),
|
factory: () => new ChildWithSelector(),
|
||||||
template: (cmp: Parent, cm: boolean) => {
|
template: (cmp: ChildWithSelector, cm: boolean) => {
|
||||||
if (cm) {
|
if (cm) {
|
||||||
container(0, embeddedTemplate);
|
projectionDef(0, [[['header']]], ['header']);
|
||||||
elementStart(1, 'child-with-selector');
|
elementStart(1, 'first');
|
||||||
elementStart(2, 'footer', ['vcref', '']);
|
{ projection(2, 0, 1); }
|
||||||
text(3, 'blah');
|
|
||||||
elementEnd();
|
elementEnd();
|
||||||
|
elementStart(3, 'second');
|
||||||
|
{ projection(4, 0); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
const tplRef = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(0)));
|
}
|
||||||
elementProperty(2, 'tplRef', bind(tplRef));
|
|
||||||
elementProperty(2, 'name', bind(cmp.name));
|
|
||||||
},
|
|
||||||
directives: [ChildWithSelector, DirectiveWithVCRef]
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const fixture = new ComponentFixture(Parent);
|
it('should project the ViewContainerRef content along its host, when the host matches a selector',
|
||||||
expect(fixture.html)
|
() => {
|
||||||
.toEqual(
|
@Component({
|
||||||
'<child-with-selector><first></first><second><footer vcref="">blah</footer></second></child-with-selector>');
|
selector: 'parent',
|
||||||
|
template: `
|
||||||
|
<ng-template #foo>
|
||||||
|
<span>{{name}}</span>
|
||||||
|
</ng-template>
|
||||||
|
<child-with-selector><header vcref [tplRef]="foo" [name]="name">blah</header></child-with-selector>`
|
||||||
|
})
|
||||||
|
class Parent {
|
||||||
|
name: string = 'bar';
|
||||||
|
static ngComponentDef = defineComponent({
|
||||||
|
type: Parent,
|
||||||
|
selectors: [['parent']],
|
||||||
|
factory: () => new Parent(),
|
||||||
|
template: (cmp: Parent, cm: boolean) => {
|
||||||
|
if (cm) {
|
||||||
|
container(0, embeddedTemplate);
|
||||||
|
elementStart(1, 'child-with-selector');
|
||||||
|
elementStart(2, 'header', ['vcref', '']);
|
||||||
|
text(3, 'blah');
|
||||||
|
elementEnd();
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
const tplRef = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(0)));
|
||||||
|
elementProperty(2, 'tplRef', bind(tplRef));
|
||||||
|
elementProperty(2, 'name', bind(cmp.name));
|
||||||
|
},
|
||||||
|
directives: [ChildWithSelector, DirectiveWithVCRef]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
directiveInstance !.vcref.createEmbeddedView(directiveInstance !.tplRef, fixture.component);
|
const fixture = new ComponentFixture(Parent);
|
||||||
fixture.update();
|
expect(fixture.html)
|
||||||
expect(fixture.html)
|
.toEqual(
|
||||||
.toEqual(
|
'<child-with-selector><first><header vcref="">blah</header></first><second></second></child-with-selector>');
|
||||||
'<child-with-selector><first></first><second><footer vcref="">blah</footer><span>bar</span></second></child-with-selector>');
|
|
||||||
|
directiveInstance !.vcref.createEmbeddedView(
|
||||||
|
directiveInstance !.tplRef, fixture.component);
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual(
|
||||||
|
'<child-with-selector><first><header vcref="">blah</header><span>bar</span></first><second></second></child-with-selector>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not project the ViewContainerRef content, when the host does not match a selector',
|
||||||
|
() => {
|
||||||
|
@Component({
|
||||||
|
selector: 'parent',
|
||||||
|
template: `
|
||||||
|
<ng-template #foo>
|
||||||
|
<span>{{name}}</span>
|
||||||
|
</ng-template>
|
||||||
|
<child-with-selector><footer vcref [tplRef]="foo" [name]="name">blah</footer></child-with-selector>`
|
||||||
|
})
|
||||||
|
class Parent {
|
||||||
|
name: string = 'bar';
|
||||||
|
static ngComponentDef = defineComponent({
|
||||||
|
type: Parent,
|
||||||
|
selectors: [['parent']],
|
||||||
|
factory: () => new Parent(),
|
||||||
|
template: (cmp: Parent, cm: boolean) => {
|
||||||
|
if (cm) {
|
||||||
|
container(0, embeddedTemplate);
|
||||||
|
elementStart(1, 'child-with-selector');
|
||||||
|
elementStart(2, 'footer', ['vcref', '']);
|
||||||
|
text(3, 'blah');
|
||||||
|
elementEnd();
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
const tplRef = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(0)));
|
||||||
|
elementProperty(2, 'tplRef', bind(tplRef));
|
||||||
|
elementProperty(2, 'name', bind(cmp.name));
|
||||||
|
},
|
||||||
|
directives: [ChildWithSelector, DirectiveWithVCRef]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixture = new ComponentFixture(Parent);
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual(
|
||||||
|
'<child-with-selector><first></first><second><footer vcref="">blah</footer></second></child-with-selector>');
|
||||||
|
|
||||||
|
directiveInstance !.vcref.createEmbeddedView(
|
||||||
|
directiveInstance !.tplRef, fixture.component);
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual(
|
||||||
|
'<child-with-selector><first></first><second><footer vcref="">blah</footer><span>bar</span></second></child-with-selector>');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue