feat(core): separate refs from vars.
Introduces `ref-` to give a name to an element or a directive (also works for `<template>` elements), and `let-` to introduce an input variable for a `<template>` element. BREAKING CHANGE: - `#...` now always means `ref-`. - `<template #abc>` now defines a reference to the TemplateRef, instead of an input variable used inside of the template. - `#...` inside of a *ngIf, … directives is deprecated. Use `let …` instead. - `var-...` is deprecated. Replace with `let-...` for `<template>` elements and `ref-` for non `<template>` elements. Closes #7158 Closes #8264
This commit is contained in:
@ -58,7 +58,7 @@ import {BaseException} from "../../facade/exceptions";
|
||||
*
|
||||
* ### Syntax
|
||||
*
|
||||
* - `<li *ngFor="#item of items; #i = index">...</li>`
|
||||
* - `<li *ngFor="let item of items; #i = index">...</li>`
|
||||
* - `<li template="ngFor #item of items; #i = index">...</li>`
|
||||
* - `<template ngFor #item [ngForOf]="items" #i="index"><li>...</li></template>`
|
||||
*
|
||||
|
@ -96,7 +96,7 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||
*
|
||||
* ```
|
||||
* <select ngControl="city">
|
||||
* <option *ngFor="#c of cities" [value]="c"></option>
|
||||
* <option *ngFor="let c of cities" [value]="c"></option>
|
||||
* </select>
|
||||
* ```
|
||||
*/
|
||||
|
@ -46,7 +46,11 @@ export class Token {
|
||||
|
||||
isKeyword(): boolean { return (this.type == TokenType.Keyword); }
|
||||
|
||||
isKeywordVar(): boolean { return (this.type == TokenType.Keyword && this.strValue == "var"); }
|
||||
isKeywordDeprecatedVar(): boolean {
|
||||
return (this.type == TokenType.Keyword && this.strValue == "var");
|
||||
}
|
||||
|
||||
isKeywordLet(): boolean { return (this.type == TokenType.Keyword && this.strValue == "let"); }
|
||||
|
||||
isKeywordNull(): boolean { return (this.type == TokenType.Keyword && this.strValue == "null"); }
|
||||
|
||||
@ -464,4 +468,4 @@ var OPERATORS = SetWrapper.createFromList([
|
||||
|
||||
|
||||
var KEYWORDS =
|
||||
SetWrapper.createFromList(['var', 'null', 'undefined', 'true', 'false', 'if', 'else']);
|
||||
SetWrapper.createFromList(['var', 'let', 'null', 'undefined', 'true', 'false', 'if', 'else']);
|
||||
|
@ -62,6 +62,10 @@ export class SplitInterpolation {
|
||||
constructor(public strings: string[], public expressions: string[]) {}
|
||||
}
|
||||
|
||||
export class TemplateBindingParseResult {
|
||||
constructor(public templateBindings: TemplateBinding[], public warnings: string[]) {}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class Parser {
|
||||
constructor(/** @internal */
|
||||
@ -112,7 +116,7 @@ export class Parser {
|
||||
return new Quote(prefix, uninterpretedExpression, location);
|
||||
}
|
||||
|
||||
parseTemplateBindings(input: string, location: any): TemplateBinding[] {
|
||||
parseTemplateBindings(input: string, location: any): TemplateBindingParseResult {
|
||||
var tokens = this._lexer.tokenize(input);
|
||||
return new _ParseAST(input, location, tokens, false).parseTemplateBindings();
|
||||
}
|
||||
@ -228,16 +232,11 @@ export class _ParseAST {
|
||||
}
|
||||
}
|
||||
|
||||
optionalKeywordVar(): boolean {
|
||||
if (this.peekKeywordVar()) {
|
||||
this.advance();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
peekKeywordLet(): boolean { return this.next.isKeywordLet(); }
|
||||
|
||||
peekKeywordVar(): boolean { return this.next.isKeywordVar() || this.next.isOperator('#'); }
|
||||
peekDeprecatedKeywordVar(): boolean { return this.next.isKeywordDeprecatedVar(); }
|
||||
|
||||
peekDeprecatedOperatorHash(): boolean { return this.next.isOperator('#'); }
|
||||
|
||||
expectCharacter(code: number) {
|
||||
if (this.optionalCharacter(code)) return;
|
||||
@ -617,11 +616,23 @@ export class _ParseAST {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
parseTemplateBindings(): any[] {
|
||||
var bindings = [];
|
||||
parseTemplateBindings(): TemplateBindingParseResult {
|
||||
var bindings: TemplateBinding[] = [];
|
||||
var prefix = null;
|
||||
var warnings: string[] = [];
|
||||
while (this.index < this.tokens.length) {
|
||||
var keyIsVar: boolean = this.optionalKeywordVar();
|
||||
var keyIsVar: boolean = this.peekKeywordLet();
|
||||
if (!keyIsVar && this.peekDeprecatedKeywordVar()) {
|
||||
keyIsVar = true;
|
||||
warnings.push(`"var" inside of expressions is deprecated. Use "let" instead!`);
|
||||
}
|
||||
if (!keyIsVar && this.peekDeprecatedOperatorHash()) {
|
||||
keyIsVar = true;
|
||||
warnings.push(`"#" inside of expressions is deprecated. Use "let" instead!`);
|
||||
}
|
||||
if (keyIsVar) {
|
||||
this.advance();
|
||||
}
|
||||
var key = this.expectTemplateBindingKey();
|
||||
if (!keyIsVar) {
|
||||
if (prefix == null) {
|
||||
@ -639,7 +650,8 @@ export class _ParseAST {
|
||||
} else {
|
||||
name = '\$implicit';
|
||||
}
|
||||
} else if (this.next !== EOF && !this.peekKeywordVar()) {
|
||||
} else if (this.next !== EOF && !this.peekKeywordLet() && !this.peekDeprecatedKeywordVar() &&
|
||||
!this.peekDeprecatedOperatorHash()) {
|
||||
var start = this.inputIndex;
|
||||
var ast = this.parsePipe();
|
||||
var source = this.input.substring(start, this.inputIndex);
|
||||
@ -650,7 +662,7 @@ export class _ParseAST {
|
||||
this.optionalCharacter($COMMA);
|
||||
}
|
||||
}
|
||||
return bindings;
|
||||
return new TemplateBindingParseResult(bindings, warnings);
|
||||
}
|
||||
|
||||
error(message: string, index: number = null) {
|
||||
|
@ -17,8 +17,14 @@ export class ParseSourceSpan {
|
||||
}
|
||||
}
|
||||
|
||||
export enum ParseErrorLevel {
|
||||
WARNING,
|
||||
FATAL
|
||||
}
|
||||
|
||||
export abstract class ParseError {
|
||||
constructor(public span: ParseSourceSpan, public msg: string) {}
|
||||
constructor(public span: ParseSourceSpan, public msg: string,
|
||||
public level: ParseErrorLevel = ParseErrorLevel.FATAL) {}
|
||||
|
||||
toString(): string {
|
||||
var source = this.span.start.file.content;
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
NgContentAst,
|
||||
EmbeddedTemplateAst,
|
||||
ElementAst,
|
||||
VariableAst,
|
||||
ReferenceAst,
|
||||
BoundEventAst,
|
||||
BoundElementPropertyAst,
|
||||
AttrAst,
|
||||
@ -69,7 +69,7 @@ export class ProviderElementContext {
|
||||
|
||||
constructor(private _viewContext: ProviderViewContext, private _parent: ProviderElementContext,
|
||||
private _isViewRoot: boolean, private _directiveAsts: DirectiveAst[],
|
||||
attrs: AttrAst[], vars: VariableAst[], private _sourceSpan: ParseSourceSpan) {
|
||||
attrs: AttrAst[], refs: ReferenceAst[], private _sourceSpan: ParseSourceSpan) {
|
||||
this._attrs = {};
|
||||
attrs.forEach((attrAst) => this._attrs[attrAst.name] = attrAst.value);
|
||||
var directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive);
|
||||
@ -79,9 +79,8 @@ export class ProviderElementContext {
|
||||
var queriedTokens = new CompileTokenMap<boolean>();
|
||||
this._allProviders.values().forEach(
|
||||
(provider) => { this._addQueryReadsTo(provider.token, queriedTokens); });
|
||||
vars.forEach((varAst) => {
|
||||
var varToken = new CompileTokenMetadata({value: varAst.name});
|
||||
this._addQueryReadsTo(varToken, queriedTokens);
|
||||
refs.forEach((refAst) => {
|
||||
this._addQueryReadsTo(new CompileTokenMetadata({value: refAst.name}), queriedTokens);
|
||||
});
|
||||
if (isPresent(queriedTokens.get(identifierToken(Identifiers.ViewContainerRef)))) {
|
||||
this._hasViewContainer = true;
|
||||
|
@ -30,7 +30,6 @@ import {
|
||||
NgContentAst,
|
||||
EmbeddedTemplateAst,
|
||||
ElementAst,
|
||||
VariableAst,
|
||||
BoundEventAst,
|
||||
BoundElementPropertyAst,
|
||||
AttrAst,
|
||||
|
@ -82,7 +82,18 @@ export class BoundEventAst implements TemplateAst {
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable declaration on an element (e.g. `#var="expression"`).
|
||||
* A reference declaration on an element (e.g. `let someName="expression"`).
|
||||
*/
|
||||
export class ReferenceAst implements TemplateAst {
|
||||
constructor(public name: string, public value: CompileTokenMetadata,
|
||||
public sourceSpan: ParseSourceSpan) {}
|
||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||
return visitor.visitReference(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable declaration on a <template> (e.g. `var-someName="someLocalName"`).
|
||||
*/
|
||||
export class VariableAst implements TemplateAst {
|
||||
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
|
||||
@ -97,7 +108,7 @@ export class VariableAst implements TemplateAst {
|
||||
export class ElementAst implements TemplateAst {
|
||||
constructor(public name: string, public attrs: AttrAst[],
|
||||
public inputs: BoundElementPropertyAst[], public outputs: BoundEventAst[],
|
||||
public exportAsVars: VariableAst[], public directives: DirectiveAst[],
|
||||
public references: ReferenceAst[], public directives: DirectiveAst[],
|
||||
public providers: ProviderAst[], public hasViewContainer: boolean,
|
||||
public children: TemplateAst[], public ngContentIndex: number,
|
||||
public sourceSpan: ParseSourceSpan) {}
|
||||
@ -106,14 +117,6 @@ export class ElementAst implements TemplateAst {
|
||||
return visitor.visitElement(this, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the element has any active bindings (inputs, outputs, vars, or directives).
|
||||
*/
|
||||
isBound(): boolean {
|
||||
return (this.inputs.length > 0 || this.outputs.length > 0 || this.exportAsVars.length > 0 ||
|
||||
this.directives.length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the component associated with this element, if any.
|
||||
*/
|
||||
@ -132,7 +135,8 @@ export class ElementAst implements TemplateAst {
|
||||
* A `<template>` element included in an Angular template.
|
||||
*/
|
||||
export class EmbeddedTemplateAst implements TemplateAst {
|
||||
constructor(public attrs: AttrAst[], public outputs: BoundEventAst[], public vars: VariableAst[],
|
||||
constructor(public attrs: AttrAst[], public outputs: BoundEventAst[],
|
||||
public references: ReferenceAst[], public variables: VariableAst[],
|
||||
public directives: DirectiveAst[], public providers: ProviderAst[],
|
||||
public hasViewContainer: boolean, public children: TemplateAst[],
|
||||
public ngContentIndex: number, public sourceSpan: ParseSourceSpan) {}
|
||||
@ -160,7 +164,7 @@ export class DirectiveAst implements TemplateAst {
|
||||
constructor(public directive: CompileDirectiveMetadata,
|
||||
public inputs: BoundDirectivePropertyAst[],
|
||||
public hostProperties: BoundElementPropertyAst[], public hostEvents: BoundEventAst[],
|
||||
public exportAsVars: VariableAst[], public sourceSpan: ParseSourceSpan) {}
|
||||
public sourceSpan: ParseSourceSpan) {}
|
||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||
return visitor.visitDirective(this, context);
|
||||
}
|
||||
@ -232,6 +236,7 @@ export interface TemplateAstVisitor {
|
||||
visitNgContent(ast: NgContentAst, context: any): any;
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any;
|
||||
visitElement(ast: ElementAst, context: any): any;
|
||||
visitReference(ast: ReferenceAst, context: any): any;
|
||||
visitVariable(ast: VariableAst, context: any): any;
|
||||
visitEvent(ast: BoundEventAst, context: any): any;
|
||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any;
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
isArray
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {Injectable, Inject, OpaqueToken, Optional} from 'angular2/core';
|
||||
import {Console} from 'angular2/src/core/console';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {
|
||||
AST,
|
||||
@ -34,14 +35,14 @@ import {
|
||||
} from './compile_metadata';
|
||||
import {HtmlParser} from './html_parser';
|
||||
import {splitNsName, mergeNsAndName} from './html_tags';
|
||||
import {ParseSourceSpan, ParseError, ParseLocation} from './parse_util';
|
||||
import {ParseSourceSpan, ParseError, ParseLocation, ParseErrorLevel} from './parse_util';
|
||||
import {MAX_INTERPOLATION_VALUES} from 'angular2/src/core/linker/view_utils';
|
||||
|
||||
import {
|
||||
ElementAst,
|
||||
BoundElementPropertyAst,
|
||||
BoundEventAst,
|
||||
VariableAst,
|
||||
ReferenceAst,
|
||||
TemplateAst,
|
||||
TemplateAstVisitor,
|
||||
templateVisitAll,
|
||||
@ -54,7 +55,8 @@ import {
|
||||
DirectiveAst,
|
||||
BoundDirectivePropertyAst,
|
||||
ProviderAst,
|
||||
ProviderAstType
|
||||
ProviderAstType,
|
||||
VariableAst
|
||||
} from './template_ast';
|
||||
import {CssSelector, SelectorMatcher} from 'angular2/src/compiler/selector';
|
||||
|
||||
@ -76,19 +78,22 @@ import {
|
||||
} from './html_ast';
|
||||
|
||||
import {splitAtColon} from './util';
|
||||
import {identifierToken, Identifiers} from './identifiers';
|
||||
|
||||
import {ProviderElementContext, ProviderViewContext} from './provider_parser';
|
||||
|
||||
// Group 1 = "bind-"
|
||||
// Group 2 = "var-" or "#"
|
||||
// Group 3 = "on-"
|
||||
// Group 4 = "bindon-"
|
||||
// Group 5 = the identifier after "bind-", "var-/#", or "on-"
|
||||
// Group 6 = identifier inside [()]
|
||||
// Group 7 = identifier inside []
|
||||
// Group 8 = identifier inside ()
|
||||
// Group 2 = "var-"
|
||||
// Group 3 = "let-"
|
||||
// Group 4 = "ref-/#"
|
||||
// Group 5 = "on-"
|
||||
// Group 6 = "bindon-"
|
||||
// Group 7 = the identifier after "bind-", "var-/#", or "on-"
|
||||
// Group 8 = identifier inside [()]
|
||||
// Group 9 = identifier inside []
|
||||
// Group 10 = identifier inside ()
|
||||
var BIND_NAME_REGEXP =
|
||||
/^(?:(?:(?:(bind-)|(var-|#)|(on-)|(bindon-))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/g;
|
||||
/^(?:(?:(?:(bind-)|(var-)|(let-)|(ref-|#)|(on-)|(bindon-))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/g;
|
||||
|
||||
const TEMPLATE_ELEMENT = 'template';
|
||||
const TEMPLATE_ATTR = 'template';
|
||||
@ -112,7 +117,9 @@ var TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];
|
||||
export const TEMPLATE_TRANSFORMS = CONST_EXPR(new OpaqueToken('TemplateTransforms'));
|
||||
|
||||
export class TemplateParseError extends ParseError {
|
||||
constructor(message: string, span: ParseSourceSpan) { super(span, message); }
|
||||
constructor(message: string, span: ParseSourceSpan, level: ParseErrorLevel) {
|
||||
super(span, message, level);
|
||||
}
|
||||
}
|
||||
|
||||
export class TemplateParseResult {
|
||||
@ -122,15 +129,20 @@ export class TemplateParseResult {
|
||||
@Injectable()
|
||||
export class TemplateParser {
|
||||
constructor(private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry,
|
||||
private _htmlParser: HtmlParser,
|
||||
private _htmlParser: HtmlParser, private _console: Console,
|
||||
@Optional() @Inject(TEMPLATE_TRANSFORMS) public transforms: TemplateAstVisitor[]) {}
|
||||
|
||||
parse(component: CompileDirectiveMetadata, template: string,
|
||||
directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
||||
templateUrl: string): TemplateAst[] {
|
||||
var result = this.tryParse(component, template, directives, pipes, templateUrl);
|
||||
if (isPresent(result.errors)) {
|
||||
var errorString = result.errors.join('\n');
|
||||
var warnings = result.errors.filter(error => error.level === ParseErrorLevel.WARNING);
|
||||
var errors = result.errors.filter(error => error.level === ParseErrorLevel.FATAL);
|
||||
if (warnings.length > 0) {
|
||||
this._console.warn(`Template parse warnings:\n${warnings.join('\n')}`);
|
||||
}
|
||||
if (errors.length > 0) {
|
||||
var errorString = errors.join('\n');
|
||||
throw new BaseException(`Template parse errors:\n${errorString}`);
|
||||
}
|
||||
return result.templateAst;
|
||||
@ -162,7 +174,7 @@ export class TemplateParser {
|
||||
this.transforms.forEach(
|
||||
(transform: TemplateAstVisitor) => { result = templateVisitAll(transform, result); });
|
||||
}
|
||||
return new TemplateParseResult(result);
|
||||
return new TemplateParseResult(result, errors);
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,8 +199,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
pipes.forEach(pipe => this.pipesByName.set(pipe.name, pipe));
|
||||
}
|
||||
|
||||
private _reportError(message: string, sourceSpan: ParseSourceSpan) {
|
||||
this.errors.push(new TemplateParseError(message, sourceSpan));
|
||||
private _reportError(message: string, sourceSpan: ParseSourceSpan,
|
||||
level: ParseErrorLevel = ParseErrorLevel.FATAL) {
|
||||
this.errors.push(new TemplateParseError(message, sourceSpan, level));
|
||||
}
|
||||
|
||||
private _parseInterpolation(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
||||
@ -235,13 +248,15 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
private _parseTemplateBindings(value: string, sourceSpan: ParseSourceSpan): TemplateBinding[] {
|
||||
var sourceInfo = sourceSpan.start.toString();
|
||||
try {
|
||||
var bindings = this._exprParser.parseTemplateBindings(value, sourceInfo);
|
||||
bindings.forEach((binding) => {
|
||||
var bindingsResult = this._exprParser.parseTemplateBindings(value, sourceInfo);
|
||||
bindingsResult.templateBindings.forEach((binding) => {
|
||||
if (isPresent(binding.expression)) {
|
||||
this._checkPipes(binding.expression, sourceSpan);
|
||||
}
|
||||
});
|
||||
return bindings;
|
||||
bindingsResult.warnings.forEach(
|
||||
(warning) => { this._reportError(warning, sourceSpan, ParseErrorLevel.WARNING); });
|
||||
return bindingsResult.templateBindings;
|
||||
} catch (e) {
|
||||
this._reportError(`${e}`, sourceSpan);
|
||||
return [];
|
||||
@ -299,19 +314,25 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
|
||||
var matchableAttrs: string[][] = [];
|
||||
var elementOrDirectiveProps: BoundElementOrDirectiveProperty[] = [];
|
||||
var vars: VariableAst[] = [];
|
||||
var elementOrDirectiveRefs: ElementOrDirectiveRef[] = [];
|
||||
var elementVars: VariableAst[] = [];
|
||||
var events: BoundEventAst[] = [];
|
||||
|
||||
var templateElementOrDirectiveProps: BoundElementOrDirectiveProperty[] = [];
|
||||
var templateVars: VariableAst[] = [];
|
||||
var templateMatchableAttrs: string[][] = [];
|
||||
var templateElementVars: VariableAst[] = [];
|
||||
|
||||
var hasInlineTemplates = false;
|
||||
var attrs = [];
|
||||
var lcElName = splitNsName(nodeName.toLowerCase())[1];
|
||||
var isTemplateElement = lcElName == TEMPLATE_ELEMENT;
|
||||
|
||||
element.attrs.forEach(attr => {
|
||||
var hasBinding = this._parseAttr(attr, matchableAttrs, elementOrDirectiveProps, events, vars);
|
||||
var hasBinding =
|
||||
this._parseAttr(isTemplateElement, attr, matchableAttrs, elementOrDirectiveProps, events,
|
||||
elementOrDirectiveRefs, elementVars);
|
||||
var hasTemplateBinding = this._parseInlineTemplateBinding(
|
||||
attr, templateMatchableAttrs, templateElementOrDirectiveProps, templateVars);
|
||||
attr, templateMatchableAttrs, templateElementOrDirectiveProps, templateElementVars);
|
||||
if (!hasBinding && !hasTemplateBinding) {
|
||||
// don't include the bindings as attributes as well in the AST
|
||||
attrs.push(this.visitAttr(attr, null));
|
||||
@ -322,19 +343,18 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
}
|
||||
});
|
||||
|
||||
var lcElName = splitNsName(nodeName.toLowerCase())[1];
|
||||
var isTemplateElement = lcElName == TEMPLATE_ELEMENT;
|
||||
var elementCssSelector = createElementCssSelector(nodeName, matchableAttrs);
|
||||
var directiveMetas = this._parseDirectives(this.selectorMatcher, elementCssSelector);
|
||||
var directiveAsts =
|
||||
this._createDirectiveAsts(element.name, directiveMetas, elementOrDirectiveProps,
|
||||
isTemplateElement ? [] : vars, element.sourceSpan);
|
||||
var references: ReferenceAst[] = [];
|
||||
var directiveAsts = this._createDirectiveAsts(isTemplateElement, element.name, directiveMetas,
|
||||
elementOrDirectiveProps, elementOrDirectiveRefs,
|
||||
element.sourceSpan, references);
|
||||
var elementProps: BoundElementPropertyAst[] =
|
||||
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts);
|
||||
var isViewRoot = parent.isTemplateElement || hasInlineTemplates;
|
||||
var providerContext =
|
||||
new ProviderElementContext(this.providerViewContext, parent.providerContext, isViewRoot,
|
||||
directiveAsts, attrs, vars, element.sourceSpan);
|
||||
directiveAsts, attrs, references, element.sourceSpan);
|
||||
var children = htmlVisitAll(
|
||||
preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children,
|
||||
ElementContext.create(isTemplateElement, directiveAsts,
|
||||
@ -362,17 +382,15 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
element.sourceSpan);
|
||||
|
||||
parsedElement = new EmbeddedTemplateAst(
|
||||
attrs, events, vars, providerContext.transformedDirectiveAsts,
|
||||
attrs, events, references, elementVars, providerContext.transformedDirectiveAsts,
|
||||
providerContext.transformProviders, providerContext.transformedHasViewContainer, children,
|
||||
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
|
||||
} else {
|
||||
this._assertOnlyOneComponent(directiveAsts, element.sourceSpan);
|
||||
var elementExportAsVars = vars.filter(varAst => varAst.value.length === 0);
|
||||
let ngContentIndex =
|
||||
hasInlineTemplates ? null : parent.findNgContentIndex(projectionSelector);
|
||||
|
||||
parsedElement = new ElementAst(
|
||||
nodeName, attrs, elementProps, events, elementExportAsVars,
|
||||
nodeName, attrs, elementProps, events, references,
|
||||
providerContext.transformedDirectiveAsts, providerContext.transformProviders,
|
||||
providerContext.transformedHasViewContainer, children,
|
||||
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
|
||||
@ -381,18 +399,18 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
var templateCssSelector = createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
|
||||
var templateDirectiveMetas = this._parseDirectives(this.selectorMatcher, templateCssSelector);
|
||||
var templateDirectiveAsts =
|
||||
this._createDirectiveAsts(element.name, templateDirectiveMetas,
|
||||
templateElementOrDirectiveProps, [], element.sourceSpan);
|
||||
this._createDirectiveAsts(true, element.name, templateDirectiveMetas,
|
||||
templateElementOrDirectiveProps, [], element.sourceSpan, []);
|
||||
var templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts(
|
||||
element.name, templateElementOrDirectiveProps, templateDirectiveAsts);
|
||||
this._assertNoComponentsNorElementBindingsOnTemplate(
|
||||
templateDirectiveAsts, templateElementProps, element.sourceSpan);
|
||||
var templateProviderContext = new ProviderElementContext(
|
||||
this.providerViewContext, parent.providerContext, parent.isTemplateElement,
|
||||
templateDirectiveAsts, [], templateVars, element.sourceSpan);
|
||||
templateDirectiveAsts, [], [], element.sourceSpan);
|
||||
templateProviderContext.afterElement();
|
||||
|
||||
parsedElement = new EmbeddedTemplateAst([], [], templateVars,
|
||||
parsedElement = new EmbeddedTemplateAst([], [], [], templateElementVars,
|
||||
templateProviderContext.transformedDirectiveAsts,
|
||||
templateProviderContext.transformProviders,
|
||||
templateProviderContext.transformedHasViewContainer,
|
||||
@ -417,7 +435,6 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
var binding = bindings[i];
|
||||
if (binding.keyIsVar) {
|
||||
targetVars.push(new VariableAst(binding.key, binding.name, attr.sourceSpan));
|
||||
targetMatchableAttrs.push([binding.key, binding.name]);
|
||||
} else if (isPresent(binding.expression)) {
|
||||
this._parsePropertyAst(binding.key, binding.expression, attr.sourceSpan,
|
||||
targetMatchableAttrs, targetProps);
|
||||
@ -431,9 +448,10 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
return false;
|
||||
}
|
||||
|
||||
private _parseAttr(attr: HtmlAttrAst, targetMatchableAttrs: string[][],
|
||||
private _parseAttr(isTemplateElement: boolean, attr: HtmlAttrAst,
|
||||
targetMatchableAttrs: string[][],
|
||||
targetProps: BoundElementOrDirectiveProperty[], targetEvents: BoundEventAst[],
|
||||
targetVars: VariableAst[]): boolean {
|
||||
targetRefs: ElementOrDirectiveRef[], targetVars: VariableAst[]): boolean {
|
||||
var attrName = this._normalizeAttributeName(attr.name);
|
||||
var attrValue = attr.value;
|
||||
var bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName);
|
||||
@ -441,36 +459,55 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
if (isPresent(bindParts)) {
|
||||
hasBinding = true;
|
||||
if (isPresent(bindParts[1])) { // match: bind-prop
|
||||
this._parseProperty(bindParts[5], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetProps);
|
||||
|
||||
} else if (isPresent(
|
||||
bindParts[2])) { // match: var-name / var-name="iden" / #name / #name="iden"
|
||||
var identifier = bindParts[5];
|
||||
this._parseVariable(identifier, attrValue, attr.sourceSpan, targetVars);
|
||||
|
||||
} else if (isPresent(bindParts[3])) { // match: on-event
|
||||
this._parseEvent(bindParts[5], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetEvents);
|
||||
|
||||
} else if (isPresent(bindParts[4])) { // match: bindon-prop
|
||||
this._parseProperty(bindParts[5], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetProps);
|
||||
this._parseAssignmentEvent(bindParts[5], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetEvents);
|
||||
|
||||
} else if (isPresent(bindParts[6])) { // match: [(expr)]
|
||||
this._parseProperty(bindParts[6], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetProps);
|
||||
this._parseAssignmentEvent(bindParts[6], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetEvents);
|
||||
|
||||
} else if (isPresent(bindParts[7])) { // match: [expr]
|
||||
this._parseProperty(bindParts[7], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetProps);
|
||||
|
||||
} else if (isPresent(bindParts[8])) { // match: (event)
|
||||
this._parseEvent(bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
} else if (isPresent(bindParts[2])) { // match: var-name / var-name="iden"
|
||||
var identifier = bindParts[7];
|
||||
if (isTemplateElement) {
|
||||
this._reportError(`"var-" on <template> elements is deprecated. Use "let-" instead!`,
|
||||
attr.sourceSpan, ParseErrorLevel.WARNING);
|
||||
this._parseVariable(identifier, attrValue, attr.sourceSpan, targetVars);
|
||||
} else {
|
||||
this._reportError(`"var-" on non <template> elements is deprecated. Use "ref-" instead!`,
|
||||
attr.sourceSpan, ParseErrorLevel.WARNING);
|
||||
this._parseReference(identifier, attrValue, attr.sourceSpan, targetRefs);
|
||||
}
|
||||
|
||||
} else if (isPresent(bindParts[3])) { // match: let-name
|
||||
if (isTemplateElement) {
|
||||
var identifier = bindParts[7];
|
||||
this._parseVariable(identifier, attrValue, attr.sourceSpan, targetVars);
|
||||
} else {
|
||||
this._reportError(`"let-" is only supported on template elements.`, attr.sourceSpan);
|
||||
}
|
||||
|
||||
} else if (isPresent(bindParts[4])) { // match: ref- / #iden
|
||||
var identifier = bindParts[7];
|
||||
this._parseReference(identifier, attrValue, attr.sourceSpan, targetRefs);
|
||||
|
||||
} else if (isPresent(bindParts[5])) { // match: on-event
|
||||
this._parseEvent(bindParts[7], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetEvents);
|
||||
|
||||
} else if (isPresent(bindParts[6])) { // match: bindon-prop
|
||||
this._parseProperty(bindParts[7], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetProps);
|
||||
this._parseAssignmentEvent(bindParts[7], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetEvents);
|
||||
|
||||
} else if (isPresent(bindParts[8])) { // match: [(expr)]
|
||||
this._parseProperty(bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetProps);
|
||||
this._parseAssignmentEvent(bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetEvents);
|
||||
|
||||
} else if (isPresent(bindParts[9])) { // match: [expr]
|
||||
this._parseProperty(bindParts[9], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetProps);
|
||||
|
||||
} else if (isPresent(bindParts[10])) { // match: (event)
|
||||
this._parseEvent(bindParts[10], attrValue, attr.sourceSpan, targetMatchableAttrs,
|
||||
targetEvents);
|
||||
}
|
||||
} else {
|
||||
@ -495,6 +532,14 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
targetVars.push(new VariableAst(identifier, value, sourceSpan));
|
||||
}
|
||||
|
||||
private _parseReference(identifier: string, value: string, sourceSpan: ParseSourceSpan,
|
||||
targetRefs: ElementOrDirectiveRef[]) {
|
||||
if (identifier.indexOf('-') > -1) {
|
||||
this._reportError(`"-" is not allowed in reference names`, sourceSpan);
|
||||
}
|
||||
targetRefs.push(new ElementOrDirectiveRef(identifier, value, sourceSpan));
|
||||
}
|
||||
|
||||
private _parseProperty(name: string, expression: string, sourceSpan: ParseSourceSpan,
|
||||
targetMatchableAttrs: string[][],
|
||||
targetProps: BoundElementOrDirectiveProperty[]) {
|
||||
@ -547,33 +592,28 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
|
||||
private _parseDirectives(selectorMatcher: SelectorMatcher,
|
||||
elementCssSelector: CssSelector): CompileDirectiveMetadata[] {
|
||||
var directives = [];
|
||||
selectorMatcher.match(elementCssSelector,
|
||||
(selector, directive) => { directives.push(directive); });
|
||||
// Need to sort the directives so that we get consistent results throughout,
|
||||
// as selectorMatcher uses Maps inside.
|
||||
// Also need to make components the first directive in the array
|
||||
ListWrapper.sort(directives,
|
||||
(dir1: CompileDirectiveMetadata, dir2: CompileDirectiveMetadata) => {
|
||||
var dir1Comp = dir1.isComponent;
|
||||
var dir2Comp = dir2.isComponent;
|
||||
if (dir1Comp && !dir2Comp) {
|
||||
return -1;
|
||||
} else if (!dir1Comp && dir2Comp) {
|
||||
return 1;
|
||||
} else {
|
||||
return this.directivesIndex.get(dir1) - this.directivesIndex.get(dir2);
|
||||
}
|
||||
});
|
||||
return directives;
|
||||
// Also dedupe directives as they might match more than one time!
|
||||
var directives = ListWrapper.createFixedSize(this.directivesIndex.size);
|
||||
selectorMatcher.match(elementCssSelector, (selector, directive) => {
|
||||
directives[this.directivesIndex.get(directive)] = directive;
|
||||
});
|
||||
return directives.filter(dir => isPresent(dir));
|
||||
}
|
||||
|
||||
private _createDirectiveAsts(elementName: string, directives: CompileDirectiveMetadata[],
|
||||
private _createDirectiveAsts(isTemplateElement: boolean, elementName: string,
|
||||
directives: CompileDirectiveMetadata[],
|
||||
props: BoundElementOrDirectiveProperty[],
|
||||
possibleExportAsVars: VariableAst[],
|
||||
sourceSpan: ParseSourceSpan): DirectiveAst[] {
|
||||
var matchedVariables = new Set<string>();
|
||||
elementOrDirectiveRefs: ElementOrDirectiveRef[],
|
||||
sourceSpan: ParseSourceSpan,
|
||||
targetReferences: ReferenceAst[]): DirectiveAst[] {
|
||||
var matchedReferences = new Set<string>();
|
||||
var component: CompileDirectiveMetadata = null;
|
||||
var directiveAsts = directives.map((directive: CompileDirectiveMetadata) => {
|
||||
if (directive.isComponent) {
|
||||
component = directive;
|
||||
}
|
||||
var hostProperties: BoundElementPropertyAst[] = [];
|
||||
var hostEvents: BoundEventAst[] = [];
|
||||
var directiveProperties: BoundDirectivePropertyAst[] = [];
|
||||
@ -581,21 +621,29 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
hostProperties);
|
||||
this._createDirectiveHostEventAsts(directive.hostListeners, sourceSpan, hostEvents);
|
||||
this._createDirectivePropertyAsts(directive.inputs, props, directiveProperties);
|
||||
var exportAsVars = [];
|
||||
possibleExportAsVars.forEach((varAst) => {
|
||||
if ((varAst.value.length === 0 && directive.isComponent) ||
|
||||
(directive.exportAs == varAst.value)) {
|
||||
exportAsVars.push(varAst);
|
||||
matchedVariables.add(varAst.name);
|
||||
elementOrDirectiveRefs.forEach((elOrDirRef) => {
|
||||
if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
|
||||
(directive.exportAs == elOrDirRef.value)) {
|
||||
targetReferences.push(new ReferenceAst(elOrDirRef.name, identifierToken(directive.type),
|
||||
elOrDirRef.sourceSpan));
|
||||
matchedReferences.add(elOrDirRef.name);
|
||||
}
|
||||
});
|
||||
return new DirectiveAst(directive, directiveProperties, hostProperties, hostEvents,
|
||||
exportAsVars, sourceSpan);
|
||||
sourceSpan);
|
||||
});
|
||||
possibleExportAsVars.forEach((varAst) => {
|
||||
if (varAst.value.length > 0 && !SetWrapper.has(matchedVariables, varAst.name)) {
|
||||
this._reportError(`There is no directive with "exportAs" set to "${varAst.value}"`,
|
||||
varAst.sourceSpan);
|
||||
elementOrDirectiveRefs.forEach((elOrDirRef) => {
|
||||
if (elOrDirRef.value.length > 0) {
|
||||
if (!SetWrapper.has(matchedReferences, elOrDirRef.name)) {
|
||||
this._reportError(`There is no directive with "exportAs" set to "${elOrDirRef.value}"`,
|
||||
elOrDirRef.sourceSpan);
|
||||
};
|
||||
} else if (isBlank(component)) {
|
||||
var refToken = null;
|
||||
if (isTemplateElement) {
|
||||
refToken = identifierToken(Identifiers.TemplateRef);
|
||||
}
|
||||
targetReferences.push(new ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.sourceSpan));
|
||||
}
|
||||
});
|
||||
return directiveAsts;
|
||||
@ -793,6 +841,10 @@ class BoundElementOrDirectiveProperty {
|
||||
public sourceSpan: ParseSourceSpan) {}
|
||||
}
|
||||
|
||||
class ElementOrDirectiveRef {
|
||||
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
|
||||
}
|
||||
|
||||
export function splitClasses(classAttrValue: string): string[] {
|
||||
return StringWrapper.split(classAttrValue.trim(), /\s+/g);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import {InjectMethodVars} from './constants';
|
||||
import {CompileView} from './compile_view';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {TemplateAst, ProviderAst, ProviderAstType} from '../template_ast';
|
||||
import {TemplateAst, ProviderAst, ProviderAstType, ReferenceAst} from '../template_ast';
|
||||
import {
|
||||
CompileTokenMap,
|
||||
CompileDirectiveMetadata,
|
||||
@ -13,7 +13,7 @@ import {
|
||||
CompileProviderMetadata,
|
||||
CompileDiDependencyMetadata,
|
||||
CompileIdentifierMetadata,
|
||||
CompileTypeMetadata
|
||||
CompileTypeMetadata,
|
||||
} from '../compile_metadata';
|
||||
import {getPropertyInView, createDiTokenExpression, injectFromViewParentInjector} from './util';
|
||||
import {CompileQuery, createQueryList, addQueryToTokenMap} from './compile_query';
|
||||
@ -30,7 +30,7 @@ export class CompileNode {
|
||||
|
||||
export class CompileElement extends CompileNode {
|
||||
static createNull(): CompileElement {
|
||||
return new CompileElement(null, null, null, null, null, null, [], [], false, false, {});
|
||||
return new CompileElement(null, null, null, null, null, null, [], [], false, false, []);
|
||||
}
|
||||
|
||||
private _compViewExpr: o.Expression = null;
|
||||
@ -47,15 +47,18 @@ export class CompileElement extends CompileNode {
|
||||
public contentNodesByNgContentIndex: Array<o.Expression>[] = null;
|
||||
public embeddedView: CompileView;
|
||||
public directiveInstances: o.Expression[];
|
||||
public referenceTokens: {[key: string]: CompileTokenMetadata};
|
||||
|
||||
constructor(parent: CompileElement, view: CompileView, nodeIndex: number,
|
||||
renderNode: o.Expression, sourceAst: TemplateAst,
|
||||
public component: CompileDirectiveMetadata,
|
||||
private _directives: CompileDirectiveMetadata[],
|
||||
private _resolvedProvidersArray: ProviderAst[], public hasViewContainer: boolean,
|
||||
public hasEmbeddedView: boolean,
|
||||
public variableTokens: {[key: string]: CompileTokenMetadata}) {
|
||||
public hasEmbeddedView: boolean, references: ReferenceAst[]) {
|
||||
super(parent, view, nodeIndex, renderNode, sourceAst);
|
||||
this.referenceTokens = {};
|
||||
references.forEach(ref => this.referenceTokens[ref.name] = ref.value);
|
||||
|
||||
this.elementRef = o.importExpr(Identifiers.ElementRef).instantiate([this.renderNode]);
|
||||
this._instances.add(identifierToken(Identifiers.ElementRef), this.elementRef);
|
||||
this.injector = o.THIS_EXPR.callMethod('injector', [o.literal(this.nodeIndex)]);
|
||||
@ -167,15 +170,15 @@ export class CompileElement extends CompileNode {
|
||||
queriesWithReads,
|
||||
queriesForProvider.map(query => new _QueryWithRead(query, resolvedProvider.token)));
|
||||
});
|
||||
StringMapWrapper.forEach(this.variableTokens, (_, varName) => {
|
||||
var token = this.variableTokens[varName];
|
||||
StringMapWrapper.forEach(this.referenceTokens, (_, varName) => {
|
||||
var token = this.referenceTokens[varName];
|
||||
var varValue;
|
||||
if (isPresent(token)) {
|
||||
varValue = this._instances.get(token);
|
||||
} else {
|
||||
varValue = this.renderNode;
|
||||
}
|
||||
this.view.variables.set(varName, varValue);
|
||||
this.view.locals.set(varName, varValue);
|
||||
var varToken = new CompileTokenMetadata({value: varName});
|
||||
ListWrapper.addAll(queriesWithReads, this._getQueriesFor(varToken)
|
||||
.map(query => new _QueryWithRead(query, varToken)));
|
||||
@ -186,8 +189,8 @@ export class CompileElement extends CompileNode {
|
||||
// query for an identifier
|
||||
value = this._instances.get(queryWithRead.read);
|
||||
} else {
|
||||
// query for a variable
|
||||
var token = this.variableTokens[queryWithRead.read.value];
|
||||
// query for a reference
|
||||
var token = this.referenceTokens[queryWithRead.read.value];
|
||||
if (isPresent(token)) {
|
||||
value = this._instances.get(token);
|
||||
} else {
|
||||
@ -247,12 +250,6 @@ export class CompileElement extends CompileNode {
|
||||
(resolvedProvider) => createDiTokenExpression(resolvedProvider.token));
|
||||
}
|
||||
|
||||
getDeclaredVariablesNames(): string[] {
|
||||
var res = [];
|
||||
StringMapWrapper.forEach(this.variableTokens, (_, key) => { res.push(key); });
|
||||
return res;
|
||||
}
|
||||
|
||||
private _getQueriesFor(token: CompileTokenMetadata): CompileQuery[] {
|
||||
var result: CompileQuery[] = [];
|
||||
var currentEl: CompileElement = this;
|
||||
|
@ -56,7 +56,7 @@ export class CompileView implements NameResolver {
|
||||
public componentView: CompileView;
|
||||
public purePipes = new Map<string, CompilePipe>();
|
||||
public pipes: CompilePipe[] = [];
|
||||
public variables = new Map<string, o.Expression>();
|
||||
public locals = new Map<string, o.Expression>();
|
||||
public className: string;
|
||||
public classType: o.Type;
|
||||
public viewFactory: o.ReadVarExpr;
|
||||
@ -112,7 +112,7 @@ export class CompileView implements NameResolver {
|
||||
}
|
||||
this.viewQueries = viewQueries;
|
||||
templateVariableBindings.forEach((entry) => {
|
||||
this.variables.set(entry[1], o.THIS_EXPR.prop('locals').key(o.literal(entry[0])));
|
||||
this.locals.set(entry[1], o.THIS_EXPR.prop('locals').key(o.literal(entry[0])));
|
||||
});
|
||||
|
||||
if (!this.declarationElement.isNull()) {
|
||||
@ -133,15 +133,15 @@ export class CompileView implements NameResolver {
|
||||
return pipe.call(this, [input].concat(args));
|
||||
}
|
||||
|
||||
getVariable(name: string): o.Expression {
|
||||
getLocal(name: string): o.Expression {
|
||||
if (name == EventHandlerVars.event.name) {
|
||||
return EventHandlerVars.event;
|
||||
}
|
||||
var currView: CompileView = this;
|
||||
var result = currView.variables.get(name);
|
||||
var result = currView.locals.get(name);
|
||||
while (isBlank(result) && isPresent(currView.declarationElement.view)) {
|
||||
currView = currView.declarationElement.view;
|
||||
result = currView.variables.get(name);
|
||||
result = currView.locals.get(name);
|
||||
}
|
||||
if (isPresent(result)) {
|
||||
return getPropertyInView(result, this, currView);
|
||||
|
@ -9,7 +9,7 @@ var IMPLICIT_RECEIVER = o.variable('#implicit');
|
||||
|
||||
export interface NameResolver {
|
||||
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression;
|
||||
getVariable(name: string): o.Expression;
|
||||
getLocal(name: string): o.Expression;
|
||||
createLiteralArray(values: o.Expression[]): o.Expression;
|
||||
createLiteralMap(values: Array<Array<string | o.Expression>>): o.Expression;
|
||||
}
|
||||
@ -185,7 +185,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||
var result = null;
|
||||
var receiver = ast.receiver.visit(this, _Mode.Expression);
|
||||
if (receiver === IMPLICIT_RECEIVER) {
|
||||
var varExpr = this._nameResolver.getVariable(ast.name);
|
||||
var varExpr = this._nameResolver.getLocal(ast.name);
|
||||
if (isPresent(varExpr)) {
|
||||
result = varExpr.callFn(args);
|
||||
} else {
|
||||
@ -204,7 +204,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||
var result = null;
|
||||
var receiver = ast.receiver.visit(this, _Mode.Expression);
|
||||
if (receiver === IMPLICIT_RECEIVER) {
|
||||
result = this._nameResolver.getVariable(ast.name);
|
||||
result = this._nameResolver.getLocal(ast.name);
|
||||
if (isBlank(result)) {
|
||||
receiver = this._implicitReceiver;
|
||||
}
|
||||
@ -217,9 +217,9 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||
visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
|
||||
var receiver: o.Expression = ast.receiver.visit(this, _Mode.Expression);
|
||||
if (receiver === IMPLICIT_RECEIVER) {
|
||||
var varExpr = this._nameResolver.getVariable(ast.name);
|
||||
var varExpr = this._nameResolver.getLocal(ast.name);
|
||||
if (isPresent(varExpr)) {
|
||||
throw new BaseException('Cannot reassign a variable binding');
|
||||
throw new BaseException('Cannot assign to a reference or variable!');
|
||||
}
|
||||
receiver = this._implicitReceiver;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
NgContentAst,
|
||||
EmbeddedTemplateAst,
|
||||
ElementAst,
|
||||
ReferenceAst,
|
||||
VariableAst,
|
||||
BoundEventAst,
|
||||
BoundElementPropertyAst,
|
||||
@ -113,6 +114,7 @@ class ViewBinderVisitor implements TemplateAstVisitor {
|
||||
return null;
|
||||
}
|
||||
|
||||
visitReference(ast: ReferenceAst, ctx: any): any { return null; }
|
||||
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
||||
|
@ -26,6 +26,7 @@ import {
|
||||
NgContentAst,
|
||||
EmbeddedTemplateAst,
|
||||
ElementAst,
|
||||
ReferenceAst,
|
||||
VariableAst,
|
||||
BoundEventAst,
|
||||
BoundElementPropertyAst,
|
||||
@ -201,8 +202,6 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||
|
||||
var component = ast.getComponent();
|
||||
var directives = ast.directives.map(directiveAst => directiveAst.directive);
|
||||
var variables =
|
||||
_readHtmlAndDirectiveVariables(ast.exportAsVars, ast.directives, this.view.viewType);
|
||||
var htmlAttrs = _readHtmlAttrs(ast.attrs);
|
||||
var attrNameAndValues = _mergeHtmlAndDirectiveAttrs(htmlAttrs, directives);
|
||||
for (var i = 0; i < attrNameAndValues.length; i++) {
|
||||
@ -216,7 +215,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||
}
|
||||
var compileElement =
|
||||
new CompileElement(parent, this.view, nodeIndex, renderNode, ast, component, directives,
|
||||
ast.providers, ast.hasViewContainer, false, variables);
|
||||
ast.providers, ast.hasViewContainer, false, ast.references);
|
||||
this.view.nodes.push(compileElement);
|
||||
var compViewExpr: o.ReadVarExpr = null;
|
||||
if (isPresent(component)) {
|
||||
@ -269,13 +268,13 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||
.toStmt());
|
||||
var renderNode = o.THIS_EXPR.prop(fieldName);
|
||||
|
||||
var templateVariableBindings = ast.vars.map(
|
||||
var templateVariableBindings = ast.variables.map(
|
||||
varAst => [varAst.value.length > 0 ? varAst.value : IMPLICIT_TEMPLATE_VAR, varAst.name]);
|
||||
|
||||
var directives = ast.directives.map(directiveAst => directiveAst.directive);
|
||||
var compileElement =
|
||||
new CompileElement(parent, this.view, nodeIndex, renderNode, ast, null, directives,
|
||||
ast.providers, ast.hasViewContainer, true, {});
|
||||
ast.providers, ast.hasViewContainer, true, ast.references);
|
||||
this.view.nodes.push(compileElement);
|
||||
|
||||
this.nestedViewCount++;
|
||||
@ -297,6 +296,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||
return null;
|
||||
}
|
||||
|
||||
visitReference(ast: ReferenceAst, ctx: any): any { return null; }
|
||||
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
||||
@ -321,24 +321,6 @@ function _readHtmlAttrs(attrs: AttrAst[]): {[key: string]: string} {
|
||||
return htmlAttrs;
|
||||
}
|
||||
|
||||
function _readHtmlAndDirectiveVariables(elementExportAsVars: VariableAst[],
|
||||
directives: DirectiveAst[],
|
||||
viewType: ViewType): {[key: string]: CompileTokenMetadata} {
|
||||
var variables: {[key: string]: CompileTokenMetadata} = {};
|
||||
var component: CompileDirectiveMetadata = null;
|
||||
directives.forEach((directive) => {
|
||||
if (directive.directive.isComponent) {
|
||||
component = directive.directive;
|
||||
}
|
||||
directive.exportAsVars.forEach(
|
||||
varAst => { variables[varAst.name] = identifierToken(directive.directive.type); });
|
||||
});
|
||||
elementExportAsVars.forEach((varAst) => {
|
||||
variables[varAst.name] = isPresent(component) ? identifierToken(component.type) : null;
|
||||
});
|
||||
return variables;
|
||||
}
|
||||
|
||||
function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: string): string {
|
||||
if (attrName == CLASS_ATTR || attrName == STYLE_ATTR) {
|
||||
return `${attrValue1} ${attrValue2}`;
|
||||
@ -392,7 +374,7 @@ function createStaticNodeDebugInfo(node: CompileNode): o.Expression {
|
||||
if (isPresent(compileElement.component)) {
|
||||
componentToken = createDiTokenExpression(identifierToken(compileElement.component.type));
|
||||
}
|
||||
StringMapWrapper.forEach(compileElement.variableTokens, (token, varName) => {
|
||||
StringMapWrapper.forEach(compileElement.referenceTokens, (token, varName) => {
|
||||
varTokenEntries.push(
|
||||
[varName, isPresent(token) ? createDiTokenExpression(token) : o.NULL_EXPR]);
|
||||
});
|
||||
|
@ -70,7 +70,7 @@ export abstract class ChangeDetectorRef {
|
||||
* @Component({
|
||||
* selector: 'giant-list',
|
||||
* template: `
|
||||
* <li *ngFor="#d of dataProvider.data">Data {{d}}</lig>
|
||||
* <li *ngFor="let d of dataProvider.data">Data {{d}}</lig>
|
||||
* `,
|
||||
* directives: [NgFor]
|
||||
* })
|
||||
|
@ -1,7 +1,13 @@
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {print} from 'angular2/src/facade/lang';
|
||||
import {print, warn} from 'angular2/src/facade/lang';
|
||||
|
||||
// Note: Need to rename warn as in Dart
|
||||
// class members and imports can't use the same name.
|
||||
let _warnImpl = warn;
|
||||
|
||||
@Injectable()
|
||||
export class Console {
|
||||
log(message: string): void { print(message); }
|
||||
}
|
||||
// Note: for reporting errors use `DOM.logError()` as it is platform specific
|
||||
warn(message: string): void { _warnImpl(message); }
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import {ViewType} from './view_type';
|
||||
@CONST()
|
||||
export class StaticNodeDebugInfo {
|
||||
constructor(public providerTokens: any[], public componentToken: any,
|
||||
public varTokens: {[key: string]: any}) {}
|
||||
public refTokens: {[key: string]: any}) {}
|
||||
}
|
||||
|
||||
export class DebugContext implements RenderDebugInfo {
|
||||
@ -61,15 +61,15 @@ export class DebugContext implements RenderDebugInfo {
|
||||
ListWrapper.forEachWithIndex(
|
||||
this._view.staticNodeDebugInfos,
|
||||
(staticNodeInfo: StaticNodeDebugInfo, nodeIndex: number) => {
|
||||
var vars = staticNodeInfo.varTokens;
|
||||
StringMapWrapper.forEach(vars, (varToken, varName) => {
|
||||
var refs = staticNodeInfo.refTokens;
|
||||
StringMapWrapper.forEach(refs, (refToken, refName) => {
|
||||
var varValue;
|
||||
if (isBlank(varToken)) {
|
||||
if (isBlank(refToken)) {
|
||||
varValue = isPresent(this._view.allNodes) ? this._view.allNodes[nodeIndex] : null;
|
||||
} else {
|
||||
varValue = this._view.injectorGet(varToken, nodeIndex, null);
|
||||
varValue = this._view.injectorGet(refToken, nodeIndex, null);
|
||||
}
|
||||
varValues[varName] = varValue;
|
||||
varValues[refName] = varValue;
|
||||
});
|
||||
});
|
||||
StringMapWrapper.forEach(this._view.locals,
|
||||
|
@ -11,7 +11,7 @@ import {Observable, EventEmitter} from 'angular2/src/facade/async';
|
||||
*
|
||||
* Implements an iterable interface, therefore it can be used in both ES6
|
||||
* javascript `for (var i of items)` loops as well as in Angular templates with
|
||||
* `*ngFor="#i of myList"`.
|
||||
* `*ngFor="let i of myList"`.
|
||||
*
|
||||
* Changes can be observed by subscribing to the changes `Observable`.
|
||||
*
|
||||
|
@ -34,7 +34,7 @@ export abstract class ViewRef extends ChangeDetectorRef {
|
||||
* ```
|
||||
* Count: {{items.length}}
|
||||
* <ul>
|
||||
* <li *ngFor="var item of items">{{item}}</li>
|
||||
* <li *ngFor="let item of items">{{item}}</li>
|
||||
* </ul>
|
||||
* ```
|
||||
*
|
||||
@ -44,7 +44,7 @@ export abstract class ViewRef extends ChangeDetectorRef {
|
||||
* ```
|
||||
* Count: {{items.length}}
|
||||
* <ul>
|
||||
* <template ngFor var-item [ngForOf]="items"></template>
|
||||
* <template ngFor let-item [ngForOf]="items"></template>
|
||||
* </ul>
|
||||
* ```
|
||||
*
|
||||
|
@ -964,7 +964,7 @@ export var Attribute: AttributeMetadataFactory = makeParamDecorator(AttributeMet
|
||||
* ```html
|
||||
* <tabs>
|
||||
* <pane title="Overview">...</pane>
|
||||
* <pane *ngFor="#o of objects" [title]="o.title">{{o.text}}</pane>
|
||||
* <pane *ngFor="let o of objects" [title]="o.title">{{o.text}}</pane>
|
||||
* </tabs>
|
||||
* ```
|
||||
*
|
||||
@ -983,7 +983,7 @@ export var Attribute: AttributeMetadataFactory = makeParamDecorator(AttributeMet
|
||||
* selector: 'tabs',
|
||||
* template: `
|
||||
* <ul>
|
||||
* <li *ngFor="#pane of panes">{{pane.title}}</li>
|
||||
* <li *ngFor="let pane of panes">{{pane.title}}</li>
|
||||
* </ul>
|
||||
* <ng-content></ng-content>
|
||||
* `
|
||||
|
@ -46,7 +46,7 @@ export class AttributeMetadata extends DependencyMetadata {
|
||||
* ```html
|
||||
* <tabs>
|
||||
* <pane title="Overview">...</pane>
|
||||
* <pane *ngFor="#o of objects" [title]="o.title">{{o.text}}</pane>
|
||||
* <pane *ngFor="let o of objects" [title]="o.title">{{o.text}}</pane>
|
||||
* </tabs>
|
||||
* ```
|
||||
*
|
||||
@ -65,7 +65,7 @@ export class AttributeMetadata extends DependencyMetadata {
|
||||
* selector: 'tabs',
|
||||
* template: `
|
||||
* <ul>
|
||||
* <li *ngFor="#pane of panes">{{pane.title}}</li>
|
||||
* <li *ngFor="let pane of panes">{{pane.title}}</li>
|
||||
* </ul>
|
||||
* <ng-content></ng-content>
|
||||
* `
|
||||
|
@ -149,7 +149,7 @@ export interface OnInit { ngOnInit(); }
|
||||
* template: `
|
||||
* <p>Changes:</p>
|
||||
* <ul>
|
||||
* <li *ngFor="#line of logs">{{line}}</li>
|
||||
* <li *ngFor="let line of logs">{{line}}</li>
|
||||
* </ul>`,
|
||||
* directives: [NgFor]
|
||||
* })
|
||||
|
@ -104,7 +104,7 @@ export class ViewMetadata {
|
||||
* directives: [NgFor]
|
||||
* template: '
|
||||
* <ul>
|
||||
* <li *ngFor="#item of items">{{item}}</li>
|
||||
* <li *ngFor="let item of items">{{item}}</li>
|
||||
* </ul>'
|
||||
* })
|
||||
* class MyComponent {
|
||||
|
@ -298,6 +298,10 @@ bool isJsObject(o) {
|
||||
return false;
|
||||
}
|
||||
|
||||
warn(o) {
|
||||
print(o);
|
||||
}
|
||||
|
||||
// Functions below are noop in Dart. Imperatively controlling dev mode kills
|
||||
// tree shaking. We should only rely on `assertionsEnabled`.
|
||||
@Deprecated('Do not use this function. It is for JS only. There is no alternative.')
|
||||
|
@ -408,6 +408,10 @@ export function print(obj: Error | Object) {
|
||||
console.log(obj);
|
||||
}
|
||||
|
||||
export function warn(obj: Error | Object) {
|
||||
console.warn(obj);
|
||||
}
|
||||
|
||||
// Can't be all uppercase as our transpiler would think it is a special directive...
|
||||
export class Json {
|
||||
static parse(s: string): Object { return _global.JSON.parse(s); }
|
||||
|
@ -185,11 +185,13 @@ export class RouterLinkTransform implements TemplateAstVisitor {
|
||||
let updatedChildren = ast.children.map(c => c.visit(this, context));
|
||||
let updatedInputs = ast.inputs.map(c => c.visit(this, context));
|
||||
let updatedDirectives = ast.directives.map(c => c.visit(this, context));
|
||||
return new ElementAst(ast.name, ast.attrs, updatedInputs, ast.outputs, ast.exportAsVars,
|
||||
return new ElementAst(ast.name, ast.attrs, updatedInputs, ast.outputs, ast.references,
|
||||
updatedDirectives, ast.providers, ast.hasViewContainer, updatedChildren,
|
||||
ast.ngContentIndex, ast.sourceSpan);
|
||||
}
|
||||
|
||||
visitReference(ast: any, context: any): any { return ast; }
|
||||
|
||||
visitVariable(ast: any, context: any): any { return ast; }
|
||||
|
||||
visitEvent(ast: any, context: any): any { return ast; }
|
||||
@ -205,7 +207,7 @@ export class RouterLinkTransform implements TemplateAstVisitor {
|
||||
visitDirective(ast: DirectiveAst, context: any): any {
|
||||
let updatedInputs = ast.inputs.map(c => c.visit(this, context));
|
||||
return new DirectiveAst(ast.directive, updatedInputs, ast.hostProperties, ast.hostEvents,
|
||||
ast.exportAsVars, ast.sourceSpan);
|
||||
ast.sourceSpan);
|
||||
}
|
||||
|
||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {
|
||||
|
Reference in New Issue
Block a user