fix(compiler): Allow templates to access variables that are declared afterwards.
Fixes #8261
This commit is contained in:
parent
c209836fd0
commit
1e8864c4a5
|
@ -103,6 +103,7 @@ class ViewBinderVisitor implements TemplateAstVisitor {
|
||||||
bindDirectiveDestroyLifecycleCallbacks(directiveAst.directive, directiveInstance,
|
bindDirectiveDestroyLifecycleCallbacks(directiveAst.directive, directiveInstance,
|
||||||
compileElement);
|
compileElement);
|
||||||
});
|
});
|
||||||
|
bindView(compileElement.embeddedView, ast.children);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,8 +50,6 @@ import {
|
||||||
CompileTokenMetadata
|
CompileTokenMetadata
|
||||||
} from '../compile_metadata';
|
} from '../compile_metadata';
|
||||||
|
|
||||||
import {bindView} from './view_binder';
|
|
||||||
|
|
||||||
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
||||||
const CLASS_ATTR = 'class';
|
const CLASS_ATTR = 'class';
|
||||||
const STYLE_ATTR = 'style';
|
const STYLE_ATTR = 'style';
|
||||||
|
@ -65,28 +63,28 @@ export class ViewCompileDependency {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildView(view: CompileView, template: TemplateAst[],
|
export function buildView(view: CompileView, template: TemplateAst[],
|
||||||
targetDependencies: ViewCompileDependency[],
|
targetDependencies: ViewCompileDependency[]): number {
|
||||||
targetStatements: o.Statement[]): number {
|
var builderVisitor = new ViewBuilderVisitor(view, targetDependencies);
|
||||||
var builderVisitor = new ViewBuilderVisitor(view, targetDependencies, targetStatements);
|
|
||||||
templateVisitAll(builderVisitor, template, view.declarationElement.isNull() ?
|
templateVisitAll(builderVisitor, template, view.declarationElement.isNull() ?
|
||||||
view.declarationElement :
|
view.declarationElement :
|
||||||
view.declarationElement.parent);
|
view.declarationElement.parent);
|
||||||
// Need to separate binding from creation to be able to refer to
|
|
||||||
// variables that have been declared after usage.
|
|
||||||
bindView(view, template);
|
|
||||||
view.afterNodes();
|
|
||||||
|
|
||||||
createViewTopLevelStmts(view, targetStatements);
|
|
||||||
|
|
||||||
return builderVisitor.nestedViewCount;
|
return builderVisitor.nestedViewCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function finishView(view: CompileView, targetStatements: o.Statement[]) {
|
||||||
|
view.afterNodes();
|
||||||
|
createViewTopLevelStmts(view, targetStatements);
|
||||||
|
view.nodes.forEach((node) => {
|
||||||
|
if (node instanceof CompileElement && node.hasEmbeddedView) {
|
||||||
|
finishView(node.embeddedView, targetStatements);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class ViewBuilderVisitor implements TemplateAstVisitor {
|
class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||||
nestedViewCount: number = 0;
|
nestedViewCount: number = 0;
|
||||||
|
|
||||||
constructor(public view: CompileView, public targetDependencies: ViewCompileDependency[],
|
constructor(public view: CompileView, public targetDependencies: ViewCompileDependency[]) {}
|
||||||
public targetStatements: o.Statement[]) {}
|
|
||||||
|
|
||||||
private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; }
|
private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; }
|
||||||
|
|
||||||
|
@ -284,8 +282,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||||
var embeddedView = new CompileView(
|
var embeddedView = new CompileView(
|
||||||
this.view.component, this.view.genConfig, this.view.pipeMetas, o.NULL_EXPR,
|
this.view.component, this.view.genConfig, this.view.pipeMetas, o.NULL_EXPR,
|
||||||
this.view.viewIndex + this.nestedViewCount, compileElement, templateVariableBindings);
|
this.view.viewIndex + this.nestedViewCount, compileElement, templateVariableBindings);
|
||||||
this.nestedViewCount +=
|
this.nestedViewCount += buildView(embeddedView, ast.children, this.targetDependencies);
|
||||||
buildView(embeddedView, ast.children, this.targetDependencies, this.targetStatements);
|
|
||||||
|
|
||||||
compileElement.beforeChildren();
|
compileElement.beforeChildren();
|
||||||
this._addRootNodeAndProject(compileElement, ast.ngContentIndex, parent);
|
this._addRootNodeAndProject(compileElement, ast.ngContentIndex, parent);
|
||||||
|
|
|
@ -3,7 +3,8 @@ import {Injectable} from 'angular2/src/core/di';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {CompileElement} from './compile_element';
|
import {CompileElement} from './compile_element';
|
||||||
import {CompileView} from './compile_view';
|
import {CompileView} from './compile_view';
|
||||||
import {buildView, ViewCompileDependency} from './view_builder';
|
import {buildView, finishView, ViewCompileDependency} from './view_builder';
|
||||||
|
import {bindView} from './view_binder';
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata';
|
||||||
|
|
||||||
|
@ -25,7 +26,12 @@ export class ViewCompiler {
|
||||||
var dependencies = [];
|
var dependencies = [];
|
||||||
var view = new CompileView(component, this._genConfig, pipes, styles, 0,
|
var view = new CompileView(component, this._genConfig, pipes, styles, 0,
|
||||||
CompileElement.createNull(), []);
|
CompileElement.createNull(), []);
|
||||||
buildView(view, template, dependencies, statements);
|
buildView(view, template, dependencies);
|
||||||
|
// Need to separate binding from creation to be able to refer to
|
||||||
|
// variables that have been declared after usage.
|
||||||
|
bindView(view, template);
|
||||||
|
finishView(view, statements);
|
||||||
|
|
||||||
return new ViewCompileResult(statements, view.viewFactory.name, dependencies);
|
return new ViewCompileResult(statements, view.viewFactory.name, dependencies);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -587,18 +587,16 @@ function declareTests(isJit: boolean) {
|
||||||
(tcb: TestComponentBuilder, async) => {
|
(tcb: TestComponentBuilder, async) => {
|
||||||
tcb.overrideView(
|
tcb.overrideView(
|
||||||
MyComp, new ViewMetadata({
|
MyComp, new ViewMetadata({
|
||||||
template: '<p>{{alice.ctxProp}}<child-cmp var-alice></child-cmp></p>',
|
template:
|
||||||
directives: [ChildComp]
|
'<template [ngIf]="true">{{alice.ctxProp}}</template>|{{alice.ctxProp}}|<child-cmp var-alice></child-cmp>',
|
||||||
|
directives: [ChildComp, NgIf]
|
||||||
}))
|
}))
|
||||||
|
|
||||||
.createAsync(MyComp)
|
.createAsync(MyComp)
|
||||||
.then((fixture) => {
|
.then((fixture) => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(fixture.debugElement.nativeElement)
|
expect(fixture.debugElement.nativeElement).toHaveText('hello|hello|hello');
|
||||||
.toHaveText('hellohello'); // this first one is the
|
|
||||||
// component, the second one is
|
|
||||||
// the text binding
|
|
||||||
async.done();
|
async.done();
|
||||||
})}));
|
})}));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue