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:
Tobias Bosch 2016-04-25 19:52:24 -07:00
parent ff2ae7a2e1
commit d2efac18ed
69 changed files with 651 additions and 414 deletions

View File

@ -14,7 +14,7 @@ Removes or recreates a portion of the DOM tree based on the showSection expressi
@cheatsheetItem @cheatsheetItem
syntax: syntax:
`<li *ngFor="#item of list">`|`*ngFor` `<li *ngFor="let item of list">`|`*ngFor`
description: description:
Turns the li element and its contents into a template, and uses that to instantiate a view for each item in list. Turns the li element and its contents into a template, and uses that to instantiate a view for each item in list.

View File

@ -433,7 +433,7 @@ Finally, we can move the `ngFor` keyword to the left hand side and prefix it wit
``` ```
<ul> <ul>
<li *ngFor="var person of people; var i=index">{{i}}. {{person}}<li> <li *ngFor="let person of people; var i=index">{{i}}. {{person}}<li>
</ul> </ul>
``` ```

View File

@ -94,7 +94,7 @@ Let's start with a View such as:
``` ```
<ul> <ul>
<li template="ngFor: #person of people">{{person}}</li> <li template="ngFor: let person of people">{{person}}</li>
</ul> </ul>
``` ```

View File

@ -22,7 +22,7 @@ export class SlicePipeStringExample {
@Component({ @Component({
selector: 'slice-list-example', selector: 'slice-list-example',
template: `<div> template: `<div>
<li *ngFor="var i of collection | slice:1:3">{{i}}</li> <li *ngFor="let i of collection | slice:1:3">{{i}}</li>
</div>` </div>`
}) })
export class SlicePipeListExample { export class SlicePipeListExample {

View File

@ -36,7 +36,7 @@ class MyCmp implements OnDeactivate {
<router-outlet></router-outlet> <router-outlet></router-outlet>
<div id="log"> <div id="log">
<h2>Log:</h2> <h2>Log:</h2>
<p *ngFor="#logItem of logService.logs">{{ logItem }}</p> <p *ngFor="let logItem of logService.logs">{{ logItem }}</p>
</div> </div>
`, `,
directives: [ROUTER_DIRECTIVES] directives: [ROUTER_DIRECTIVES]

View File

@ -56,7 +56,7 @@ export {URLSearchParams} from './src/http/url_search_params';
* <div> * <div>
* <h1>People</h1> * <h1>People</h1>
* <ul> * <ul>
* <li *ngFor="#person of people"> * <li *ngFor="let person of people">
* {{person.name}} * {{person.name}}
* </li> * </li>
* </ul> * </ul>
@ -194,7 +194,7 @@ export const HTTP_BINDINGS = HTTP_PROVIDERS;
* <div> * <div>
* <h1>People</h1> * <h1>People</h1>
* <ul> * <ul>
* <li *ngFor="#person of people"> * <li *ngFor="let person of people">
* {{person.name}} * {{person.name}}
* </li> * </li>
* </ul> * </ul>

View File

@ -58,7 +58,7 @@ import {BaseException} from "../../facade/exceptions";
* *
* ### Syntax * ### 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>` * - `<li template="ngFor #item of items; #i = index">...</li>`
* - `<template ngFor #item [ngForOf]="items" #i="index"><li>...</li></template>` * - `<template ngFor #item [ngForOf]="items" #i="index"><li>...</li></template>`
* *

View File

@ -96,7 +96,7 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
* *
* ``` * ```
* <select ngControl="city"> * <select ngControl="city">
* <option *ngFor="#c of cities" [value]="c"></option> * <option *ngFor="let c of cities" [value]="c"></option>
* </select> * </select>
* ``` * ```
*/ */

View File

@ -46,7 +46,11 @@ export class Token {
isKeyword(): boolean { return (this.type == TokenType.Keyword); } 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"); } isKeywordNull(): boolean { return (this.type == TokenType.Keyword && this.strValue == "null"); }
@ -464,4 +468,4 @@ var OPERATORS = SetWrapper.createFromList([
var KEYWORDS = var KEYWORDS =
SetWrapper.createFromList(['var', 'null', 'undefined', 'true', 'false', 'if', 'else']); SetWrapper.createFromList(['var', 'let', 'null', 'undefined', 'true', 'false', 'if', 'else']);

View File

@ -62,6 +62,10 @@ export class SplitInterpolation {
constructor(public strings: string[], public expressions: string[]) {} constructor(public strings: string[], public expressions: string[]) {}
} }
export class TemplateBindingParseResult {
constructor(public templateBindings: TemplateBinding[], public warnings: string[]) {}
}
@Injectable() @Injectable()
export class Parser { export class Parser {
constructor(/** @internal */ constructor(/** @internal */
@ -112,7 +116,7 @@ export class Parser {
return new Quote(prefix, uninterpretedExpression, location); return new Quote(prefix, uninterpretedExpression, location);
} }
parseTemplateBindings(input: string, location: any): TemplateBinding[] { parseTemplateBindings(input: string, location: any): TemplateBindingParseResult {
var tokens = this._lexer.tokenize(input); var tokens = this._lexer.tokenize(input);
return new _ParseAST(input, location, tokens, false).parseTemplateBindings(); return new _ParseAST(input, location, tokens, false).parseTemplateBindings();
} }
@ -228,16 +232,11 @@ export class _ParseAST {
} }
} }
optionalKeywordVar(): boolean { peekKeywordLet(): boolean { return this.next.isKeywordLet(); }
if (this.peekKeywordVar()) {
this.advance();
return true;
} else {
return false;
}
}
peekKeywordVar(): boolean { return this.next.isKeywordVar() || this.next.isOperator('#'); } peekDeprecatedKeywordVar(): boolean { return this.next.isKeywordDeprecatedVar(); }
peekDeprecatedOperatorHash(): boolean { return this.next.isOperator('#'); }
expectCharacter(code: number) { expectCharacter(code: number) {
if (this.optionalCharacter(code)) return; if (this.optionalCharacter(code)) return;
@ -617,11 +616,23 @@ export class _ParseAST {
return result.toString(); return result.toString();
} }
parseTemplateBindings(): any[] { parseTemplateBindings(): TemplateBindingParseResult {
var bindings = []; var bindings: TemplateBinding[] = [];
var prefix = null; var prefix = null;
var warnings: string[] = [];
while (this.index < this.tokens.length) { 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(); var key = this.expectTemplateBindingKey();
if (!keyIsVar) { if (!keyIsVar) {
if (prefix == null) { if (prefix == null) {
@ -639,7 +650,8 @@ export class _ParseAST {
} else { } else {
name = '\$implicit'; 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 start = this.inputIndex;
var ast = this.parsePipe(); var ast = this.parsePipe();
var source = this.input.substring(start, this.inputIndex); var source = this.input.substring(start, this.inputIndex);
@ -650,7 +662,7 @@ export class _ParseAST {
this.optionalCharacter($COMMA); this.optionalCharacter($COMMA);
} }
} }
return bindings; return new TemplateBindingParseResult(bindings, warnings);
} }
error(message: string, index: number = null) { error(message: string, index: number = null) {

View File

@ -17,8 +17,14 @@ export class ParseSourceSpan {
} }
} }
export enum ParseErrorLevel {
WARNING,
FATAL
}
export abstract class ParseError { 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 { toString(): string {
var source = this.span.start.file.content; var source = this.span.start.file.content;

View File

@ -6,7 +6,7 @@ import {
NgContentAst, NgContentAst,
EmbeddedTemplateAst, EmbeddedTemplateAst,
ElementAst, ElementAst,
VariableAst, ReferenceAst,
BoundEventAst, BoundEventAst,
BoundElementPropertyAst, BoundElementPropertyAst,
AttrAst, AttrAst,
@ -69,7 +69,7 @@ export class ProviderElementContext {
constructor(private _viewContext: ProviderViewContext, private _parent: ProviderElementContext, constructor(private _viewContext: ProviderViewContext, private _parent: ProviderElementContext,
private _isViewRoot: boolean, private _directiveAsts: DirectiveAst[], private _isViewRoot: boolean, private _directiveAsts: DirectiveAst[],
attrs: AttrAst[], vars: VariableAst[], private _sourceSpan: ParseSourceSpan) { attrs: AttrAst[], refs: ReferenceAst[], private _sourceSpan: ParseSourceSpan) {
this._attrs = {}; this._attrs = {};
attrs.forEach((attrAst) => this._attrs[attrAst.name] = attrAst.value); attrs.forEach((attrAst) => this._attrs[attrAst.name] = attrAst.value);
var directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive); var directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive);
@ -79,9 +79,8 @@ export class ProviderElementContext {
var queriedTokens = new CompileTokenMap<boolean>(); var queriedTokens = new CompileTokenMap<boolean>();
this._allProviders.values().forEach( this._allProviders.values().forEach(
(provider) => { this._addQueryReadsTo(provider.token, queriedTokens); }); (provider) => { this._addQueryReadsTo(provider.token, queriedTokens); });
vars.forEach((varAst) => { refs.forEach((refAst) => {
var varToken = new CompileTokenMetadata({value: varAst.name}); this._addQueryReadsTo(new CompileTokenMetadata({value: refAst.name}), queriedTokens);
this._addQueryReadsTo(varToken, queriedTokens);
}); });
if (isPresent(queriedTokens.get(identifierToken(Identifiers.ViewContainerRef)))) { if (isPresent(queriedTokens.get(identifierToken(Identifiers.ViewContainerRef)))) {
this._hasViewContainer = true; this._hasViewContainer = true;

View File

@ -30,7 +30,6 @@ import {
NgContentAst, NgContentAst,
EmbeddedTemplateAst, EmbeddedTemplateAst,
ElementAst, ElementAst,
VariableAst,
BoundEventAst, BoundEventAst,
BoundElementPropertyAst, BoundElementPropertyAst,
AttrAst, AttrAst,

View File

@ -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 { export class VariableAst implements TemplateAst {
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {} constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
@ -97,7 +108,7 @@ export class VariableAst implements TemplateAst {
export class ElementAst implements TemplateAst { export class ElementAst implements TemplateAst {
constructor(public name: string, public attrs: AttrAst[], constructor(public name: string, public attrs: AttrAst[],
public inputs: BoundElementPropertyAst[], public outputs: BoundEventAst[], 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 providers: ProviderAst[], public hasViewContainer: boolean,
public children: TemplateAst[], public ngContentIndex: number, public children: TemplateAst[], public ngContentIndex: number,
public sourceSpan: ParseSourceSpan) {} public sourceSpan: ParseSourceSpan) {}
@ -106,14 +117,6 @@ export class ElementAst implements TemplateAst {
return visitor.visitElement(this, context); 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. * 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. * A `<template>` element included in an Angular template.
*/ */
export class EmbeddedTemplateAst implements TemplateAst { 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 directives: DirectiveAst[], public providers: ProviderAst[],
public hasViewContainer: boolean, public children: TemplateAst[], public hasViewContainer: boolean, public children: TemplateAst[],
public ngContentIndex: number, public sourceSpan: ParseSourceSpan) {} public ngContentIndex: number, public sourceSpan: ParseSourceSpan) {}
@ -160,7 +164,7 @@ export class DirectiveAst implements TemplateAst {
constructor(public directive: CompileDirectiveMetadata, constructor(public directive: CompileDirectiveMetadata,
public inputs: BoundDirectivePropertyAst[], public inputs: BoundDirectivePropertyAst[],
public hostProperties: BoundElementPropertyAst[], public hostEvents: BoundEventAst[], public hostProperties: BoundElementPropertyAst[], public hostEvents: BoundEventAst[],
public exportAsVars: VariableAst[], public sourceSpan: ParseSourceSpan) {} public sourceSpan: ParseSourceSpan) {}
visit(visitor: TemplateAstVisitor, context: any): any { visit(visitor: TemplateAstVisitor, context: any): any {
return visitor.visitDirective(this, context); return visitor.visitDirective(this, context);
} }
@ -232,6 +236,7 @@ export interface TemplateAstVisitor {
visitNgContent(ast: NgContentAst, context: any): any; visitNgContent(ast: NgContentAst, context: any): any;
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any; visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any;
visitElement(ast: ElementAst, context: any): any; visitElement(ast: ElementAst, context: any): any;
visitReference(ast: ReferenceAst, context: any): any;
visitVariable(ast: VariableAst, context: any): any; visitVariable(ast: VariableAst, context: any): any;
visitEvent(ast: BoundEventAst, context: any): any; visitEvent(ast: BoundEventAst, context: any): any;
visitElementProperty(ast: BoundElementPropertyAst, context: any): any; visitElementProperty(ast: BoundElementPropertyAst, context: any): any;

View File

@ -13,6 +13,7 @@ import {
isArray isArray
} from 'angular2/src/facade/lang'; } from 'angular2/src/facade/lang';
import {Injectable, Inject, OpaqueToken, Optional} from 'angular2/core'; import {Injectable, Inject, OpaqueToken, Optional} from 'angular2/core';
import {Console} from 'angular2/src/core/console';
import {BaseException} from 'angular2/src/facade/exceptions'; import {BaseException} from 'angular2/src/facade/exceptions';
import { import {
AST, AST,
@ -34,14 +35,14 @@ import {
} from './compile_metadata'; } from './compile_metadata';
import {HtmlParser} from './html_parser'; import {HtmlParser} from './html_parser';
import {splitNsName, mergeNsAndName} from './html_tags'; 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 {MAX_INTERPOLATION_VALUES} from 'angular2/src/core/linker/view_utils';
import { import {
ElementAst, ElementAst,
BoundElementPropertyAst, BoundElementPropertyAst,
BoundEventAst, BoundEventAst,
VariableAst, ReferenceAst,
TemplateAst, TemplateAst,
TemplateAstVisitor, TemplateAstVisitor,
templateVisitAll, templateVisitAll,
@ -54,7 +55,8 @@ import {
DirectiveAst, DirectiveAst,
BoundDirectivePropertyAst, BoundDirectivePropertyAst,
ProviderAst, ProviderAst,
ProviderAstType ProviderAstType,
VariableAst
} from './template_ast'; } from './template_ast';
import {CssSelector, SelectorMatcher} from 'angular2/src/compiler/selector'; import {CssSelector, SelectorMatcher} from 'angular2/src/compiler/selector';
@ -76,19 +78,22 @@ import {
} from './html_ast'; } from './html_ast';
import {splitAtColon} from './util'; import {splitAtColon} from './util';
import {identifierToken, Identifiers} from './identifiers';
import {ProviderElementContext, ProviderViewContext} from './provider_parser'; import {ProviderElementContext, ProviderViewContext} from './provider_parser';
// Group 1 = "bind-" // Group 1 = "bind-"
// Group 2 = "var-" or "#" // Group 2 = "var-"
// Group 3 = "on-" // Group 3 = "let-"
// Group 4 = "bindon-" // Group 4 = "ref-/#"
// Group 5 = the identifier after "bind-", "var-/#", or "on-" // Group 5 = "on-"
// Group 6 = identifier inside [()] // Group 6 = "bindon-"
// Group 7 = identifier inside [] // Group 7 = the identifier after "bind-", "var-/#", or "on-"
// Group 8 = identifier inside () // Group 8 = identifier inside [()]
// Group 9 = identifier inside []
// Group 10 = identifier inside ()
var BIND_NAME_REGEXP = var BIND_NAME_REGEXP =
/^(?:(?:(?:(bind-)|(var-|#)|(on-)|(bindon-))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/g; /^(?:(?:(?:(bind-)|(var-)|(let-)|(ref-|#)|(on-)|(bindon-))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/g;
const TEMPLATE_ELEMENT = 'template'; const TEMPLATE_ELEMENT = 'template';
const TEMPLATE_ATTR = '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 const TEMPLATE_TRANSFORMS = CONST_EXPR(new OpaqueToken('TemplateTransforms'));
export class TemplateParseError extends ParseError { 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 { export class TemplateParseResult {
@ -122,15 +129,20 @@ export class TemplateParseResult {
@Injectable() @Injectable()
export class TemplateParser { export class TemplateParser {
constructor(private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry, constructor(private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry,
private _htmlParser: HtmlParser, private _htmlParser: HtmlParser, private _console: Console,
@Optional() @Inject(TEMPLATE_TRANSFORMS) public transforms: TemplateAstVisitor[]) {} @Optional() @Inject(TEMPLATE_TRANSFORMS) public transforms: TemplateAstVisitor[]) {}
parse(component: CompileDirectiveMetadata, template: string, parse(component: CompileDirectiveMetadata, template: string,
directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[], directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
templateUrl: string): TemplateAst[] { templateUrl: string): TemplateAst[] {
var result = this.tryParse(component, template, directives, pipes, templateUrl); var result = this.tryParse(component, template, directives, pipes, templateUrl);
if (isPresent(result.errors)) { var warnings = result.errors.filter(error => error.level === ParseErrorLevel.WARNING);
var errorString = result.errors.join('\n'); 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}`); throw new BaseException(`Template parse errors:\n${errorString}`);
} }
return result.templateAst; return result.templateAst;
@ -162,7 +174,7 @@ export class TemplateParser {
this.transforms.forEach( this.transforms.forEach(
(transform: TemplateAstVisitor) => { result = templateVisitAll(transform, result); }); (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)); pipes.forEach(pipe => this.pipesByName.set(pipe.name, pipe));
} }
private _reportError(message: string, sourceSpan: ParseSourceSpan) { private _reportError(message: string, sourceSpan: ParseSourceSpan,
this.errors.push(new TemplateParseError(message, sourceSpan)); level: ParseErrorLevel = ParseErrorLevel.FATAL) {
this.errors.push(new TemplateParseError(message, sourceSpan, level));
} }
private _parseInterpolation(value: string, sourceSpan: ParseSourceSpan): ASTWithSource { private _parseInterpolation(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
@ -235,13 +248,15 @@ class TemplateParseVisitor implements HtmlAstVisitor {
private _parseTemplateBindings(value: string, sourceSpan: ParseSourceSpan): TemplateBinding[] { private _parseTemplateBindings(value: string, sourceSpan: ParseSourceSpan): TemplateBinding[] {
var sourceInfo = sourceSpan.start.toString(); var sourceInfo = sourceSpan.start.toString();
try { try {
var bindings = this._exprParser.parseTemplateBindings(value, sourceInfo); var bindingsResult = this._exprParser.parseTemplateBindings(value, sourceInfo);
bindings.forEach((binding) => { bindingsResult.templateBindings.forEach((binding) => {
if (isPresent(binding.expression)) { if (isPresent(binding.expression)) {
this._checkPipes(binding.expression, sourceSpan); this._checkPipes(binding.expression, sourceSpan);
} }
}); });
return bindings; bindingsResult.warnings.forEach(
(warning) => { this._reportError(warning, sourceSpan, ParseErrorLevel.WARNING); });
return bindingsResult.templateBindings;
} catch (e) { } catch (e) {
this._reportError(`${e}`, sourceSpan); this._reportError(`${e}`, sourceSpan);
return []; return [];
@ -299,19 +314,25 @@ class TemplateParseVisitor implements HtmlAstVisitor {
var matchableAttrs: string[][] = []; var matchableAttrs: string[][] = [];
var elementOrDirectiveProps: BoundElementOrDirectiveProperty[] = []; var elementOrDirectiveProps: BoundElementOrDirectiveProperty[] = [];
var vars: VariableAst[] = []; var elementOrDirectiveRefs: ElementOrDirectiveRef[] = [];
var elementVars: VariableAst[] = [];
var events: BoundEventAst[] = []; var events: BoundEventAst[] = [];
var templateElementOrDirectiveProps: BoundElementOrDirectiveProperty[] = []; var templateElementOrDirectiveProps: BoundElementOrDirectiveProperty[] = [];
var templateVars: VariableAst[] = [];
var templateMatchableAttrs: string[][] = []; var templateMatchableAttrs: string[][] = [];
var templateElementVars: VariableAst[] = [];
var hasInlineTemplates = false; var hasInlineTemplates = false;
var attrs = []; var attrs = [];
var lcElName = splitNsName(nodeName.toLowerCase())[1];
var isTemplateElement = lcElName == TEMPLATE_ELEMENT;
element.attrs.forEach(attr => { 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( var hasTemplateBinding = this._parseInlineTemplateBinding(
attr, templateMatchableAttrs, templateElementOrDirectiveProps, templateVars); attr, templateMatchableAttrs, templateElementOrDirectiveProps, templateElementVars);
if (!hasBinding && !hasTemplateBinding) { if (!hasBinding && !hasTemplateBinding) {
// don't include the bindings as attributes as well in the AST // don't include the bindings as attributes as well in the AST
attrs.push(this.visitAttr(attr, null)); 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 elementCssSelector = createElementCssSelector(nodeName, matchableAttrs);
var directiveMetas = this._parseDirectives(this.selectorMatcher, elementCssSelector); var directiveMetas = this._parseDirectives(this.selectorMatcher, elementCssSelector);
var directiveAsts = var references: ReferenceAst[] = [];
this._createDirectiveAsts(element.name, directiveMetas, elementOrDirectiveProps, var directiveAsts = this._createDirectiveAsts(isTemplateElement, element.name, directiveMetas,
isTemplateElement ? [] : vars, element.sourceSpan); elementOrDirectiveProps, elementOrDirectiveRefs,
element.sourceSpan, references);
var elementProps: BoundElementPropertyAst[] = var elementProps: BoundElementPropertyAst[] =
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts); this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts);
var isViewRoot = parent.isTemplateElement || hasInlineTemplates; var isViewRoot = parent.isTemplateElement || hasInlineTemplates;
var providerContext = var providerContext =
new ProviderElementContext(this.providerViewContext, parent.providerContext, isViewRoot, new ProviderElementContext(this.providerViewContext, parent.providerContext, isViewRoot,
directiveAsts, attrs, vars, element.sourceSpan); directiveAsts, attrs, references, element.sourceSpan);
var children = htmlVisitAll( var children = htmlVisitAll(
preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children, preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children,
ElementContext.create(isTemplateElement, directiveAsts, ElementContext.create(isTemplateElement, directiveAsts,
@ -362,17 +382,15 @@ class TemplateParseVisitor implements HtmlAstVisitor {
element.sourceSpan); element.sourceSpan);
parsedElement = new EmbeddedTemplateAst( parsedElement = new EmbeddedTemplateAst(
attrs, events, vars, providerContext.transformedDirectiveAsts, attrs, events, references, elementVars, providerContext.transformedDirectiveAsts,
providerContext.transformProviders, providerContext.transformedHasViewContainer, children, providerContext.transformProviders, providerContext.transformedHasViewContainer, children,
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan); hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
} else { } else {
this._assertOnlyOneComponent(directiveAsts, element.sourceSpan); this._assertOnlyOneComponent(directiveAsts, element.sourceSpan);
var elementExportAsVars = vars.filter(varAst => varAst.value.length === 0);
let ngContentIndex = let ngContentIndex =
hasInlineTemplates ? null : parent.findNgContentIndex(projectionSelector); hasInlineTemplates ? null : parent.findNgContentIndex(projectionSelector);
parsedElement = new ElementAst( parsedElement = new ElementAst(
nodeName, attrs, elementProps, events, elementExportAsVars, nodeName, attrs, elementProps, events, references,
providerContext.transformedDirectiveAsts, providerContext.transformProviders, providerContext.transformedDirectiveAsts, providerContext.transformProviders,
providerContext.transformedHasViewContainer, children, providerContext.transformedHasViewContainer, children,
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan); hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
@ -381,18 +399,18 @@ class TemplateParseVisitor implements HtmlAstVisitor {
var templateCssSelector = createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs); var templateCssSelector = createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
var templateDirectiveMetas = this._parseDirectives(this.selectorMatcher, templateCssSelector); var templateDirectiveMetas = this._parseDirectives(this.selectorMatcher, templateCssSelector);
var templateDirectiveAsts = var templateDirectiveAsts =
this._createDirectiveAsts(element.name, templateDirectiveMetas, this._createDirectiveAsts(true, element.name, templateDirectiveMetas,
templateElementOrDirectiveProps, [], element.sourceSpan); templateElementOrDirectiveProps, [], element.sourceSpan, []);
var templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts( var templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts(
element.name, templateElementOrDirectiveProps, templateDirectiveAsts); element.name, templateElementOrDirectiveProps, templateDirectiveAsts);
this._assertNoComponentsNorElementBindingsOnTemplate( this._assertNoComponentsNorElementBindingsOnTemplate(
templateDirectiveAsts, templateElementProps, element.sourceSpan); templateDirectiveAsts, templateElementProps, element.sourceSpan);
var templateProviderContext = new ProviderElementContext( var templateProviderContext = new ProviderElementContext(
this.providerViewContext, parent.providerContext, parent.isTemplateElement, this.providerViewContext, parent.providerContext, parent.isTemplateElement,
templateDirectiveAsts, [], templateVars, element.sourceSpan); templateDirectiveAsts, [], [], element.sourceSpan);
templateProviderContext.afterElement(); templateProviderContext.afterElement();
parsedElement = new EmbeddedTemplateAst([], [], templateVars, parsedElement = new EmbeddedTemplateAst([], [], [], templateElementVars,
templateProviderContext.transformedDirectiveAsts, templateProviderContext.transformedDirectiveAsts,
templateProviderContext.transformProviders, templateProviderContext.transformProviders,
templateProviderContext.transformedHasViewContainer, templateProviderContext.transformedHasViewContainer,
@ -417,7 +435,6 @@ class TemplateParseVisitor implements HtmlAstVisitor {
var binding = bindings[i]; var binding = bindings[i];
if (binding.keyIsVar) { if (binding.keyIsVar) {
targetVars.push(new VariableAst(binding.key, binding.name, attr.sourceSpan)); targetVars.push(new VariableAst(binding.key, binding.name, attr.sourceSpan));
targetMatchableAttrs.push([binding.key, binding.name]);
} else if (isPresent(binding.expression)) { } else if (isPresent(binding.expression)) {
this._parsePropertyAst(binding.key, binding.expression, attr.sourceSpan, this._parsePropertyAst(binding.key, binding.expression, attr.sourceSpan,
targetMatchableAttrs, targetProps); targetMatchableAttrs, targetProps);
@ -431,9 +448,10 @@ class TemplateParseVisitor implements HtmlAstVisitor {
return false; return false;
} }
private _parseAttr(attr: HtmlAttrAst, targetMatchableAttrs: string[][], private _parseAttr(isTemplateElement: boolean, attr: HtmlAttrAst,
targetMatchableAttrs: string[][],
targetProps: BoundElementOrDirectiveProperty[], targetEvents: BoundEventAst[], targetProps: BoundElementOrDirectiveProperty[], targetEvents: BoundEventAst[],
targetVars: VariableAst[]): boolean { targetRefs: ElementOrDirectiveRef[], targetVars: VariableAst[]): boolean {
var attrName = this._normalizeAttributeName(attr.name); var attrName = this._normalizeAttributeName(attr.name);
var attrValue = attr.value; var attrValue = attr.value;
var bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName); var bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName);
@ -441,36 +459,55 @@ class TemplateParseVisitor implements HtmlAstVisitor {
if (isPresent(bindParts)) { if (isPresent(bindParts)) {
hasBinding = true; hasBinding = true;
if (isPresent(bindParts[1])) { // match: bind-prop 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, this._parseProperty(bindParts[7], attrValue, attr.sourceSpan, targetMatchableAttrs,
targetProps); targetProps);
} else if (isPresent(bindParts[8])) { // match: (event) } else if (isPresent(bindParts[2])) { // match: var-name / var-name="iden"
this._parseEvent(bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs, 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); targetEvents);
} }
} else { } else {
@ -495,6 +532,14 @@ class TemplateParseVisitor implements HtmlAstVisitor {
targetVars.push(new VariableAst(identifier, value, sourceSpan)); 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, private _parseProperty(name: string, expression: string, sourceSpan: ParseSourceSpan,
targetMatchableAttrs: string[][], targetMatchableAttrs: string[][],
targetProps: BoundElementOrDirectiveProperty[]) { targetProps: BoundElementOrDirectiveProperty[]) {
@ -547,33 +592,28 @@ class TemplateParseVisitor implements HtmlAstVisitor {
private _parseDirectives(selectorMatcher: SelectorMatcher, private _parseDirectives(selectorMatcher: SelectorMatcher,
elementCssSelector: CssSelector): CompileDirectiveMetadata[] { 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, // Need to sort the directives so that we get consistent results throughout,
// as selectorMatcher uses Maps inside. // as selectorMatcher uses Maps inside.
// Also need to make components the first directive in the array // Also dedupe directives as they might match more than one time!
ListWrapper.sort(directives, var directives = ListWrapper.createFixedSize(this.directivesIndex.size);
(dir1: CompileDirectiveMetadata, dir2: CompileDirectiveMetadata) => { selectorMatcher.match(elementCssSelector, (selector, directive) => {
var dir1Comp = dir1.isComponent; directives[this.directivesIndex.get(directive)] = directive;
var dir2Comp = dir2.isComponent; });
if (dir1Comp && !dir2Comp) { return directives.filter(dir => isPresent(dir));
return -1;
} else if (!dir1Comp && dir2Comp) {
return 1;
} else {
return this.directivesIndex.get(dir1) - this.directivesIndex.get(dir2);
}
});
return directives;
} }
private _createDirectiveAsts(elementName: string, directives: CompileDirectiveMetadata[], private _createDirectiveAsts(isTemplateElement: boolean, elementName: string,
directives: CompileDirectiveMetadata[],
props: BoundElementOrDirectiveProperty[], props: BoundElementOrDirectiveProperty[],
possibleExportAsVars: VariableAst[], elementOrDirectiveRefs: ElementOrDirectiveRef[],
sourceSpan: ParseSourceSpan): DirectiveAst[] { sourceSpan: ParseSourceSpan,
var matchedVariables = new Set<string>(); targetReferences: ReferenceAst[]): DirectiveAst[] {
var matchedReferences = new Set<string>();
var component: CompileDirectiveMetadata = null;
var directiveAsts = directives.map((directive: CompileDirectiveMetadata) => { var directiveAsts = directives.map((directive: CompileDirectiveMetadata) => {
if (directive.isComponent) {
component = directive;
}
var hostProperties: BoundElementPropertyAst[] = []; var hostProperties: BoundElementPropertyAst[] = [];
var hostEvents: BoundEventAst[] = []; var hostEvents: BoundEventAst[] = [];
var directiveProperties: BoundDirectivePropertyAst[] = []; var directiveProperties: BoundDirectivePropertyAst[] = [];
@ -581,21 +621,29 @@ class TemplateParseVisitor implements HtmlAstVisitor {
hostProperties); hostProperties);
this._createDirectiveHostEventAsts(directive.hostListeners, sourceSpan, hostEvents); this._createDirectiveHostEventAsts(directive.hostListeners, sourceSpan, hostEvents);
this._createDirectivePropertyAsts(directive.inputs, props, directiveProperties); this._createDirectivePropertyAsts(directive.inputs, props, directiveProperties);
var exportAsVars = []; elementOrDirectiveRefs.forEach((elOrDirRef) => {
possibleExportAsVars.forEach((varAst) => { if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
if ((varAst.value.length === 0 && directive.isComponent) || (directive.exportAs == elOrDirRef.value)) {
(directive.exportAs == varAst.value)) { targetReferences.push(new ReferenceAst(elOrDirRef.name, identifierToken(directive.type),
exportAsVars.push(varAst); elOrDirRef.sourceSpan));
matchedVariables.add(varAst.name); matchedReferences.add(elOrDirRef.name);
} }
}); });
return new DirectiveAst(directive, directiveProperties, hostProperties, hostEvents, return new DirectiveAst(directive, directiveProperties, hostProperties, hostEvents,
exportAsVars, sourceSpan); sourceSpan);
}); });
possibleExportAsVars.forEach((varAst) => { elementOrDirectiveRefs.forEach((elOrDirRef) => {
if (varAst.value.length > 0 && !SetWrapper.has(matchedVariables, varAst.name)) { if (elOrDirRef.value.length > 0) {
this._reportError(`There is no directive with "exportAs" set to "${varAst.value}"`, if (!SetWrapper.has(matchedReferences, elOrDirRef.name)) {
varAst.sourceSpan); 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; return directiveAsts;
@ -793,6 +841,10 @@ class BoundElementOrDirectiveProperty {
public sourceSpan: ParseSourceSpan) {} public sourceSpan: ParseSourceSpan) {}
} }
class ElementOrDirectiveRef {
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
}
export function splitClasses(classAttrValue: string): string[] { export function splitClasses(classAttrValue: string): string[] {
return StringWrapper.split(classAttrValue.trim(), /\s+/g); return StringWrapper.split(classAttrValue.trim(), /\s+/g);
} }

View File

@ -4,7 +4,7 @@ import {InjectMethodVars} from './constants';
import {CompileView} from './compile_view'; import {CompileView} from './compile_view';
import {isPresent, isBlank} from 'angular2/src/facade/lang'; import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {TemplateAst, ProviderAst, ProviderAstType} from '../template_ast'; import {TemplateAst, ProviderAst, ProviderAstType, ReferenceAst} from '../template_ast';
import { import {
CompileTokenMap, CompileTokenMap,
CompileDirectiveMetadata, CompileDirectiveMetadata,
@ -13,7 +13,7 @@ import {
CompileProviderMetadata, CompileProviderMetadata,
CompileDiDependencyMetadata, CompileDiDependencyMetadata,
CompileIdentifierMetadata, CompileIdentifierMetadata,
CompileTypeMetadata CompileTypeMetadata,
} from '../compile_metadata'; } from '../compile_metadata';
import {getPropertyInView, createDiTokenExpression, injectFromViewParentInjector} from './util'; import {getPropertyInView, createDiTokenExpression, injectFromViewParentInjector} from './util';
import {CompileQuery, createQueryList, addQueryToTokenMap} from './compile_query'; import {CompileQuery, createQueryList, addQueryToTokenMap} from './compile_query';
@ -30,7 +30,7 @@ export class CompileNode {
export class CompileElement extends CompileNode { export class CompileElement extends CompileNode {
static createNull(): CompileElement { 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; private _compViewExpr: o.Expression = null;
@ -47,15 +47,18 @@ export class CompileElement extends CompileNode {
public contentNodesByNgContentIndex: Array<o.Expression>[] = null; public contentNodesByNgContentIndex: Array<o.Expression>[] = null;
public embeddedView: CompileView; public embeddedView: CompileView;
public directiveInstances: o.Expression[]; public directiveInstances: o.Expression[];
public referenceTokens: {[key: string]: CompileTokenMetadata};
constructor(parent: CompileElement, view: CompileView, nodeIndex: number, constructor(parent: CompileElement, view: CompileView, nodeIndex: number,
renderNode: o.Expression, sourceAst: TemplateAst, renderNode: o.Expression, sourceAst: TemplateAst,
public component: CompileDirectiveMetadata, public component: CompileDirectiveMetadata,
private _directives: CompileDirectiveMetadata[], private _directives: CompileDirectiveMetadata[],
private _resolvedProvidersArray: ProviderAst[], public hasViewContainer: boolean, private _resolvedProvidersArray: ProviderAst[], public hasViewContainer: boolean,
public hasEmbeddedView: boolean, public hasEmbeddedView: boolean, references: ReferenceAst[]) {
public variableTokens: {[key: string]: CompileTokenMetadata}) {
super(parent, view, nodeIndex, renderNode, sourceAst); 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.elementRef = o.importExpr(Identifiers.ElementRef).instantiate([this.renderNode]);
this._instances.add(identifierToken(Identifiers.ElementRef), this.elementRef); this._instances.add(identifierToken(Identifiers.ElementRef), this.elementRef);
this.injector = o.THIS_EXPR.callMethod('injector', [o.literal(this.nodeIndex)]); this.injector = o.THIS_EXPR.callMethod('injector', [o.literal(this.nodeIndex)]);
@ -167,15 +170,15 @@ export class CompileElement extends CompileNode {
queriesWithReads, queriesWithReads,
queriesForProvider.map(query => new _QueryWithRead(query, resolvedProvider.token))); queriesForProvider.map(query => new _QueryWithRead(query, resolvedProvider.token)));
}); });
StringMapWrapper.forEach(this.variableTokens, (_, varName) => { StringMapWrapper.forEach(this.referenceTokens, (_, varName) => {
var token = this.variableTokens[varName]; var token = this.referenceTokens[varName];
var varValue; var varValue;
if (isPresent(token)) { if (isPresent(token)) {
varValue = this._instances.get(token); varValue = this._instances.get(token);
} else { } else {
varValue = this.renderNode; varValue = this.renderNode;
} }
this.view.variables.set(varName, varValue); this.view.locals.set(varName, varValue);
var varToken = new CompileTokenMetadata({value: varName}); var varToken = new CompileTokenMetadata({value: varName});
ListWrapper.addAll(queriesWithReads, this._getQueriesFor(varToken) ListWrapper.addAll(queriesWithReads, this._getQueriesFor(varToken)
.map(query => new _QueryWithRead(query, varToken))); .map(query => new _QueryWithRead(query, varToken)));
@ -186,8 +189,8 @@ export class CompileElement extends CompileNode {
// query for an identifier // query for an identifier
value = this._instances.get(queryWithRead.read); value = this._instances.get(queryWithRead.read);
} else { } else {
// query for a variable // query for a reference
var token = this.variableTokens[queryWithRead.read.value]; var token = this.referenceTokens[queryWithRead.read.value];
if (isPresent(token)) { if (isPresent(token)) {
value = this._instances.get(token); value = this._instances.get(token);
} else { } else {
@ -247,12 +250,6 @@ export class CompileElement extends CompileNode {
(resolvedProvider) => createDiTokenExpression(resolvedProvider.token)); (resolvedProvider) => createDiTokenExpression(resolvedProvider.token));
} }
getDeclaredVariablesNames(): string[] {
var res = [];
StringMapWrapper.forEach(this.variableTokens, (_, key) => { res.push(key); });
return res;
}
private _getQueriesFor(token: CompileTokenMetadata): CompileQuery[] { private _getQueriesFor(token: CompileTokenMetadata): CompileQuery[] {
var result: CompileQuery[] = []; var result: CompileQuery[] = [];
var currentEl: CompileElement = this; var currentEl: CompileElement = this;

View File

@ -56,7 +56,7 @@ export class CompileView implements NameResolver {
public componentView: CompileView; public componentView: CompileView;
public purePipes = new Map<string, CompilePipe>(); public purePipes = new Map<string, CompilePipe>();
public pipes: CompilePipe[] = []; public pipes: CompilePipe[] = [];
public variables = new Map<string, o.Expression>(); public locals = new Map<string, o.Expression>();
public className: string; public className: string;
public classType: o.Type; public classType: o.Type;
public viewFactory: o.ReadVarExpr; public viewFactory: o.ReadVarExpr;
@ -112,7 +112,7 @@ export class CompileView implements NameResolver {
} }
this.viewQueries = viewQueries; this.viewQueries = viewQueries;
templateVariableBindings.forEach((entry) => { 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()) { if (!this.declarationElement.isNull()) {
@ -133,15 +133,15 @@ export class CompileView implements NameResolver {
return pipe.call(this, [input].concat(args)); return pipe.call(this, [input].concat(args));
} }
getVariable(name: string): o.Expression { getLocal(name: string): o.Expression {
if (name == EventHandlerVars.event.name) { if (name == EventHandlerVars.event.name) {
return EventHandlerVars.event; return EventHandlerVars.event;
} }
var currView: CompileView = this; var currView: CompileView = this;
var result = currView.variables.get(name); var result = currView.locals.get(name);
while (isBlank(result) && isPresent(currView.declarationElement.view)) { while (isBlank(result) && isPresent(currView.declarationElement.view)) {
currView = currView.declarationElement.view; currView = currView.declarationElement.view;
result = currView.variables.get(name); result = currView.locals.get(name);
} }
if (isPresent(result)) { if (isPresent(result)) {
return getPropertyInView(result, this, currView); return getPropertyInView(result, this, currView);

View File

@ -9,7 +9,7 @@ var IMPLICIT_RECEIVER = o.variable('#implicit');
export interface NameResolver { export interface NameResolver {
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression; 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; createLiteralArray(values: o.Expression[]): o.Expression;
createLiteralMap(values: Array<Array<string | 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 result = null;
var receiver = ast.receiver.visit(this, _Mode.Expression); var receiver = ast.receiver.visit(this, _Mode.Expression);
if (receiver === IMPLICIT_RECEIVER) { if (receiver === IMPLICIT_RECEIVER) {
var varExpr = this._nameResolver.getVariable(ast.name); var varExpr = this._nameResolver.getLocal(ast.name);
if (isPresent(varExpr)) { if (isPresent(varExpr)) {
result = varExpr.callFn(args); result = varExpr.callFn(args);
} else { } else {
@ -204,7 +204,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
var result = null; var result = null;
var receiver = ast.receiver.visit(this, _Mode.Expression); var receiver = ast.receiver.visit(this, _Mode.Expression);
if (receiver === IMPLICIT_RECEIVER) { if (receiver === IMPLICIT_RECEIVER) {
result = this._nameResolver.getVariable(ast.name); result = this._nameResolver.getLocal(ast.name);
if (isBlank(result)) { if (isBlank(result)) {
receiver = this._implicitReceiver; receiver = this._implicitReceiver;
} }
@ -217,9 +217,9 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any { visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
var receiver: o.Expression = ast.receiver.visit(this, _Mode.Expression); var receiver: o.Expression = ast.receiver.visit(this, _Mode.Expression);
if (receiver === IMPLICIT_RECEIVER) { if (receiver === IMPLICIT_RECEIVER) {
var varExpr = this._nameResolver.getVariable(ast.name); var varExpr = this._nameResolver.getLocal(ast.name);
if (isPresent(varExpr)) { if (isPresent(varExpr)) {
throw new BaseException('Cannot reassign a variable binding'); throw new BaseException('Cannot assign to a reference or variable!');
} }
receiver = this._implicitReceiver; receiver = this._implicitReceiver;
} }

View File

@ -7,6 +7,7 @@ import {
NgContentAst, NgContentAst,
EmbeddedTemplateAst, EmbeddedTemplateAst,
ElementAst, ElementAst,
ReferenceAst,
VariableAst, VariableAst,
BoundEventAst, BoundEventAst,
BoundElementPropertyAst, BoundElementPropertyAst,
@ -113,6 +114,7 @@ class ViewBinderVisitor implements TemplateAstVisitor {
return null; return null;
} }
visitReference(ast: ReferenceAst, ctx: any): any { return null; }
visitVariable(ast: VariableAst, ctx: any): any { return null; } visitVariable(ast: VariableAst, ctx: any): any { return null; }
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; } visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; } visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }

View File

@ -26,6 +26,7 @@ import {
NgContentAst, NgContentAst,
EmbeddedTemplateAst, EmbeddedTemplateAst,
ElementAst, ElementAst,
ReferenceAst,
VariableAst, VariableAst,
BoundEventAst, BoundEventAst,
BoundElementPropertyAst, BoundElementPropertyAst,
@ -201,8 +202,6 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
var component = ast.getComponent(); var component = ast.getComponent();
var directives = ast.directives.map(directiveAst => directiveAst.directive); var directives = ast.directives.map(directiveAst => directiveAst.directive);
var variables =
_readHtmlAndDirectiveVariables(ast.exportAsVars, ast.directives, this.view.viewType);
var htmlAttrs = _readHtmlAttrs(ast.attrs); var htmlAttrs = _readHtmlAttrs(ast.attrs);
var attrNameAndValues = _mergeHtmlAndDirectiveAttrs(htmlAttrs, directives); var attrNameAndValues = _mergeHtmlAndDirectiveAttrs(htmlAttrs, directives);
for (var i = 0; i < attrNameAndValues.length; i++) { for (var i = 0; i < attrNameAndValues.length; i++) {
@ -216,7 +215,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
} }
var compileElement = var compileElement =
new CompileElement(parent, this.view, nodeIndex, renderNode, ast, component, directives, 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); this.view.nodes.push(compileElement);
var compViewExpr: o.ReadVarExpr = null; var compViewExpr: o.ReadVarExpr = null;
if (isPresent(component)) { if (isPresent(component)) {
@ -269,13 +268,13 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
.toStmt()); .toStmt());
var renderNode = o.THIS_EXPR.prop(fieldName); 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]); varAst => [varAst.value.length > 0 ? varAst.value : IMPLICIT_TEMPLATE_VAR, varAst.name]);
var directives = ast.directives.map(directiveAst => directiveAst.directive); var directives = ast.directives.map(directiveAst => directiveAst.directive);
var compileElement = var compileElement =
new CompileElement(parent, this.view, nodeIndex, renderNode, ast, null, directives, 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.view.nodes.push(compileElement);
this.nestedViewCount++; this.nestedViewCount++;
@ -297,6 +296,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
return null; return null;
} }
visitReference(ast: ReferenceAst, ctx: any): any { return null; }
visitVariable(ast: VariableAst, ctx: any): any { return null; } visitVariable(ast: VariableAst, ctx: any): any { return null; }
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; } visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
visitElementProperty(ast: BoundElementPropertyAst, 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; 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 { function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: string): string {
if (attrName == CLASS_ATTR || attrName == STYLE_ATTR) { if (attrName == CLASS_ATTR || attrName == STYLE_ATTR) {
return `${attrValue1} ${attrValue2}`; return `${attrValue1} ${attrValue2}`;
@ -392,7 +374,7 @@ function createStaticNodeDebugInfo(node: CompileNode): o.Expression {
if (isPresent(compileElement.component)) { if (isPresent(compileElement.component)) {
componentToken = createDiTokenExpression(identifierToken(compileElement.component.type)); componentToken = createDiTokenExpression(identifierToken(compileElement.component.type));
} }
StringMapWrapper.forEach(compileElement.variableTokens, (token, varName) => { StringMapWrapper.forEach(compileElement.referenceTokens, (token, varName) => {
varTokenEntries.push( varTokenEntries.push(
[varName, isPresent(token) ? createDiTokenExpression(token) : o.NULL_EXPR]); [varName, isPresent(token) ? createDiTokenExpression(token) : o.NULL_EXPR]);
}); });

View File

@ -70,7 +70,7 @@ export abstract class ChangeDetectorRef {
* @Component({ * @Component({
* selector: 'giant-list', * selector: 'giant-list',
* template: ` * template: `
* <li *ngFor="#d of dataProvider.data">Data {{d}}</lig> * <li *ngFor="let d of dataProvider.data">Data {{d}}</lig>
* `, * `,
* directives: [NgFor] * directives: [NgFor]
* }) * })

View File

@ -1,7 +1,13 @@
import {Injectable} from 'angular2/src/core/di'; 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() @Injectable()
export class Console { export class Console {
log(message: string): void { print(message); } log(message: string): void { print(message); }
} // Note: for reporting errors use `DOM.logError()` as it is platform specific
warn(message: string): void { _warnImpl(message); }
}

View File

@ -8,7 +8,7 @@ import {ViewType} from './view_type';
@CONST() @CONST()
export class StaticNodeDebugInfo { export class StaticNodeDebugInfo {
constructor(public providerTokens: any[], public componentToken: any, constructor(public providerTokens: any[], public componentToken: any,
public varTokens: {[key: string]: any}) {} public refTokens: {[key: string]: any}) {}
} }
export class DebugContext implements RenderDebugInfo { export class DebugContext implements RenderDebugInfo {
@ -61,15 +61,15 @@ export class DebugContext implements RenderDebugInfo {
ListWrapper.forEachWithIndex( ListWrapper.forEachWithIndex(
this._view.staticNodeDebugInfos, this._view.staticNodeDebugInfos,
(staticNodeInfo: StaticNodeDebugInfo, nodeIndex: number) => { (staticNodeInfo: StaticNodeDebugInfo, nodeIndex: number) => {
var vars = staticNodeInfo.varTokens; var refs = staticNodeInfo.refTokens;
StringMapWrapper.forEach(vars, (varToken, varName) => { StringMapWrapper.forEach(refs, (refToken, refName) => {
var varValue; var varValue;
if (isBlank(varToken)) { if (isBlank(refToken)) {
varValue = isPresent(this._view.allNodes) ? this._view.allNodes[nodeIndex] : null; varValue = isPresent(this._view.allNodes) ? this._view.allNodes[nodeIndex] : null;
} else { } 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, StringMapWrapper.forEach(this._view.locals,

View File

@ -11,7 +11,7 @@ import {Observable, EventEmitter} from 'angular2/src/facade/async';
* *
* Implements an iterable interface, therefore it can be used in both ES6 * 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 * 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`. * Changes can be observed by subscribing to the changes `Observable`.
* *

View File

@ -34,7 +34,7 @@ export abstract class ViewRef extends ChangeDetectorRef {
* ``` * ```
* Count: {{items.length}} * Count: {{items.length}}
* <ul> * <ul>
* <li *ngFor="var item of items">{{item}}</li> * <li *ngFor="let item of items">{{item}}</li>
* </ul> * </ul>
* ``` * ```
* *
@ -44,7 +44,7 @@ export abstract class ViewRef extends ChangeDetectorRef {
* ``` * ```
* Count: {{items.length}} * Count: {{items.length}}
* <ul> * <ul>
* <template ngFor var-item [ngForOf]="items"></template> * <template ngFor let-item [ngForOf]="items"></template>
* </ul> * </ul>
* ``` * ```
* *

View File

@ -964,7 +964,7 @@ export var Attribute: AttributeMetadataFactory = makeParamDecorator(AttributeMet
* ```html * ```html
* <tabs> * <tabs>
* <pane title="Overview">...</pane> * <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> * </tabs>
* ``` * ```
* *
@ -983,7 +983,7 @@ export var Attribute: AttributeMetadataFactory = makeParamDecorator(AttributeMet
* selector: 'tabs', * selector: 'tabs',
* template: ` * template: `
* <ul> * <ul>
* <li *ngFor="#pane of panes">{{pane.title}}</li> * <li *ngFor="let pane of panes">{{pane.title}}</li>
* </ul> * </ul>
* <ng-content></ng-content> * <ng-content></ng-content>
* ` * `

View File

@ -46,7 +46,7 @@ export class AttributeMetadata extends DependencyMetadata {
* ```html * ```html
* <tabs> * <tabs>
* <pane title="Overview">...</pane> * <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> * </tabs>
* ``` * ```
* *
@ -65,7 +65,7 @@ export class AttributeMetadata extends DependencyMetadata {
* selector: 'tabs', * selector: 'tabs',
* template: ` * template: `
* <ul> * <ul>
* <li *ngFor="#pane of panes">{{pane.title}}</li> * <li *ngFor="let pane of panes">{{pane.title}}</li>
* </ul> * </ul>
* <ng-content></ng-content> * <ng-content></ng-content>
* ` * `

View File

@ -149,7 +149,7 @@ export interface OnInit { ngOnInit(); }
* template: ` * template: `
* <p>Changes:</p> * <p>Changes:</p>
* <ul> * <ul>
* <li *ngFor="#line of logs">{{line}}</li> * <li *ngFor="let line of logs">{{line}}</li>
* </ul>`, * </ul>`,
* directives: [NgFor] * directives: [NgFor]
* }) * })

View File

@ -104,7 +104,7 @@ export class ViewMetadata {
* directives: [NgFor] * directives: [NgFor]
* template: ' * template: '
* <ul> * <ul>
* <li *ngFor="#item of items">{{item}}</li> * <li *ngFor="let item of items">{{item}}</li>
* </ul>' * </ul>'
* }) * })
* class MyComponent { * class MyComponent {

View File

@ -298,6 +298,10 @@ bool isJsObject(o) {
return false; return false;
} }
warn(o) {
print(o);
}
// Functions below are noop in Dart. Imperatively controlling dev mode kills // Functions below are noop in Dart. Imperatively controlling dev mode kills
// tree shaking. We should only rely on `assertionsEnabled`. // tree shaking. We should only rely on `assertionsEnabled`.
@Deprecated('Do not use this function. It is for JS only. There is no alternative.') @Deprecated('Do not use this function. It is for JS only. There is no alternative.')

View File

@ -408,6 +408,10 @@ export function print(obj: Error | Object) {
console.log(obj); 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... // Can't be all uppercase as our transpiler would think it is a special directive...
export class Json { export class Json {
static parse(s: string): Object { return _global.JSON.parse(s); } static parse(s: string): Object { return _global.JSON.parse(s); }

View File

@ -185,11 +185,13 @@ export class RouterLinkTransform implements TemplateAstVisitor {
let updatedChildren = ast.children.map(c => c.visit(this, context)); let updatedChildren = ast.children.map(c => c.visit(this, context));
let updatedInputs = ast.inputs.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)); 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, updatedDirectives, ast.providers, ast.hasViewContainer, updatedChildren,
ast.ngContentIndex, ast.sourceSpan); ast.ngContentIndex, ast.sourceSpan);
} }
visitReference(ast: any, context: any): any { return ast; }
visitVariable(ast: any, context: any): any { return ast; } visitVariable(ast: any, context: any): any { return ast; }
visitEvent(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 { visitDirective(ast: DirectiveAst, context: any): any {
let updatedInputs = ast.inputs.map(c => c.visit(this, context)); let updatedInputs = ast.inputs.map(c => c.visit(this, context));
return new DirectiveAst(ast.directive, updatedInputs, ast.hostProperties, ast.hostEvents, return new DirectiveAst(ast.directive, updatedInputs, ast.hostProperties, ast.hostEvents,
ast.exportAsVars, ast.sourceSpan); ast.sourceSpan);
} }
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {

View File

@ -29,7 +29,7 @@ export function main() {
it('should clean up when the directive is destroyed', it('should clean up when the directive is destroyed',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = '<div *ngFor="var item of items" [ngClass]="item"></div>'; var template = '<div *ngFor="let item of items" [ngClass]="item"></div>';
tcb.overrideTemplate(TestComponent, template) tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent) .createAsync(TestComponent)
.then((fixture) => { .then((fixture) => {

View File

@ -23,7 +23,7 @@ import {By} from 'angular2/platform/common_dom';
export function main() { export function main() {
describe('ngFor', () => { describe('ngFor', () => {
var TEMPLATE = var TEMPLATE =
'<div><copy-me template="ngFor #item of items">{{item.toString()}};</copy-me></div>'; '<div><copy-me template="ngFor let item of items">{{item.toString()}};</copy-me></div>';
it('should reflect initial elements', it('should reflect initial elements',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
@ -100,7 +100,7 @@ export function main() {
it('should iterate over an array of objects', it('should iterate over an array of objects',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = '<ul><li template="ngFor #item of items">{{item["name"]}};</li></ul>'; var template = '<ul><li template="ngFor let item of items">{{item["name"]}};</li></ul>';
tcb.overrideTemplate(TestComponent, template) tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent) .createAsync(TestComponent)
@ -130,7 +130,7 @@ export function main() {
it('should gracefully handle nulls', it('should gracefully handle nulls',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = '<ul><li template="ngFor #item of null">{{item}};</li></ul>'; var template = '<ul><li template="ngFor let item of null">{{item}};</li></ul>';
tcb.overrideTemplate(TestComponent, template) tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent) .createAsync(TestComponent)
.then((fixture) => { .then((fixture) => {
@ -207,8 +207,8 @@ export function main() {
it('should repeat over nested arrays', it('should repeat over nested arrays',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = '<div>' + var template = '<div>' +
'<div template="ngFor #item of items">' + '<div template="ngFor let item of items">' +
'<div template="ngFor #subitem of item">' + '<div template="ngFor let subitem of item">' +
'{{subitem}}-{{item.length}};' + '{{subitem}}-{{item.length}};' +
'</div>|' + '</div>|' +
'</div>' + '</div>' +
@ -233,8 +233,8 @@ export function main() {
it('should repeat over nested arrays with no intermediate element', it('should repeat over nested arrays with no intermediate element',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = '<div><template ngFor #item [ngForOf]="items">' + var template = '<div><template ngFor let-item [ngForOf]="items">' +
'<div template="ngFor #subitem of item">' + '<div template="ngFor let subitem of item">' +
'{{subitem}}-{{item.length}};' + '{{subitem}}-{{item.length}};' +
'</div></template></div>'; '</div></template></div>';
@ -255,7 +255,7 @@ export function main() {
it('should repeat over nested ngIf that are the last node in the ngFor temlate', it('should repeat over nested ngIf that are the last node in the ngFor temlate',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = var template =
`<div><template ngFor #item [ngForOf]="items" #i="index"><div>{{i}}|</div>` + `<div><template ngFor let-item [ngForOf]="items" let-i="index"><div>{{i}}|</div>` +
`<div *ngIf="i % 2 == 0">even|</div></template></div>`; `<div *ngIf="i % 2 == 0">even|</div></template></div>`;
tcb.overrideTemplate(TestComponent, template) tcb.overrideTemplate(TestComponent, template)
@ -282,7 +282,7 @@ export function main() {
it('should display indices correctly', it('should display indices correctly',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = var template =
'<div><copy-me template="ngFor: var item of items; var i=index">{{i.toString()}}</copy-me></div>'; '<div><copy-me template="ngFor: let item of items; let i=index">{{i.toString()}}</copy-me></div>';
tcb.overrideTemplate(TestComponent, template) tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent) .createAsync(TestComponent)
@ -301,7 +301,7 @@ export function main() {
it('should display first item correctly', it('should display first item correctly',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = var template =
'<div><copy-me template="ngFor: var item of items; var isFirst=first">{{isFirst.toString()}}</copy-me></div>'; '<div><copy-me template="ngFor: let item of items; let isFirst=first">{{isFirst.toString()}}</copy-me></div>';
tcb.overrideTemplate(TestComponent, template) tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent) .createAsync(TestComponent)
@ -320,7 +320,7 @@ export function main() {
it('should display last item correctly', it('should display last item correctly',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = var template =
'<div><copy-me template="ngFor: var item of items; var isLast=last">{{isLast.toString()}}</copy-me></div>'; '<div><copy-me template="ngFor: let item of items; let isLast=last">{{isLast.toString()}}</copy-me></div>';
tcb.overrideTemplate(TestComponent, template) tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent) .createAsync(TestComponent)
@ -339,7 +339,7 @@ export function main() {
it('should display even items correctly', it('should display even items correctly',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = var template =
'<div><copy-me template="ngFor: var item of items; var isEven=even">{{isEven.toString()}}</copy-me></div>'; '<div><copy-me template="ngFor: let item of items; let isEven=even">{{isEven.toString()}}</copy-me></div>';
tcb.overrideTemplate(TestComponent, template) tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent) .createAsync(TestComponent)
@ -358,7 +358,7 @@ export function main() {
it('should display odd items correctly', it('should display odd items correctly',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = var template =
'<div><copy-me template="ngFor: var item of items; var isOdd=odd">{{isOdd.toString()}}</copy-me></div>'; '<div><copy-me template="ngFor: let item of items; let isOdd=odd">{{isOdd.toString()}}</copy-me></div>';
tcb.overrideTemplate(TestComponent, template) tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent) .createAsync(TestComponent)
@ -381,7 +381,7 @@ export function main() {
'<ul><template ngFor [ngForOf]="items" [ngForTemplate]="contentTpl"></template></ul>') '<ul><template ngFor [ngForOf]="items" [ngForTemplate]="contentTpl"></template></ul>')
.overrideTemplate( .overrideTemplate(
ComponentUsingTestComponent, ComponentUsingTestComponent,
'<test-cmp><li template="#item #i=index">{{i}}: {{item}};</li></test-cmp>') '<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>')
.createAsync(ComponentUsingTestComponent) .createAsync(ComponentUsingTestComponent)
.then((fixture) => { .then((fixture) => {
var testComponent = fixture.debugElement.children[0]; var testComponent = fixture.debugElement.children[0];
@ -395,8 +395,8 @@ export function main() {
it('should use a default template if a custom one is null', it('should use a default template if a custom one is null',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb.overrideTemplate(TestComponent, `<ul><template ngFor #item [ngForOf]="items" tcb.overrideTemplate(TestComponent, `<ul><template ngFor let-item [ngForOf]="items"
[ngForTemplate]="contentTpl" #i="index">{{i}}: {{item}};</template></ul>`) [ngForTemplate]="contentTpl" let-i="index">{{i}}: {{item}};</template></ul>`)
.overrideTemplate(ComponentUsingTestComponent, '<test-cmp></test-cmp>') .overrideTemplate(ComponentUsingTestComponent, '<test-cmp></test-cmp>')
.createAsync(ComponentUsingTestComponent) .createAsync(ComponentUsingTestComponent)
.then((fixture) => { .then((fixture) => {
@ -411,11 +411,11 @@ export function main() {
it('should use a custom template when both default and a custom one are present', it('should use a custom template when both default and a custom one are present',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb.overrideTemplate(TestComponent, `<ul><template ngFor #item [ngForOf]="items" tcb.overrideTemplate(TestComponent, `<ul><template ngFor let-item [ngForOf]="items"
[ngForTemplate]="contentTpl" #i="index">{{i}}=> {{item}};</template></ul>`) [ngForTemplate]="contentTpl" let-i="index">{{i}}=> {{item}};</template></ul>`)
.overrideTemplate( .overrideTemplate(
ComponentUsingTestComponent, ComponentUsingTestComponent,
'<test-cmp><li template="#item #i=index">{{i}}: {{item}};</li></test-cmp>') '<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>')
.createAsync(ComponentUsingTestComponent) .createAsync(ComponentUsingTestComponent)
.then((fixture) => { .then((fixture) => {
var testComponent = fixture.debugElement.children[0]; var testComponent = fixture.debugElement.children[0];
@ -431,7 +431,7 @@ export function main() {
it('should not replace tracked items', it('should not replace tracked items',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = var template =
`<template ngFor #item [ngForOf]="items" [ngForTrackBy]="trackById" #i="index"> `<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById" let-i="index">
<p>{{items[i]}}</p> <p>{{items[i]}}</p>
</template>`; </template>`;
tcb.overrideTemplate(TestComponent, template) tcb.overrideTemplate(TestComponent, template)
@ -453,7 +453,7 @@ export function main() {
it('should update implicit local variable on view', it('should update implicit local variable on view',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = var template =
`<div><template ngFor #item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`; `<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
tcb.overrideTemplate(TestComponent, template) tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent) .createAsync(TestComponent)
.then((fixture) => { .then((fixture) => {
@ -469,7 +469,7 @@ export function main() {
it('should move items around and keep them updated ', it('should move items around and keep them updated ',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = var template =
`<div><template ngFor #item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`; `<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
tcb.overrideTemplate(TestComponent, template) tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent) .createAsync(TestComponent)
.then((fixture) => { .then((fixture) => {
@ -488,7 +488,7 @@ export function main() {
it('should handle added and removed items properly when tracking by index', it('should handle added and removed items properly when tracking by index',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = var template =
`<div><template ngFor #item [ngForOf]="items" [ngForTrackBy]="trackByIndex">{{item}}</template></div>`; `<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByIndex">{{item}}</template></div>`;
tcb.overrideTemplate(TestComponent, template) tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent) .createAsync(TestComponent)
.then((fixture) => { .then((fixture) => {

View File

@ -448,7 +448,7 @@ export function main() {
inject([TestComponentBuilder, AsyncTestCompleter], inject([TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async) => { (tcb: TestComponentBuilder, async) => {
var t = `<select> var t = `<select>
<option *ngFor="#city of list" [value]="city['id']"> <option *ngFor="let city of list" [value]="city['id']">
{{ city['name'] }} {{ city['name'] }}
</option> </option>
</select>`; </select>`;
@ -503,7 +503,7 @@ export function main() {
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
var t = `<div [ngFormModel]="form"> var t = `<div [ngFormModel]="form">
<select ngControl="city"> <select ngControl="city">
<option *ngFor="#c of data" [value]="c"></option> <option *ngFor="let c of data" [value]="c"></option>
</select> </select>
</div>`; </div>`;
@ -528,7 +528,7 @@ export function main() {
(tcb: TestComponentBuilder, async) => { (tcb: TestComponentBuilder, async) => {
var t = `<div> var t = `<div>
<select [(ngModel)]="selectedCity"> <select [(ngModel)]="selectedCity">
<option *ngFor="#c of list" [ngValue]="c">{{c['name']}}</option> <option *ngFor="let c of list" [ngValue]="c">{{c['name']}}</option>
</select> </select>
</div>`; </div>`;
@ -560,7 +560,7 @@ export function main() {
(tcb: TestComponentBuilder, async) => { (tcb: TestComponentBuilder, async) => {
var t = `<div> var t = `<div>
<select [(ngModel)]="selectedCity"> <select [(ngModel)]="selectedCity">
<option *ngFor="#c of list" [ngValue]="c">{{c['name']}}</option> <option *ngFor="let c of list" [ngValue]="c">{{c['name']}}</option>
</select> </select>
</div>`; </div>`;
@ -588,7 +588,7 @@ export function main() {
(tcb: TestComponentBuilder, async) => { (tcb: TestComponentBuilder, async) => {
var t = `<div> var t = `<div>
<select [(ngModel)]="selectedCity"> <select [(ngModel)]="selectedCity">
<option *ngFor="#c of list" [ngValue]="c">{{c}}</option> <option *ngFor="let c of list" [ngValue]="c">{{c}}</option>
</select> </select>
</div>`; </div>`;
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => { tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
@ -614,7 +614,7 @@ export function main() {
(tcb: TestComponentBuilder, async) => { (tcb: TestComponentBuilder, async) => {
var t = `<div> var t = `<div>
<select [(ngModel)]="selectedCity"> <select [(ngModel)]="selectedCity">
<option *ngFor="#c of list; trackBy:customTrackBy" [ngValue]="c">{{c}}</option> <option *ngFor="let c of list; trackBy:customTrackBy" [ngValue]="c">{{c}}</option>
</select> </select>
</div>`; </div>`;
@ -644,7 +644,7 @@ export function main() {
(tcb: TestComponentBuilder, async) => { (tcb: TestComponentBuilder, async) => {
var t = `<div> var t = `<div>
<select [(ngModel)]="selectedCity"> <select [(ngModel)]="selectedCity">
<option *ngFor="#c of list" [ngValue]="c">{{c}}</option> <option *ngFor="let c of list" [ngValue]="c">{{c}}</option>
</select> </select>
</div>`; </div>`;
@ -673,7 +673,7 @@ export function main() {
(tcb: TestComponentBuilder, async) => { (tcb: TestComponentBuilder, async) => {
var t = `<div> var t = `<div>
<select [(ngModel)]="selectedCity"> <select [(ngModel)]="selectedCity">
<option *ngFor="#c of list" [ngValue]="c">{{c['name']}}</option> <option *ngFor="let c of list" [ngValue]="c">{{c['name']}}</option>
</select> </select>
</div>`; </div>`;

View File

@ -17,7 +17,7 @@ export function main() {
} }
function parseTemplateBindings(text, location = null): any { function parseTemplateBindings(text, location = null): any {
return createParser().parseTemplateBindings(text, location); return createParser().parseTemplateBindings(text, location).templateBindings;
} }
function parseInterpolation(text, location = null): any { function parseInterpolation(text, location = null): any {
@ -293,7 +293,7 @@ export function main() {
function keyValues(templateBindings: any[]) { function keyValues(templateBindings: any[]) {
return templateBindings.map(binding => { return templateBindings.map(binding => {
if (binding.keyIsVar) { if (binding.keyIsVar) {
return '#' + binding.key + (isBlank(binding.name) ? '=null' : '=' + binding.name); return 'let ' + binding.key + (isBlank(binding.name) ? '=null' : '=' + binding.name);
} else { } else {
return binding.key + (isBlank(binding.expression) ? '' : `=${binding.expression}`) return binding.key + (isBlank(binding.expression) ? '' : `=${binding.expression}`)
} }
@ -339,8 +339,8 @@ export function main() {
}); });
it('should detect names as value', () => { it('should detect names as value', () => {
var bindings = parseTemplateBindings("a:#b"); var bindings = parseTemplateBindings("a:let b");
expect(keyValues(bindings)).toEqual(['a', '#b=\$implicit']); expect(keyValues(bindings)).toEqual(['a', 'let b=\$implicit']);
}); });
it('should allow space and colon as separators', () => { it('should allow space and colon as separators', () => {
@ -370,31 +370,46 @@ export function main() {
expect(bindings[0].expression.location).toEqual('location'); expect(bindings[0].expression.location).toEqual('location');
}); });
it('should support var/# notation', () => { it('should support var notation with a deprecation warning', () => {
var bindings = parseTemplateBindings("var i"); var bindings = createParser().parseTemplateBindings("var i", null);
expect(keyValues(bindings)).toEqual(['#i=\$implicit']); expect(keyValues(bindings.templateBindings)).toEqual(['let i=\$implicit']);
expect(bindings.warnings)
.toEqual(['"var" inside of expressions is deprecated. Use "let" instead!']);
});
bindings = parseTemplateBindings("#i"); it('should support # notation with a deprecation warning', () => {
expect(keyValues(bindings)).toEqual(['#i=\$implicit']); var bindings = createParser().parseTemplateBindings("#i", null);
expect(keyValues(bindings.templateBindings)).toEqual(['let i=\$implicit']);
expect(bindings.warnings)
.toEqual(['"#" inside of expressions is deprecated. Use "let" instead!']);
});
bindings = parseTemplateBindings("var a; var b"); it('should support let notation', () => {
expect(keyValues(bindings)).toEqual(['#a=\$implicit', '#b=\$implicit']); var bindings = parseTemplateBindings("let i");
expect(keyValues(bindings)).toEqual(['let i=\$implicit']);
bindings = parseTemplateBindings("#a; #b;"); bindings = parseTemplateBindings("let i");
expect(keyValues(bindings)).toEqual(['#a=\$implicit', '#b=\$implicit']); expect(keyValues(bindings)).toEqual(['let i=\$implicit']);
bindings = parseTemplateBindings("var i-a = k-a"); bindings = parseTemplateBindings("let a; let b");
expect(keyValues(bindings)).toEqual(['#i-a=k-a']); expect(keyValues(bindings)).toEqual(['let a=\$implicit', 'let b=\$implicit']);
bindings = parseTemplateBindings("keyword var item; var i = k"); bindings = parseTemplateBindings("let a; let b;");
expect(keyValues(bindings)).toEqual(['keyword', '#item=\$implicit', '#i=k']); expect(keyValues(bindings)).toEqual(['let a=\$implicit', 'let b=\$implicit']);
bindings = parseTemplateBindings("keyword: #item; #i = k"); bindings = parseTemplateBindings("let i-a = k-a");
expect(keyValues(bindings)).toEqual(['keyword', '#item=\$implicit', '#i=k']); expect(keyValues(bindings)).toEqual(['let i-a=k-a']);
bindings = parseTemplateBindings("directive: var item in expr; var a = b", 'location'); bindings = parseTemplateBindings("keyword let item; let i = k");
expect(keyValues(bindings)).toEqual(['keyword', 'let item=\$implicit', 'let i=k']);
bindings = parseTemplateBindings("keyword: let item; let i = k");
expect(keyValues(bindings)).toEqual(['keyword', 'let item=\$implicit', 'let i=k']);
bindings = parseTemplateBindings("directive: let item in expr; let a = b", 'location');
expect(keyValues(bindings)) expect(keyValues(bindings))
.toEqual(['directive', '#item=\$implicit', 'directiveIn=expr in location', '#a=b']); .toEqual(
['directive', 'let item=\$implicit', 'directiveIn=expr in location', 'let a=b']);
}); });
it('should parse pipes', () => { it('should parse pipes', () => {

View File

@ -1,5 +1,6 @@
import {print, IS_DART} from 'angular2/src/facade/lang'; import {print, IS_DART} from 'angular2/src/facade/lang';
import {OutputEmitter} from 'angular2/src/compiler/output/abstract_emitter'; import {OutputEmitter} from 'angular2/src/compiler/output/abstract_emitter';
import {Console} from 'angular2/src/core/console';
import { import {
OfflineCompiler, OfflineCompiler,
@ -48,8 +49,8 @@ function _createOfflineCompiler(xhr: MockXHR, emitter: OutputEmitter): OfflineCo
var htmlParser = new HtmlParser(); var htmlParser = new HtmlParser();
var normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser); var normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser);
return new OfflineCompiler( return new OfflineCompiler(
normalizer, normalizer, new TemplateParser(new Parser(new Lexer()), new MockSchemaRegistry({}, {}),
new TemplateParser(new Parser(new Lexer()), new MockSchemaRegistry({}, {}), htmlParser, []), htmlParser, new Console(), []),
new StyleCompiler(urlResolver), new ViewCompiler(new CompilerConfig(true, true, true)), new StyleCompiler(urlResolver), new ViewCompiler(new CompilerConfig(true, true, true)),
emitter); emitter);
} }

View File

@ -11,6 +11,7 @@ import {
beforeEachProviders beforeEachProviders
} from 'angular2/testing_internal'; } from 'angular2/testing_internal';
import {provide} from 'angular2/src/core/di'; import {provide} from 'angular2/src/core/di';
import {Console} from 'angular2/src/core/console';
import {TEST_PROVIDERS} from './test_bindings'; import {TEST_PROVIDERS} from './test_bindings';
import {isPresent, CONST_EXPR} from 'angular2/src/facade/lang'; import {isPresent, CONST_EXPR} from 'angular2/src/facade/lang';
@ -36,6 +37,7 @@ import {
NgContentAst, NgContentAst,
EmbeddedTemplateAst, EmbeddedTemplateAst,
ElementAst, ElementAst,
ReferenceAst,
VariableAst, VariableAst,
BoundEventAst, BoundEventAst,
BoundElementPropertyAst, BoundElementPropertyAst,
@ -47,6 +49,7 @@ import {
DirectiveAst, DirectiveAst,
ProviderAstType ProviderAstType
} from 'angular2/src/compiler/template_ast'; } from 'angular2/src/compiler/template_ast';
import {identifierToken, Identifiers} from 'angular2/src/compiler/identifiers';
import {ElementSchemaRegistry} from 'angular2/src/compiler/schema/element_schema_registry'; import {ElementSchemaRegistry} from 'angular2/src/compiler/schema/element_schema_registry';
import {MockSchemaRegistry} from './schema_registry_mock'; import {MockSchemaRegistry} from './schema_registry_mock';
@ -66,8 +69,13 @@ var MOCK_SCHEMA_REGISTRY = [
export function main() { export function main() {
var ngIf; var ngIf;
var parse; var parse;
var console: ArrayConsole;
function commonBeforeEach() { function commonBeforeEach() {
beforeEachProviders(() => {
console = new ArrayConsole();
return [provide(Console, {useValue: console})];
});
beforeEach(inject([TemplateParser], (parser) => { beforeEach(inject([TemplateParser], (parser) => {
var component = CompileDirectiveMetadata.create({ var component = CompileDirectiveMetadata.create({
selector: 'root', selector: 'root',
@ -310,7 +318,7 @@ export function main() {
}); });
describe('directives', () => { describe('directives', () => {
it('should locate directives components first and ordered by the directives array in the View', it('should order directives by the directives array in the View and match them only once',
() => { () => {
var dirA = CompileDirectiveMetadata.create({ var dirA = CompileDirectiveMetadata.create({
selector: '[a]', selector: '[a]',
@ -324,19 +332,14 @@ export function main() {
selector: '[c]', selector: '[c]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirC'}) type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirC'})
}); });
var comp = CompileDirectiveMetadata.create({ expect(humanizeTplAst(parse('<div a c b a b>', [dirA, dirB, dirC])))
selector: 'div',
isComponent: true,
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'ZComp'}),
template: new CompileTemplateMetadata({ngContentSelectors: []})
});
expect(humanizeTplAst(parse('<div a c b>', [dirA, dirB, dirC, comp])))
.toEqual([ .toEqual([
[ElementAst, 'div'], [ElementAst, 'div'],
[AttrAst, 'a', ''], [AttrAst, 'a', ''],
[AttrAst, 'c', ''], [AttrAst, 'c', ''],
[AttrAst, 'b', ''], [AttrAst, 'b', ''],
[DirectiveAst, comp], [AttrAst, 'a', ''],
[AttrAst, 'b', ''],
[DirectiveAst, dirA], [DirectiveAst, dirA],
[DirectiveAst, dirB], [DirectiveAst, dirB],
[DirectiveAst, dirC] [DirectiveAst, dirC]
@ -747,29 +750,41 @@ export function main() {
}); });
}); });
describe('variables', () => { describe('references', () => {
it('should parse variables via #... and not report them as attributes', () => { it('should parse references via #... and not report them as attributes', () => {
expect(humanizeTplAst(parse('<div #a>', []))) expect(humanizeTplAst(parse('<div #a>', [])))
.toEqual([[ElementAst, 'div'], [VariableAst, 'a', '']]); .toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]);
}); });
it('should parse variables via var-... and not report them as attributes', () => { it('should parse references via ref-... and not report them as attributes', () => {
expect(humanizeTplAst(parse('<div ref-a>', [])))
.toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]);
});
it('should parse references via var-... and report them as deprecated', () => {
expect(humanizeTplAst(parse('<div var-a>', []))) expect(humanizeTplAst(parse('<div var-a>', [])))
.toEqual([[ElementAst, 'div'], [VariableAst, 'a', '']]); .toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]);
expect(console.warnings)
.toEqual([
[
'Template parse warnings:',
'"var-" on non <template> elements is deprecated. Use "ref-" instead! ("<div [ERROR ->]var-a>"): TestComp@0:5'
].join('\n')
]);
}); });
it('should parse camel case variables', () => { it('should parse camel case references', () => {
expect(humanizeTplAst(parse('<div var-someA>', []))) expect(humanizeTplAst(parse('<div ref-someA>', [])))
.toEqual([[ElementAst, 'div'], [VariableAst, 'someA', '']]); .toEqual([[ElementAst, 'div'], [ReferenceAst, 'someA', null]]);
}); });
it('should assign variables with empty value to the element', () => { it('should assign references with empty value to the element', () => {
expect(humanizeTplAst(parse('<div #a></div>', []))) expect(humanizeTplAst(parse('<div #a></div>', [])))
.toEqual([[ElementAst, 'div'], [VariableAst, 'a', '']]); .toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]);
}); });
it('should assign variables to directives via exportAs', () => { it('should assign references to directives via exportAs', () => {
var dirA = CompileDirectiveMetadata.create({ var dirA = CompileDirectiveMetadata.create({
selector: '[a]', selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}), type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
@ -779,28 +794,27 @@ export function main() {
.toEqual([ .toEqual([
[ElementAst, 'div'], [ElementAst, 'div'],
[AttrAst, 'a', ''], [AttrAst, 'a', ''],
[ReferenceAst, 'a', identifierToken(dirA.type)],
[DirectiveAst, dirA], [DirectiveAst, dirA],
[VariableAst, 'a', 'dirA']
]); ]);
}); });
it('should report variables with values that dont match a directive as errors', () => { it('should report references with values that dont match a directive as errors', () => {
expect(() => parse('<div #a="dirA"></div>', [])).toThrowError(`Template parse errors: expect(() => parse('<div #a="dirA"></div>', [])).toThrowError(`Template parse errors:
There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"></div>"): TestComp@0:5`); There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"></div>"): TestComp@0:5`);
}); });
it('should report invalid variable names', () => { it('should report invalid reference names', () => {
expect(() => parse('<div #a-b></div>', [])).toThrowError(`Template parse errors: expect(() => parse('<div #a-b></div>', [])).toThrowError(`Template parse errors:
"-" is not allowed in variable names ("<div [ERROR ->]#a-b></div>"): TestComp@0:5`); "-" is not allowed in reference names ("<div [ERROR ->]#a-b></div>"): TestComp@0:5`);
}); });
it('should allow variables with values that dont match a directive on embedded template elements', it('should report variables as errors', () => {
() => { expect(() => parse('<div let-a></div>', [])).toThrowError(`Template parse errors:
expect(humanizeTplAst(parse('<template #a="b"></template>', []))) "let-" is only supported on template elements. ("<div [ERROR ->]let-a></div>"): TestComp@0:5`);
.toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b']]); });
});
it('should assign variables with empty value to components', () => { it('should assign references with empty value to components', () => {
var dirA = CompileDirectiveMetadata.create({ var dirA = CompileDirectiveMetadata.create({
selector: '[a]', selector: '[a]',
isComponent: true, isComponent: true,
@ -812,12 +826,19 @@ There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"><
.toEqual([ .toEqual([
[ElementAst, 'div'], [ElementAst, 'div'],
[AttrAst, 'a', ''], [AttrAst, 'a', ''],
[VariableAst, 'a', ''], [ReferenceAst, 'a', identifierToken(dirA.type)],
[DirectiveAst, dirA], [DirectiveAst, dirA],
[VariableAst, 'a', '']
]); ]);
}); });
it('should not locate directives in references', () => {
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'})
});
expect(humanizeTplAst(parse('<div ref-a>', [dirA])))
.toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]);
});
}); });
describe('explicit templates', () => { describe('explicit templates', () => {
@ -836,6 +857,49 @@ There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"><
[EmbeddedTemplateAst], [EmbeddedTemplateAst],
]); ]);
}); });
it('should support references via #...', () => {
expect(humanizeTplAst(parse('<template #a>', [])))
.toEqual([
[EmbeddedTemplateAst],
[ReferenceAst, 'a', identifierToken(Identifiers.TemplateRef)]
]);
});
it('should support references via ref-...', () => {
expect(humanizeTplAst(parse('<template ref-a>', [])))
.toEqual([
[EmbeddedTemplateAst],
[ReferenceAst, 'a', identifierToken(Identifiers.TemplateRef)]
]);
});
it('should parse variables via let-...', () => {
expect(humanizeTplAst(parse('<template let-a="b">', [])))
.toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b']]);
});
it('should parse variables via var-... and report them as deprecated', () => {
expect(humanizeTplAst(parse('<template var-a="b">', [])))
.toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b']]);
expect(console.warnings)
.toEqual([
[
'Template parse warnings:',
'"var-" on <template> elements is deprecated. Use "let-" instead! ("<template [ERROR ->]var-a="b">"): TestComp@0:10'
].join('\n')
]);
});
it('should not locate directives in variables', () => {
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'})
});
expect(humanizeTplAst(parse('<template let-a="b"></template>', [dirA])))
.toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b']]);
});
}); });
describe('inline templates', () => { describe('inline templates', () => {
@ -854,13 +918,32 @@ There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"><
]); ]);
}); });
it('should parse variables via #...', () => { it('should parse variables via #... and report them as deprecated', () => {
expect(humanizeTplAst(parse('<div template="ngIf #a=b">', []))) expect(humanizeTplAst(parse('<div *ngIf="#a=b">', [])))
.toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b'], [ElementAst, 'div']]); .toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b'], [ElementAst, 'div']]);
expect(console.warnings)
.toEqual([
[
'Template parse warnings:',
'"#" inside of expressions is deprecated. Use "let" instead! ("<div [ERROR ->]*ngIf="#a=b">"): TestComp@0:5'
].join('\n')
]);
}); });
it('should parse variables via var ...', () => { it('should parse variables via var ... and report them as deprecated', () => {
expect(humanizeTplAst(parse('<div template="ngIf var a=b">', []))) expect(humanizeTplAst(parse('<div *ngIf="var a=b">', [])))
.toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b'], [ElementAst, 'div']]);
expect(console.warnings)
.toEqual([
[
'Template parse warnings:',
'"var" inside of expressions is deprecated. Use "let" instead! ("<div [ERROR ->]*ngIf="var a=b">"): TestComp@0:5'
].join('\n')
]);
});
it('should parse variables via let ...', () => {
expect(humanizeTplAst(parse('<div *ngIf="let a=b">', [])))
.toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b'], [ElementAst, 'div']]); .toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b'], [ElementAst, 'div']]);
}); });
@ -886,24 +969,22 @@ There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"><
]); ]);
}); });
it('should locate directives in variable bindings', () => { it('should not locate directives in variables', () => {
var dirA = CompileDirectiveMetadata.create({ var dirA = CompileDirectiveMetadata.create({
selector: '[a=b]', selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}) type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'})
}); });
var dirB = CompileDirectiveMetadata.create({ expect(humanizeTplAst(parse('<div template="let a=b">', [dirA])))
selector: '[b]', .toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b'], [ElementAst, 'div']]);
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirB'}) });
it('should not locate directives in references', () => {
var dirA = CompileDirectiveMetadata.create({
selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'})
}); });
expect(humanizeTplAst(parse('<div template="#a=b" b>', [dirA, dirB]))) expect(humanizeTplAst(parse('<div ref-a>', [dirA])))
.toEqual([ .toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]);
[EmbeddedTemplateAst],
[VariableAst, 'a', 'b'],
[DirectiveAst, dirA],
[ElementAst, 'div'],
[AttrAst, 'b', ''],
[DirectiveAst, dirB]
]);
}); });
}); });
@ -1248,22 +1329,20 @@ Property binding a not used by any directive on an embedded template ("[ERROR ->
}); });
it('should support references', () => {
expect(humanizeTplAstSourceSpans(parse('<div #a></div>', [])))
.toEqual([[ElementAst, 'div', '<div #a>'], [ReferenceAst, 'a', null, '#a']]);
});
it('should support variables', () => { it('should support variables', () => {
var dirA = CompileDirectiveMetadata.create({ expect(humanizeTplAstSourceSpans(parse('<template let-a="b"></template>', [])))
selector: '[a]',
type: new CompileTypeMetadata({moduleUrl: someModuleUrl, name: 'DirA'}),
exportAs: 'dirA'
});
expect(humanizeTplAstSourceSpans(parse('<div a #a="dirA"></div>', [dirA])))
.toEqual([ .toEqual([
[ElementAst, 'div', '<div a #a="dirA">'], [EmbeddedTemplateAst, '<template let-a="b">'],
[AttrAst, 'a', '', 'a'], [VariableAst, 'a', 'b', 'let-a="b"']
[DirectiveAst, dirA, '<div a #a="dirA">'],
[VariableAst, 'a', 'dirA', '#a="dirA"']
]); ]);
}); });
it('should support event', () => { it('should support events', () => {
expect(humanizeTplAstSourceSpans(parse('<div (window:event)="v">', []))) expect(humanizeTplAstSourceSpans(parse('<div (window:event)="v">', [])))
.toEqual([ .toEqual([
[ElementAst, 'div', '<div (window:event)="v">'], [ElementAst, 'div', '<div (window:event)="v">'],
@ -1311,8 +1390,8 @@ Property binding a not used by any directive on an embedded template ("[ERROR ->
.toEqual([ .toEqual([
[ElementAst, 'div', '<div a>'], [ElementAst, 'div', '<div a>'],
[AttrAst, 'a', '', 'a'], [AttrAst, 'a', '', 'a'],
[DirectiveAst, comp, '<div a>'],
[DirectiveAst, dirA, '<div a>'], [DirectiveAst, dirA, '<div a>'],
[DirectiveAst, comp, '<div a>']
]); ]);
}); });
@ -1399,7 +1478,8 @@ class TemplateHumanizer implements TemplateAstVisitor {
this.result.push(this._appendContext(ast, res)); this.result.push(this._appendContext(ast, res));
templateVisitAll(this, ast.attrs); templateVisitAll(this, ast.attrs);
templateVisitAll(this, ast.outputs); templateVisitAll(this, ast.outputs);
templateVisitAll(this, ast.vars); templateVisitAll(this, ast.references);
templateVisitAll(this, ast.variables);
templateVisitAll(this, ast.directives); templateVisitAll(this, ast.directives);
templateVisitAll(this, ast.children); templateVisitAll(this, ast.children);
return null; return null;
@ -1410,11 +1490,16 @@ class TemplateHumanizer implements TemplateAstVisitor {
templateVisitAll(this, ast.attrs); templateVisitAll(this, ast.attrs);
templateVisitAll(this, ast.inputs); templateVisitAll(this, ast.inputs);
templateVisitAll(this, ast.outputs); templateVisitAll(this, ast.outputs);
templateVisitAll(this, ast.exportAsVars); templateVisitAll(this, ast.references);
templateVisitAll(this, ast.directives); templateVisitAll(this, ast.directives);
templateVisitAll(this, ast.children); templateVisitAll(this, ast.children);
return null; return null;
} }
visitReference(ast: ReferenceAst, context: any): any {
var res = [ReferenceAst, ast.name, ast.value];
this.result.push(this._appendContext(ast, res));
return null;
}
visitVariable(ast: VariableAst, context: any): any { visitVariable(ast: VariableAst, context: any): any {
var res = [VariableAst, ast.name, ast.value]; var res = [VariableAst, ast.name, ast.value];
this.result.push(this._appendContext(ast, res)); this.result.push(this._appendContext(ast, res));
@ -1457,7 +1542,6 @@ class TemplateHumanizer implements TemplateAstVisitor {
templateVisitAll(this, ast.inputs); templateVisitAll(this, ast.inputs);
templateVisitAll(this, ast.hostProperties); templateVisitAll(this, ast.hostProperties);
templateVisitAll(this, ast.hostEvents); templateVisitAll(this, ast.hostEvents);
templateVisitAll(this, ast.exportAsVars);
return null; return null;
} }
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {
@ -1499,6 +1583,7 @@ class TemplateContentProjectionHumanizer implements TemplateAstVisitor {
templateVisitAll(this, ast.children); templateVisitAll(this, ast.children);
return null; return null;
} }
visitReference(ast: ReferenceAst, context: any): any { return null; }
visitVariable(ast: VariableAst, context: any): any { return null; } visitVariable(ast: VariableAst, context: any): any { return null; }
visitEvent(ast: BoundEventAst, context: any): any { return null; } visitEvent(ast: BoundEventAst, context: any): any { return null; }
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; } visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
@ -1523,6 +1608,7 @@ class FooAstTransformer implements TemplateAstVisitor {
return new ElementAst('foo', [], [], [], [], [], [], false, [], ast.ngContentIndex, return new ElementAst('foo', [], [], [], [], [], [], false, [], ast.ngContentIndex,
ast.sourceSpan); ast.sourceSpan);
} }
visitReference(ast: ReferenceAst, context: any): any { throw 'not implemented'; }
visitVariable(ast: VariableAst, context: any): any { throw 'not implemented'; } visitVariable(ast: VariableAst, context: any): any { throw 'not implemented'; }
visitEvent(ast: BoundEventAst, context: any): any { throw 'not implemented'; } visitEvent(ast: BoundEventAst, context: any): any { throw 'not implemented'; }
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { throw 'not implemented'; } visitElementProperty(ast: BoundElementPropertyAst, context: any): any { throw 'not implemented'; }
@ -1542,3 +1628,10 @@ class BarAstTransformer extends FooAstTransformer {
ast.sourceSpan); ast.sourceSpan);
} }
} }
class ArrayConsole implements Console {
logs: string[] = [];
warnings: string[] = [];
log(msg: string) { this.logs.push(msg); }
warn(msg: string) { this.warnings.push(msg); }
}

View File

@ -179,4 +179,5 @@ class _MockComponentRef extends ComponentRef_ {
class _MockConsole implements Console { class _MockConsole implements Console {
log(message) {} log(message) {}
warn(message) {}
} }

View File

@ -132,9 +132,9 @@ class ConditionalParentComp {
@Component({ @Component({
selector: 'using-for', selector: 'using-for',
viewProviders: [Logger], viewProviders: [Logger],
template: `<span *ngFor="#thing of stuff" [innerHtml]="thing"></span> template: `<span *ngFor="let thing of stuff" [innerHtml]="thing"></span>
<ul message="list"> <ul message="list">
<li *ngFor="#item of stuff" [innerHtml]="item"></li> <li *ngFor="let item of stuff" [innerHtml]="item"></li>
</ul>`, </ul>`,
directives: [NgFor, MessageDir], directives: [NgFor, MessageDir],
}) })

View File

@ -50,7 +50,7 @@ class App {
@Component({ @Component({
selector: 'lock', selector: 'lock',
directives: [NgFor], directives: [NgFor],
template: `{{frame.name}}(<span *ngFor="var lock of locks">{{lock.name}}</span>)`, template: `{{frame.name}}(<span *ngFor="let lock of locks">{{lock.name}}</span>)`,
}) })
class Door { class Door {
locks: QueryList<Lock>; locks: QueryList<Lock>;

View File

@ -438,7 +438,7 @@ export function main() {
it('should read locals', fakeAsync(() => { it('should read locals', fakeAsync(() => {
var ctx = var ctx =
createCompFixture('<template testLocals var-local="someLocal">{{local}}</template>'); createCompFixture('<template testLocals let-local="someLocal">{{local}}</template>');
ctx.detectChanges(false); ctx.detectChanges(false);
expect(renderLog.log).toEqual(['{{someLocalValue}}']); expect(renderLog.log).toEqual(['{{someLocalValue}}']);
@ -581,7 +581,7 @@ export function main() {
it('should throw when trying to assign to a local', fakeAsync(() => { it('should throw when trying to assign to a local', fakeAsync(() => {
expect(() => {_bindSimpleProp('(event)="$event=1"')}) expect(() => {_bindSimpleProp('(event)="$event=1"')})
.toThrowError(new RegExp("Cannot reassign a variable binding")); .toThrowError(new RegExp("Cannot assign to a reference or variable!"));
})); }));
it('should support short-circuiting', fakeAsync(() => { it('should support short-circuiting', fakeAsync(() => {
@ -609,7 +609,7 @@ export function main() {
it('should read directive properties', fakeAsync(() => { it('should read directive properties', fakeAsync(() => {
var ctx = var ctx =
createCompFixture( createCompFixture(
'<div testDirective [a]="42" var-dir="testDirective" [someProp]="dir.a"></div>') '<div testDirective [a]="42" ref-dir="testDirective" [someProp]="dir.a"></div>')
ctx.detectChanges(false); ctx.detectChanges(false);
expect(renderLog.loggedValues).toEqual([42]); expect(renderLog.loggedValues).toEqual([42]);
})); }));

View File

@ -259,7 +259,7 @@ class OnChangeComponent implements OnChanges {
]) ])
@View( @View(
template: template:
'<span *ngFor="#item of list">{{item}}</span><directive-logging-checks></directive-logging-checks>', '<span *ngFor="let item of list">{{item}}</span><directive-logging-checks></directive-logging-checks>',
directives: const [NgFor, DirectiveLoggingChecks]) directives: const [NgFor, DirectiveLoggingChecks])
class ComponentWithObservableList { class ComponentWithObservableList {
Iterable list; Iterable list;

View File

@ -89,7 +89,7 @@ import {EmbeddedViewRef} from 'angular2/src/core/linker/view_ref';
import {ComponentResolver} from 'angular2/src/core/linker/component_resolver'; import {ComponentResolver} from 'angular2/src/core/linker/component_resolver';
import {ElementRef} from 'angular2/src/core/linker/element_ref'; import {ElementRef} from 'angular2/src/core/linker/element_ref';
import {TemplateRef} from 'angular2/src/core/linker/template_ref'; import {TemplateRef_, TemplateRef} from 'angular2/src/core/linker/template_ref';
import {Renderer} from 'angular2/src/core/render'; import {Renderer} from 'angular2/src/core/render';
@ -473,7 +473,7 @@ function declareTests(isJit: boolean) {
tcb.overrideView( tcb.overrideView(
MyComp, new ViewMetadata({ MyComp, new ViewMetadata({
template: template:
'<template some-viewport var-greeting="some-tmpl"><copy-me>{{greeting}}</copy-me></template>', '<template some-viewport let-greeting="some-tmpl"><copy-me>{{greeting}}</copy-me></template>',
directives: [SomeViewport] directives: [SomeViewport]
})) }))
@ -509,7 +509,7 @@ function declareTests(isJit: boolean) {
tcb.overrideView( tcb.overrideView(
MyComp, new ViewMetadata({ MyComp, new ViewMetadata({
template: template:
'<copy-me template="some-viewport: var greeting=some-tmpl">{{greeting}}</copy-me>', '<copy-me template="some-viewport: let greeting=some-tmpl">{{greeting}}</copy-me>',
directives: [SomeViewport] directives: [SomeViewport]
})) }))
@ -531,7 +531,7 @@ function declareTests(isJit: boolean) {
tcb.overrideView( tcb.overrideView(
MyComp, new ViewMetadata({ MyComp, new ViewMetadata({
template: template:
'<some-directive><toolbar><template toolbarpart var-toolbarProp="toolbarProp">{{ctxProp}},{{toolbarProp}},<cmp-with-host></cmp-with-host></template></toolbar></some-directive>', '<some-directive><toolbar><template toolbarpart let-toolbarProp="toolbarProp">{{ctxProp}},{{toolbarProp}},<cmp-with-host></cmp-with-host></template></toolbar></some-directive>',
directives: [SomeDirective, CompWithHost, ToolbarComponent, ToolbarPart] directives: [SomeDirective, CompWithHost, ToolbarComponent, ToolbarPart]
})) }))
.createAsync(MyComp) .createAsync(MyComp)
@ -547,12 +547,12 @@ function declareTests(isJit: boolean) {
}); });
})); }));
describe("variable bindings", () => { describe("reference bindings", () => {
it('should assign a component to a var-', it('should assign a component to a ref-',
inject([TestComponentBuilder, AsyncTestCompleter], inject([TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async) => { (tcb: TestComponentBuilder, async) => {
tcb.overrideView(MyComp, new ViewMetadata({ tcb.overrideView(MyComp, new ViewMetadata({
template: '<p><child-cmp var-alice></child-cmp></p>', template: '<p><child-cmp ref-alice></child-cmp></p>',
directives: [ChildComp] directives: [ChildComp]
})) }))
@ -564,7 +564,7 @@ function declareTests(isJit: boolean) {
async.done(); async.done();
})})); })}));
it('should assign a directive to a var-', it('should assign a directive to a ref-',
inject( inject(
[TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { [TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb.overrideView(MyComp, new ViewMetadata({ tcb.overrideView(MyComp, new ViewMetadata({
@ -588,7 +588,7 @@ function declareTests(isJit: boolean) {
tcb.overrideView( tcb.overrideView(
MyComp, new ViewMetadata({ MyComp, new ViewMetadata({
template: template:
'<template [ngIf]="true">{{alice.ctxProp}}</template>|{{alice.ctxProp}}|<child-cmp var-alice></child-cmp>', '<template [ngIf]="true">{{alice.ctxProp}}</template>|{{alice.ctxProp}}|<child-cmp ref-alice></child-cmp>',
directives: [ChildComp, NgIf] directives: [ChildComp, NgIf]
})) }))
@ -600,14 +600,14 @@ function declareTests(isJit: boolean) {
async.done(); async.done();
})})); })}));
it('should assign two component instances each with a var-', it('should assign two component instances each with a ref-',
inject( inject(
[TestComponentBuilder, AsyncTestCompleter], [TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async) => { (tcb: TestComponentBuilder, async) => {
tcb.overrideView( tcb.overrideView(
MyComp, new ViewMetadata({ MyComp, new ViewMetadata({
template: template:
'<p><child-cmp var-alice></child-cmp><child-cmp var-bob></child-cmp></p>', '<p><child-cmp ref-alice></child-cmp><child-cmp ref-bob></child-cmp></p>',
directives: [ChildComp] directives: [ChildComp]
})) }))
@ -622,7 +622,7 @@ function declareTests(isJit: boolean) {
async.done(); async.done();
})})); })}));
it('should assign the component instance to a var- with shorthand syntax', it('should assign the component instance to a ref- with shorthand syntax',
inject([TestComponentBuilder, AsyncTestCompleter], inject([TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, (tcb: TestComponentBuilder,
async) => {tcb.overrideView(MyComp, new ViewMetadata({ async) => {tcb.overrideView(MyComp, new ViewMetadata({
@ -643,7 +643,7 @@ function declareTests(isJit: boolean) {
inject([TestComponentBuilder, AsyncTestCompleter], inject([TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async) => { (tcb: TestComponentBuilder, async) => {
tcb.overrideView(MyComp, new ViewMetadata({ tcb.overrideView(MyComp, new ViewMetadata({
template: '<div><div var-alice><i>Hello</i></div></div>' template: '<div><div ref-alice><i>Hello</i></div></div>'
})) }))
.createAsync(MyComp) .createAsync(MyComp)
@ -657,11 +657,26 @@ function declareTests(isJit: boolean) {
async.done(); async.done();
})})); })}));
it('should assign the TemplateRef to a user-defined variable',
inject([TestComponentBuilder, AsyncTestCompleter],
(tcb: TestComponentBuilder, async) => {
tcb.overrideView(MyComp, new ViewMetadata(
{template: '<template ref-alice></template>'}))
.createAsync(MyComp)
.then((fixture) => {
var value = fixture.debugElement.childNodes[0].getLocal('alice');
expect(value).toBeAnInstanceOf(TemplateRef_);
async.done();
})}));
it('should preserve case', it('should preserve case',
inject( inject(
[TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { [TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb.overrideView(MyComp, new ViewMetadata({ tcb.overrideView(MyComp, new ViewMetadata({
template: '<p><child-cmp var-superAlice></child-cmp></p>', template: '<p><child-cmp ref-superAlice></child-cmp></p>',
directives: [ChildComp] directives: [ChildComp]
})) }))
@ -673,14 +688,16 @@ function declareTests(isJit: boolean) {
async.done(); async.done();
}); });
})); }));
});
describe('variables', () => {
it('should allow to use variables in a for loop', it('should allow to use variables in a for loop',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder,
async) => { async) => {
tcb.overrideView( tcb.overrideView(
MyComp, new ViewMetadata({ MyComp, new ViewMetadata({
template: template:
'<template ngFor [ngForOf]="[1]" var-i><child-cmp-no-template #cmp></child-cmp-no-template>{{i}}-{{cmp.ctxProp}}</template>', '<template ngFor [ngForOf]="[1]" let-i><child-cmp-no-template #cmp></child-cmp-no-template>{{i}}-{{cmp.ctxProp}}</template>',
directives: [ChildCompNoTemplate, NgFor] directives: [ChildCompNoTemplate, NgFor]
})) }))
@ -2281,7 +2298,7 @@ class ToolbarViewContainer {
@Component({ @Component({
selector: 'toolbar', selector: 'toolbar',
template: 'TOOLBAR(<div *ngFor="var part of query" [toolbarVc]="part"></div>)', template: 'TOOLBAR(<div *ngFor="let part of query" [toolbarVc]="part"></div>)',
directives: [ToolbarViewContainer, NgFor] directives: [ToolbarViewContainer, NgFor]
}) })
@Injectable() @Injectable()
@ -2489,7 +2506,7 @@ class DirectiveThrowingAnError {
@Component({ @Component({
selector: 'component-with-template', selector: 'component-with-template',
directives: [NgFor], directives: [NgFor],
template: `No View Decorator: <div *ngFor="#item of items">{{item}}</div>` template: `No View Decorator: <div *ngFor="let item of items">{{item}}</div>`
}) })
class ComponentWithTemplate { class ComponentWithTemplate {
items = [1, 2, 3]; items = [1, 2, 3];

View File

@ -246,7 +246,7 @@ export function main() {
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = var template =
'<div text="1"></div>' + '<div text="1"></div>' +
'<needs-query text="2"><div *ngFor="var i of list" [text]="i"></div></needs-query>' + '<needs-query text="2"><div *ngFor="let i of list" [text]="i"></div></needs-query>' +
'<div text="4"></div>'; '<div text="4"></div>';
tcb.overrideTemplate(MyComp, template) tcb.overrideTemplate(MyComp, template)
@ -268,7 +268,7 @@ export function main() {
describe('query for TemplateRef', () => { describe('query for TemplateRef', () => {
it('should find TemplateRefs in the light and shadow dom', it('should find TemplateRefs in the light and shadow dom',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = '<needs-tpl><template var-x="light"></template></needs-tpl>'; var template = '<needs-tpl><template let-x="light"></template></needs-tpl>';
tcb.overrideTemplate(MyComp, template) tcb.overrideTemplate(MyComp, template)
.createAsync(MyComp) .createAsync(MyComp)
.then((view) => { .then((view) => {
@ -284,6 +284,23 @@ export function main() {
}); });
})); }));
it('should find named TemplateRefs',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template =
'<needs-named-tpl><template let-x="light" #tpl></template></needs-named-tpl>';
tcb.overrideTemplate(MyComp, template)
.createAsync(MyComp)
.then((view) => {
view.detectChanges();
var needsTpl: NeedsNamedTpl = view.debugElement.children[0].inject(NeedsNamedTpl);
expect(needsTpl.vc.createEmbeddedView(needsTpl.contentTpl).hasLocal('light'))
.toBe(true);
expect(needsTpl.vc.createEmbeddedView(needsTpl.viewTpl).hasLocal('shadow'))
.toBe(true);
async.done();
});
}));
}); });
describe('read a different token', () => { describe('read a different token', () => {
@ -462,9 +479,10 @@ export function main() {
describe("querying by var binding", () => { describe("querying by var binding", () => {
it('should contain all the child directives in the light dom with the given var binding', it('should contain all the child directives in the light dom with the given var binding',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = '<needs-query-by-var-binding #q>' + var template =
'<div *ngFor="#item of list" [text]="item" #textLabel="textDir"></div>' + '<needs-query-by-ref-binding #q>' +
'</needs-query-by-var-binding>'; '<div *ngFor="let item of list" [text]="item" #textLabel="textDir"></div>' +
'</needs-query-by-ref-binding>';
tcb.overrideTemplate(MyComp, template) tcb.overrideTemplate(MyComp, template)
.createAsync(MyComp) .createAsync(MyComp)
@ -484,10 +502,10 @@ export function main() {
it('should support querying by multiple var bindings', it('should support querying by multiple var bindings',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = '<needs-query-by-var-bindings #q>' + var template = '<needs-query-by-ref-bindings #q>' +
'<div text="one" #textLabel1="textDir"></div>' + '<div text="one" #textLabel1="textDir"></div>' +
'<div text="two" #textLabel2="textDir"></div>' + '<div text="two" #textLabel2="textDir"></div>' +
'</needs-query-by-var-bindings>'; '</needs-query-by-ref-bindings>';
tcb.overrideTemplate(MyComp, template) tcb.overrideTemplate(MyComp, template)
.createAsync(MyComp) .createAsync(MyComp)
@ -504,9 +522,10 @@ export function main() {
it('should support dynamically inserted directives', it('should support dynamically inserted directives',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = '<needs-query-by-var-binding #q>' + var template =
'<div *ngFor="#item of list" [text]="item" #textLabel="textDir"></div>' + '<needs-query-by-ref-binding #q>' +
'</needs-query-by-var-binding>'; '<div *ngFor="let item of list" [text]="item" #textLabel="textDir"></div>' +
'</needs-query-by-ref-binding>';
tcb.overrideTemplate(MyComp, template) tcb.overrideTemplate(MyComp, template)
.createAsync(MyComp) .createAsync(MyComp)
@ -529,11 +548,11 @@ export function main() {
it('should contain all the elements in the light dom with the given var binding', it('should contain all the elements in the light dom with the given var binding',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = '<needs-query-by-var-binding #q>' + var template = '<needs-query-by-ref-binding #q>' +
'<div template="ngFor: #item of list">' + '<div template="ngFor: let item of list">' +
'<div #textLabel>{{item}}</div>' + '<div #textLabel>{{item}}</div>' +
'</div>' + '</div>' +
'</needs-query-by-var-binding>'; '</needs-query-by-ref-binding>';
tcb.overrideTemplate(MyComp, template) tcb.overrideTemplate(MyComp, template)
.createAsync(MyComp) .createAsync(MyComp)
@ -570,7 +589,7 @@ export function main() {
it('should support querying the view by using a view query', it('should support querying the view by using a view query',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = '<needs-view-query-by-var-binding #q></needs-view-query-by-var-binding>'; var template = '<needs-view-query-by-ref-binding #q></needs-view-query-by-ref-binding>';
tcb.overrideTemplate(MyComp, template) tcb.overrideTemplate(MyComp, template)
.createAsync(MyComp) .createAsync(MyComp)
@ -859,7 +878,7 @@ class InertDirective {
@Component({ @Component({
selector: 'needs-query', selector: 'needs-query',
directives: [NgFor, TextDirective], directives: [NgFor, TextDirective],
template: '<div text="ignoreme"></div><b *ngFor="var dir of query">{{dir.text}}|</b>' template: '<div text="ignoreme"></div><b *ngFor="let dir of query">{{dir.text}}|</b>'
}) })
@Injectable() @Injectable()
class NeedsQuery { class NeedsQuery {
@ -878,7 +897,7 @@ class NeedsFourQueries {
@Component({ @Component({
selector: 'needs-query-desc', selector: 'needs-query-desc',
directives: [NgFor], directives: [NgFor],
template: '<div *ngFor="var dir of query">{{dir.text}}|</div>' template: '<div *ngFor="let dir of query">{{dir.text}}|</div>'
}) })
@Injectable() @Injectable()
class NeedsQueryDesc { class NeedsQueryDesc {
@ -888,7 +907,7 @@ class NeedsQueryDesc {
} }
} }
@Component({selector: 'needs-query-by-var-binding', directives: [], template: '<ng-content>'}) @Component({selector: 'needs-query-by-ref-binding', directives: [], template: '<ng-content>'})
@Injectable() @Injectable()
class NeedsQueryByLabel { class NeedsQueryByLabel {
query: QueryList<any>; query: QueryList<any>;
@ -898,7 +917,7 @@ class NeedsQueryByLabel {
} }
@Component({ @Component({
selector: 'needs-view-query-by-var-binding', selector: 'needs-view-query-by-ref-binding',
directives: [], directives: [],
template: '<div #textLabel>text</div>' template: '<div #textLabel>text</div>'
}) })
@ -908,7 +927,7 @@ class NeedsViewQueryByLabel {
constructor(@ViewQuery("textLabel") query: QueryList<any>) { this.query = query; } constructor(@ViewQuery("textLabel") query: QueryList<any>) { this.query = query; }
} }
@Component({selector: 'needs-query-by-var-bindings', directives: [], template: '<ng-content>'}) @Component({selector: 'needs-query-by-ref-bindings', directives: [], template: '<ng-content>'})
@Injectable() @Injectable()
class NeedsQueryByTwoLabels { class NeedsQueryByTwoLabels {
query: QueryList<any>; query: QueryList<any>;
@ -920,7 +939,7 @@ class NeedsQueryByTwoLabels {
@Component({ @Component({
selector: 'needs-query-and-project', selector: 'needs-query-and-project',
directives: [NgFor], directives: [NgFor],
template: '<div *ngFor="var dir of query">{{dir.text}}|</div><ng-content></ng-content>' template: '<div *ngFor="let dir of query">{{dir.text}}|</div><ng-content></ng-content>'
}) })
@Injectable() @Injectable()
class NeedsQueryAndProject { class NeedsQueryAndProject {
@ -975,7 +994,7 @@ class NeedsViewQueryNestedIf {
selector: 'needs-view-query-order', selector: 'needs-view-query-order',
directives: [NgFor, TextDirective, InertDirective], directives: [NgFor, TextDirective, InertDirective],
template: '<div text="1"></div>' + template: '<div text="1"></div>' +
'<div *ngFor="var i of list" [text]="i"></div>' + '<div *ngFor="let i of list" [text]="i"></div>' +
'<div text="4"></div>' '<div text="4"></div>'
}) })
@Injectable() @Injectable()
@ -992,7 +1011,7 @@ class NeedsViewQueryOrder {
selector: 'needs-view-query-order-with-p', selector: 'needs-view-query-order-with-p',
directives: [NgFor, TextDirective, InertDirective], directives: [NgFor, TextDirective, InertDirective],
template: '<div dir><div text="1"></div>' + template: '<div dir><div text="1"></div>' +
'<div *ngFor="var i of list" [text]="i"></div>' + '<div *ngFor="let i of list" [text]="i"></div>' +
'<div text="4"></div></div>' '<div text="4"></div></div>'
}) })
@Injectable() @Injectable()
@ -1005,7 +1024,7 @@ class NeedsViewQueryOrderWithParent {
} }
} }
@Component({selector: 'needs-tpl', template: '<template var-x="shadow"></template>'}) @Component({selector: 'needs-tpl', template: '<template let-x="shadow"></template>'})
class NeedsTpl { class NeedsTpl {
viewQuery: QueryList<TemplateRef>; viewQuery: QueryList<TemplateRef>;
query: QueryList<TemplateRef>; query: QueryList<TemplateRef>;
@ -1016,6 +1035,13 @@ class NeedsTpl {
} }
} }
@Component({selector: 'needs-named-tpl', template: '<template #tpl let-x="shadow"></template>'})
class NeedsNamedTpl {
@ViewChild('tpl') viewTpl: TemplateRef;
@ContentChild('tpl') contentTpl: TemplateRef;
constructor(public vc: ViewContainerRef) {}
}
@Component({selector: 'needs-content-children-read', template: ''}) @Component({selector: 'needs-content-children-read', template: ''})
class NeedsContentChildrenWithRead { class NeedsContentChildrenWithRead {
@ContentChildren('q', {read: TextDirective}) textDirChildren: QueryList<TextDirective>; @ContentChildren('q', {read: TextDirective}) textDirChildren: QueryList<TextDirective>;
@ -1076,6 +1102,7 @@ class NeedsViewContainerWithRead {
NeedsViewChild, NeedsViewChild,
NeedsContentChild, NeedsContentChild,
NeedsTpl, NeedsTpl,
NeedsNamedTpl,
TextDirective, TextDirective,
InertDirective, InertDirective,
NgIf, NgIf,
@ -1097,4 +1124,4 @@ class MyComp {
this.shouldShow = false; this.shouldShow = false;
this.list = ['1d', '2d', '3d']; this.list = ['1d', '2d', '3d'];
} }
} }

View File

@ -92,6 +92,7 @@ class _ArrayLogger {
class DummyConsole implements Console { class DummyConsole implements Console {
log(message) {} log(message) {}
warn(message) {}
} }
export function main() { export function main() {

View File

@ -116,6 +116,7 @@ var NG_COMPILER = [
"TEMPLATE_TRANSFORMS", "TEMPLATE_TRANSFORMS",
"TextAst", "TextAst",
"VariableAst", "VariableAst",
"ReferenceAst",
"XHR", "XHR",
"templateVisitAll", "templateVisitAll",
"CompileDiDependencyMetadata", "CompileDiDependencyMetadata",

View File

@ -43,6 +43,7 @@ import {MockApplicationRef} from 'angular2/src/mock/mock_application_ref';
class DummyConsole implements Console { class DummyConsole implements Console {
log(message) {} log(message) {}
warn(message) {}
} }
export function main() { export function main() {

View File

@ -35,6 +35,7 @@ class _ArrayLogger {
class DummyConsole implements Console { class DummyConsole implements Console {
log(message) {} log(message) {}
warn(message) {}
} }
export function main() { export function main() {

View File

@ -63,15 +63,15 @@ class DynamicDummy {
directives: [NgIf, NgFor, DummyComponent, DummyDirective, DynamicDummy], directives: [NgIf, NgFor, DummyComponent, DummyDirective, DynamicDummy],
template: ` template: `
<div *ngIf="testingPlainComponents"> <div *ngIf="testingPlainComponents">
<dummy *ngFor="#i of list"></dummy> <dummy *ngFor="let i of list"></dummy>
</div> </div>
<div *ngIf="testingWithDirectives"> <div *ngIf="testingWithDirectives">
<dummy dummy-decorator *ngFor="#i of list"></dummy> <dummy dummy-decorator *ngFor="let i of list"></dummy>
</div> </div>
<div *ngIf="testingDynamicComponents"> <div *ngIf="testingDynamicComponents">
<dynamic-dummy *ngFor="#i of list"></dynamic-dummy> <dynamic-dummy *ngFor="let i of list"></dynamic-dummy>
</div> </div>
` `
}) })

View File

@ -214,22 +214,22 @@ class CellData {
template: ` template: `
<table [ngSwitch]="benchmarkType"> <table [ngSwitch]="benchmarkType">
<tbody template="ngSwitchWhen 'interpolation'"> <tbody template="ngSwitchWhen 'interpolation'">
<tr template="ngFor #row of data"> <tr template="ngFor let row of data">
<td template="ngFor #column of row"> <td template="ngFor let column of row">
{{column.i}}:{{column.j}}| {{column.i}}:{{column.j}}|
</td> </td>
</tr> </tr>
</tbody> </tbody>
<tbody template="ngSwitchWhen 'interpolationAttr'"> <tbody template="ngSwitchWhen 'interpolationAttr'">
<tr template="ngFor #row of data"> <tr template="ngFor let row of data">
<td template="ngFor #column of row" attr.i="{{column.i}}" attr.j="{{column.j}}"> <td template="ngFor let column of row" attr.i="{{column.i}}" attr.j="{{column.j}}">
i,j attrs i,j attrs
</td> </td>
</tr> </tr>
</tbody> </tbody>
<tbody template="ngSwitchWhen 'interpolationFn'"> <tbody template="ngSwitchWhen 'interpolationFn'">
<tr template="ngFor #row of data"> <tr template="ngFor let row of data">
<td template="ngFor #column of row"> <td template="ngFor let column of row">
{{column.iFn()}}:{{column.jFn()}}| {{column.iFn()}}:{{column.jFn()}}|
</td> </td>
</tr> </tr>

View File

@ -19,7 +19,7 @@ import {Component, Directive} from 'angular2/core';
</div> </div>
<div template="ngIf scrollAreas.length > 0"> <div template="ngIf scrollAreas.length > 0">
<p>Following tables are only here to add weight to the UI:</p> <p>Following tables are only here to add weight to the UI:</p>
<scroll-area template="ngFor #scrollArea of scrollAreas"></scroll-area> <scroll-area template="ngFor let scrollArea of scrollAreas"></scroll-area>
</div> </div>
</div>` </div>`
}) })

View File

@ -55,7 +55,7 @@ export class Stage {
directives: [NgFor], directives: [NgFor],
template: ` template: `
<div [style.width.px]="cellWidth"> <div [style.width.px]="cellWidth">
<button template="ngFor #stage of stages" <button template="ngFor let stage of stages"
[disabled]="stage.isDisabled" [disabled]="stage.isDisabled"
[style.background-color]="stage.backgroundColor" [style.background-color]="stage.backgroundColor"
on-click="setStage(stage)"> on-click="setStage(stage)">

View File

@ -28,7 +28,7 @@ import {NgFor} from 'angular2/common';
<div id="padding"></div> <div id="padding"></div>
<div id="inner"> <div id="inner">
<scroll-item <scroll-item
template="ngFor #item of visibleItems" template="ngFor let item of visibleItems"
[offering]="item"> [offering]="item">
</scroll-item> </scroll-item>
</div> </div>

View File

@ -7,7 +7,7 @@ import 'rxjs/add/operator/map';
template: ` template: `
<h1>people</h1> <h1>people</h1>
<ul class="people"> <ul class="people">
<li *ngFor="#person of people"> <li *ngFor="let person of people">
hello, {{person['name']}} hello, {{person['name']}}
</li> </li>
</ul> </ul>

View File

@ -7,7 +7,7 @@ import {ObservableWrapper} from 'angular2/src/facade/async';
template: ` template: `
<h1>people</h1> <h1>people</h1>
<ul class="people"> <ul class="people">
<li *ngFor="#person of people"> <li *ngFor="let person of people">
hello, {{person['name']}} hello, {{person['name']}}
</li> </li>
</ul> </ul>

View File

@ -102,7 +102,7 @@ class ShowError {
<p> <p>
<label for="country">Country</label> <label for="country">Country</label>
<select id="country" ngControl="country"> <select id="country" ngControl="country">
<option *ngFor="#c of countries" [value]="c">{{c}}</option> <option *ngFor="let c of countries" [value]="c">{{c}}</option>
</select> </select>
</p> </p>

View File

@ -82,7 +82,7 @@ class DataService {
selector: 'order-list-cmp', selector: 'order-list-cmp',
template: ` template: `
<h1>Orders</h1> <h1>Orders</h1>
<div *ngFor="#order of orders" [class.warning]="order.total > order.limit"> <div *ngFor="let order of orders" [class.warning]="order.total > order.limit">
<div> <div>
<label>Customer name:</label> <label>Customer name:</label>
{{order.customerName}} {{order.customerName}}
@ -173,7 +173,7 @@ class OrderItemComponent {
<h2>Items</h2> <h2>Items</h2>
<button (click)="addItem()">Add Item</button> <button (click)="addItem()">Add Item</button>
<order-item-cmp *ngFor="#item of order.items" [item]="item" (delete)="deleteItem(item)"></order-item-cmp> <order-item-cmp *ngFor="let item of order.items" [item]="item" (delete)="deleteItem(item)"></order-item-cmp>
</div> </div>
`, `,
directives: [FORM_DIRECTIVES, OrderItemComponent, NgFor, NgIf] directives: [FORM_DIRECTIVES, OrderItemComponent, NgFor, NgIf]

View File

@ -161,7 +161,7 @@ class PersonsDetailComponent {
<h1>FullName Demo</h1> <h1>FullName Demo</h1>
<div> <div>
<ul> <ul>
<li *ngFor="#person of persons"> <li *ngFor="let person of persons">
<label (click)="select(person)">{{person.fullName}}</label> <label (click)="select(person)">{{person.fullName}}</label>
</li> </li>
</ul> </ul>

View File

@ -2,7 +2,7 @@
<h2 class="page-title">Drafts</h2> <h2 class="page-title">Drafts</h2>
<ol class="inbox-list"> <ol class="inbox-list">
<li *ngFor="#item of items" class="inbox-item-record"> <li *ngFor="let item of items" class="inbox-item-record">
<a id="item-{{ item.id }}" <a id="item-{{ item.id }}"
[routerLink]="['/DetailPage', {'id':item.id}]"> [routerLink]="['/DetailPage', {'id':item.id}]">
{{ item.subject }}</a> {{ item.subject }}</a>

View File

@ -2,7 +2,7 @@
<h2 class="page-title">Inbox</h2> <h2 class="page-title">Inbox</h2>
<ol class="inbox-list"> <ol class="inbox-list">
<li *ngFor="#item of items" class="inbox-item-record"> <li *ngFor="let item of items" class="inbox-item-record">
<a id="item-{{ item.id }}" <a id="item-{{ item.id }}"
[routerLink]="['/DetailPage', {'id':item.id}]">{{ item.subject }}</a> [routerLink]="['/DetailPage', {'id':item.id}]">{{ item.subject }}</a>
</li> </li>

View File

@ -122,7 +122,7 @@ class ShowError {
<p> <p>
<label for="country">Country</label> <label for="country">Country</label>
<select id="country" ngControl="country" [(ngModel)]="model.country"> <select id="country" ngControl="country" [(ngModel)]="model.country">
<option *ngFor="#c of countries" [value]="c">{{c}}</option> <option *ngFor="let c of countries" [value]="c">{{c}}</option>
</select> </select>
</p> </p>

View File

@ -18,7 +18,7 @@
<ul id="todo-list"> <ul id="todo-list">
<li *ngFor="#todo of todoStore.list"> <li *ngFor="let todo of todoStore.list">
<div class="view" <div class="view"
[class.hidden]="todoEdit == todo"> [class.hidden]="todoEdit == todo">

View File

@ -7,7 +7,7 @@
</nav> </nav>
<section id="main" class="container"> <section id="main" class="container">
<div class="row"> <div class="row">
<div *ngFor="#image of images" class="col s12 m2"> <div *ngFor="let image of images" class="col s12 m2">
<div class="card"> <div class="card">
<div class="card-image"> <div class="card-image">
<img [src]="image.src" [class.grey]="image.filtering"/> <img [src]="image.src" [class.grey]="image.filtering"/>

View File

@ -17,7 +17,7 @@
<ul id="todo-list"> <ul id="todo-list">
<li *ngFor="#todo of todoStore.list" [class.hidden]="hideActive && !todo.completed || hideCompleted && todo.completed"> <li *ngFor="let todo of todoStore.list" [class.hidden]="hideActive && !todo.completed || hideCompleted && todo.completed">
<div class="view" <div class="view"
[class.hidden]="todoEdit == todo"> [class.hidden]="todoEdit == todo">

View File

@ -9,7 +9,7 @@ import {Zippy} from './zippy';
This is some content. This is some content.
</zippy> </zippy>
<ul> <ul>
<li *ngFor="var log of logs">{{log}}</li> <li *ngFor="let log of logs">{{log}}</li>
</ul> </ul>
`, `,
directives: [Zippy] directives: [Zippy]

View File

@ -36,7 +36,7 @@ class ExternalTemplateComponent {
class MyToken {} class MyToken {}
const TEMPLATE = const TEMPLATE =
'<div><copy-me template=\'ngFor #item of items\'>{{item.toString()}};</copy-me></div>'; '<div><copy-me template=\'ngFor let item of items\'>{{item.toString()}};</copy-me></div>';
void main() { void main() {
initAngularTests(); initAngularTests();

View File

@ -2,6 +2,7 @@ library angular2.transform.template_compiler.ng_compiler;
import 'package:angular2/src/compiler/config.dart'; import 'package:angular2/src/compiler/config.dart';
import 'package:angular2/src/compiler/view_compiler/view_compiler.dart'; import 'package:angular2/src/compiler/view_compiler/view_compiler.dart';
import 'package:angular2/src/core/console.dart';
import 'package:angular2/src/compiler/html_parser.dart'; import 'package:angular2/src/compiler/html_parser.dart';
import 'package:angular2/src/compiler/style_compiler.dart'; import 'package:angular2/src/compiler/style_compiler.dart';
import 'package:angular2/src/compiler/offline_compiler.dart'; import 'package:angular2/src/compiler/offline_compiler.dart';
@ -31,6 +32,7 @@ OfflineCompiler createTemplateCompiler(AssetReader reader,
parser, parser,
new DomElementSchemaRegistry(), new DomElementSchemaRegistry(),
_htmlParser, _htmlParser,
new Console(),
[new RouterLinkTransform(parser)]); [new RouterLinkTransform(parser)]);
return new OfflineCompiler( return new OfflineCompiler(

View File

@ -848,15 +848,14 @@ const COMPILER = [
'BoundTextAst.constructor(value:AST, ngContentIndex:number, sourceSpan:ParseSourceSpan)', 'BoundTextAst.constructor(value:AST, ngContentIndex:number, sourceSpan:ParseSourceSpan)',
'BoundTextAst.visit(visitor:TemplateAstVisitor, context:any):any', 'BoundTextAst.visit(visitor:TemplateAstVisitor, context:any):any',
'DirectiveAst', 'DirectiveAst',
'DirectiveAst.constructor(directive:CompileDirectiveMetadata, inputs:BoundDirectivePropertyAst[], hostProperties:BoundElementPropertyAst[], hostEvents:BoundEventAst[], exportAsVars:VariableAst[], sourceSpan:ParseSourceSpan)', 'DirectiveAst.constructor(directive:CompileDirectiveMetadata, inputs:BoundDirectivePropertyAst[], hostProperties:BoundElementPropertyAst[], hostEvents:BoundEventAst[], sourceSpan:ParseSourceSpan)',
'DirectiveAst.visit(visitor:TemplateAstVisitor, context:any):any', 'DirectiveAst.visit(visitor:TemplateAstVisitor, context:any):any',
'ElementAst', 'ElementAst',
'ElementAst.constructor(name:string, attrs:AttrAst[], inputs:BoundElementPropertyAst[], outputs:BoundEventAst[], exportAsVars:VariableAst[], directives:DirectiveAst[], providers:ProviderAst[], hasViewContainer:boolean, children:TemplateAst[], ngContentIndex:number, sourceSpan:ParseSourceSpan)', 'ElementAst.constructor(name:string, attrs:AttrAst[], inputs:BoundElementPropertyAst[], outputs:BoundEventAst[], references:ReferenceAst[], directives:DirectiveAst[], providers:ProviderAst[], hasViewContainer:boolean, children:TemplateAst[], ngContentIndex:number, sourceSpan:ParseSourceSpan)',
'ElementAst.getComponent():CompileDirectiveMetadata', 'ElementAst.getComponent():CompileDirectiveMetadata',
'ElementAst.isBound():boolean',
'ElementAst.visit(visitor:TemplateAstVisitor, context:any):any', 'ElementAst.visit(visitor:TemplateAstVisitor, context:any):any',
'EmbeddedTemplateAst', 'EmbeddedTemplateAst',
'EmbeddedTemplateAst.constructor(attrs:AttrAst[], outputs:BoundEventAst[], vars:VariableAst[], directives:DirectiveAst[], providers:ProviderAst[], hasViewContainer:boolean, children:TemplateAst[], ngContentIndex:number, sourceSpan:ParseSourceSpan)', 'EmbeddedTemplateAst.constructor(attrs:AttrAst[], outputs:BoundEventAst[], references:ReferenceAst[], variables:VariableAst[], directives:DirectiveAst[], providers:ProviderAst[], hasViewContainer:boolean, children:TemplateAst[], ngContentIndex:number, sourceSpan:ParseSourceSpan)',
'EmbeddedTemplateAst.visit(visitor:TemplateAstVisitor, context:any):any', 'EmbeddedTemplateAst.visit(visitor:TemplateAstVisitor, context:any):any',
'NgContentAst', 'NgContentAst',
'NgContentAst.constructor(index:number, ngContentIndex:number, sourceSpan:ParseSourceSpan)', 'NgContentAst.constructor(index:number, ngContentIndex:number, sourceSpan:ParseSourceSpan)',
@ -890,6 +889,10 @@ const COMPILER = [
'VariableAst', 'VariableAst',
'VariableAst.constructor(name:string, value:string, sourceSpan:ParseSourceSpan)', 'VariableAst.constructor(name:string, value:string, sourceSpan:ParseSourceSpan)',
'VariableAst.visit(visitor:TemplateAstVisitor, context:any):any', 'VariableAst.visit(visitor:TemplateAstVisitor, context:any):any',
'ReferenceAst',
'ReferenceAst.constructor(name:string, value:CompileTokenMetadata, sourceSpan:ParseSourceSpan)',
'ReferenceAst.visit(visitor:TemplateAstVisitor, context:any):any',
'TemplateAstVisitor.visitReference(ast:ReferenceAst, context:any):any',
'XHR', 'XHR',
'XHR.get(url:string):Promise<string>', 'XHR.get(url:string):Promise<string>',
'const COMPILER_PROVIDERS:Array<Type|Provider|any[]>', 'const COMPILER_PROVIDERS:Array<Type|Provider|any[]>',