fix(render): keep bindings of components in content and view in the right order
Bindings in the component view have to be first, before bindings of components in the light dom (i.e. have the same order as used in the `ViewManagerUtils.createView()` method. Fixes #4522 Closes #4523
This commit is contained in:
parent
649d310c31
commit
6fe8b85295
@ -128,13 +128,13 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
|
|||||||
root = this.factory.createShadowRoot(el);
|
root = this.factory.createShadowRoot(el);
|
||||||
this.nativeShadowRoots.push(root);
|
this.nativeShadowRoots.push(root);
|
||||||
}
|
}
|
||||||
this.parentStack.push(new Component(el, root, cmd, this.factory));
|
var component = new Component(el, root, cmd, this.factory, this.allBuilders);
|
||||||
|
this.parentStack.push(component);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
visitEndComponent(context: any): any {
|
visitEndComponent(context: any): any {
|
||||||
var c = <Component<N>>this.parent;
|
var c = <Component<N>>this.parent;
|
||||||
var template = this.factory.resolveComponentTemplate(c.cmd.templateId);
|
c.build();
|
||||||
this._visitChildTemplate(template, c, c.shadowRoot);
|
|
||||||
this._endElement();
|
this._endElement();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -143,7 +143,9 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
|
|||||||
this._addChild(el, cmd.ngContentIndex);
|
this._addChild(el, cmd.ngContentIndex);
|
||||||
this.boundElements.push(el);
|
this.boundElements.push(el);
|
||||||
if (cmd.isMerged) {
|
if (cmd.isMerged) {
|
||||||
this._visitChildTemplate(cmd.children, this.parentComponent, null);
|
visitAll(
|
||||||
|
new RenderViewBuilder(this.parentComponent, null, null, this.allBuilders, this.factory),
|
||||||
|
cmd.children);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -172,11 +174,6 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
|
|||||||
|
|
||||||
private _endElement() { this.parentStack.pop(); }
|
private _endElement() { this.parentStack.pop(); }
|
||||||
|
|
||||||
private _visitChildTemplate(cmds: RenderTemplateCmd[], parent: Component<N>, rootNodesParent: N) {
|
|
||||||
visitAll(new RenderViewBuilder(parent, rootNodesParent, null, this.allBuilders, this.factory),
|
|
||||||
cmds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _addChild(node: N, ngContentIndex: number) {
|
private _addChild(node: N, ngContentIndex: number) {
|
||||||
var parent = this.parent;
|
var parent = this.parent;
|
||||||
if (isPresent(parent)) {
|
if (isPresent(parent)) {
|
||||||
@ -194,9 +191,12 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
|
|||||||
class Component<N> {
|
class Component<N> {
|
||||||
private contentNodesByNgContentIndex: N[][] = [];
|
private contentNodesByNgContentIndex: N[][] = [];
|
||||||
private projectingNgContentIndex: number = 0;
|
private projectingNgContentIndex: number = 0;
|
||||||
|
private viewBuilder: RenderViewBuilder<N>;
|
||||||
|
|
||||||
constructor(public hostElement: N, public shadowRoot: N, public cmd: RenderBeginComponentCmd,
|
constructor(public hostElement: N, shadowRoot: N, public cmd: RenderBeginComponentCmd,
|
||||||
public factory: NodeFactory<N>) {}
|
public factory: NodeFactory<N>, allBuilders: RenderViewBuilder<N>[]) {
|
||||||
|
this.viewBuilder = new RenderViewBuilder(this, shadowRoot, null, allBuilders, factory);
|
||||||
|
}
|
||||||
addContentNode(ngContentIndex: number, node: N) {
|
addContentNode(ngContentIndex: number, node: N) {
|
||||||
if (isBlank(ngContentIndex)) {
|
if (isBlank(ngContentIndex)) {
|
||||||
if (this.cmd.nativeShadow) {
|
if (this.cmd.nativeShadow) {
|
||||||
@ -215,6 +215,9 @@ class Component<N> {
|
|||||||
this.contentNodesByNgContentIndex[ngContentIndex] :
|
this.contentNodesByNgContentIndex[ngContentIndex] :
|
||||||
[];
|
[];
|
||||||
}
|
}
|
||||||
|
build() {
|
||||||
|
visitAll(this.viewBuilder, this.factory.resolveComponentTemplate(this.cmd.templateId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addAll(source: any[], target: any[]) {
|
function addAll(source: any[], target: any[]) {
|
||||||
|
@ -112,6 +112,26 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should project content components',
|
||||||
|
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||||
|
tcb.overrideView(
|
||||||
|
Simple,
|
||||||
|
new ViewMetadata(
|
||||||
|
{template: 'SIMPLE({{0}}|<ng-content></ng-content>|{{2}})', directives: []}))
|
||||||
|
.overrideView(OtherComp, new ViewMetadata({template: '{{1}}', directives: []}))
|
||||||
|
.overrideView(MainComp, new ViewMetadata({
|
||||||
|
template: '<simple><other></other></simple>',
|
||||||
|
directives: [Simple, OtherComp]
|
||||||
|
}))
|
||||||
|
.createAsync(MainComp)
|
||||||
|
.then((main) => {
|
||||||
|
|
||||||
|
main.detectChanges();
|
||||||
|
expect(main.debugElement.nativeElement).toHaveText('SIMPLE(0|1|2)');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should not show the light dom even if there is no content tag',
|
it('should not show the light dom even if there is no content tag',
|
||||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||||
tcb.overrideView(MainComp,
|
tcb.overrideView(MainComp,
|
||||||
@ -453,6 +473,12 @@ class MainComp {
|
|||||||
text: string = '';
|
text: string = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'other'})
|
||||||
|
@View({template: '', directives: []})
|
||||||
|
class OtherComp {
|
||||||
|
text: string = '';
|
||||||
|
}
|
||||||
|
|
||||||
@Component({selector: 'simple', inputs: ['stringProp']})
|
@Component({selector: 'simple', inputs: ['stringProp']})
|
||||||
@View({template: 'SIMPLE(<ng-content></ng-content>)', directives: []})
|
@View({template: 'SIMPLE(<ng-content></ng-content>)', directives: []})
|
||||||
class Simple {
|
class Simple {
|
||||||
|
@ -347,6 +347,28 @@ export function main() {
|
|||||||
.toEqual(['1.1', '1.2', '1.3', '1.4', '2.1', '2.2', '3.1', '3.1']);
|
.toEqual(['1.1', '1.2', '1.3', '1.4', '2.1', '2.2', '3.1', '3.1']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should store bound elements from the view before bound elements from content components',
|
||||||
|
() => {
|
||||||
|
componentTemplates.set(0, [
|
||||||
|
beginElement('a', ['id', '2.1'], [], true, null),
|
||||||
|
endElement(),
|
||||||
|
]);
|
||||||
|
componentTemplates.set(1, [
|
||||||
|
beginElement('a', ['id', '3.1'], [], true, null),
|
||||||
|
endElement(),
|
||||||
|
]);
|
||||||
|
var view = createRenderView(
|
||||||
|
[
|
||||||
|
beginComponent('a-comp', ['id', '1.1'], [], false, null, 0),
|
||||||
|
beginComponent('b-comp', ['id', '1.2'], [], false, null, 1),
|
||||||
|
endComponent(),
|
||||||
|
endComponent(),
|
||||||
|
],
|
||||||
|
null, nodeFactory);
|
||||||
|
|
||||||
|
expect(mapAttrs(view.boundElements, 'id')).toEqual(['1.1', '1.2', '2.1', '3.1']);
|
||||||
|
});
|
||||||
|
|
||||||
it('should store bound text nodes after the bound text nodes of the main template', () => {
|
it('should store bound text nodes after the bound text nodes of the main template', () => {
|
||||||
componentTemplates.set(0, [
|
componentTemplates.set(0, [
|
||||||
text('2.1', true, null),
|
text('2.1', true, null),
|
||||||
@ -374,6 +396,22 @@ export function main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should store bound text nodes from the view before bound text nodes from content components',
|
||||||
|
() => {
|
||||||
|
componentTemplates.set(0, [text('2.1', true, null)]);
|
||||||
|
componentTemplates.set(1, [text('3.1', true, null)]);
|
||||||
|
var view = createRenderView(
|
||||||
|
[
|
||||||
|
beginComponent('a-comp', [], [], false, null, 0),
|
||||||
|
beginComponent('b-comp', [], [], false, null, 1),
|
||||||
|
endComponent(),
|
||||||
|
endComponent(),
|
||||||
|
],
|
||||||
|
null, nodeFactory);
|
||||||
|
|
||||||
|
expect(mapText(view.boundTextNodes)).toEqual(['2.1', '3.1']);
|
||||||
|
});
|
||||||
|
|
||||||
describe('content projection', () => {
|
describe('content projection', () => {
|
||||||
it('should remove non projected nodes', () => {
|
it('should remove non projected nodes', () => {
|
||||||
componentTemplates.set(0, []);
|
componentTemplates.set(0, []);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user