Skip to content

Commit

Permalink
feat(core): separate refs from vars.
Browse files Browse the repository at this point in the history
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 angular#7158

Closes angular#8264
  • Loading branch information
tbosch committed Jun 15, 2016
1 parent 6f1f76c commit 0b7e9e6
Show file tree
Hide file tree
Showing 69 changed files with 645 additions and 399 deletions.
2 changes: 1 addition & 1 deletion modules/angular2/docs/cheatsheet/built-in-directives.md
Expand Up @@ -14,7 +14,7 @@ Removes or recreates a portion of the DOM tree based on the showSection expressi

@cheatsheetItem
syntax:
`<li *ngFor="#item of list">`|`*ngFor`
`<li *ngFor="let item of list">`|`*ngFor`
description:
Turns the li element and its contents into a template, and uses that to instantiate a view for each item in list.

Expand Down
2 changes: 1 addition & 1 deletion modules/angular2/docs/core/01_templates.md
Expand Up @@ -433,7 +433,7 @@ Finally, we can move the `ngFor` keyword to the left hand side and prefix it wit

```
<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>
```

Expand Down
2 changes: 1 addition & 1 deletion modules/angular2/docs/core/10_view.md
Expand Up @@ -94,7 +94,7 @@ Let's start with a View such as:

```
<ul>
<li template="ngFor: #person of people">{{person}}</li>
<li template="ngFor: let person of people">{{person}}</li>
</ul>
```

Expand Down
Expand Up @@ -22,7 +22,7 @@ export class SlicePipeStringExample {
@Component({
selector: 'slice-list-example',
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>`
})
export class SlicePipeListExample {
Expand Down
Expand Up @@ -36,7 +36,7 @@ class MyCmp implements OnDeactivate {
<router-outlet></router-outlet>
<div id="log">
<h2>Log:</h2>
<p *ngFor="#logItem of logService.logs">{{ logItem }}</p>
<p *ngFor="let logItem of logService.logs">{{ logItem }}</p>
</div>
`,
directives: [ROUTER_DIRECTIVES]
Expand Down
4 changes: 2 additions & 2 deletions modules/angular2/http.ts
Expand Up @@ -56,7 +56,7 @@ export {URLSearchParams} from './src/http/url_search_params';
* <div>
* <h1>People</h1>
* <ul>
* <li *ngFor="#person of people">
* <li *ngFor="let person of people">
* {{person.name}}
* </li>
* </ul>
Expand Down Expand Up @@ -194,7 +194,7 @@ export const HTTP_BINDINGS = HTTP_PROVIDERS;
* <div>
* <h1>People</h1>
* <ul>
* <li *ngFor="#person of people">
* <li *ngFor="let person of people">
* {{person.name}}
* </li>
* </ul>
Expand Down
2 changes: 1 addition & 1 deletion modules/angular2/src/common/directives/ng_for.ts
Expand Up @@ -58,7 +58,7 @@ import {BaseException} from "../../facade/exceptions";
*
* ### Syntax
*
* - `<li *ngFor="#item of items; #i = index">...</li>`
* - `<li *ngFor="let item of items; #i = index">...</li>`
* - `<li template="ngFor #item of items; #i = index">...</li>`
* - `<template ngFor #item [ngForOf]="items" #i="index"><li>...</li></template>`
*
Expand Down
Expand Up @@ -96,7 +96,7 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
*
* ```
* <select ngControl="city">
* <option *ngFor="#c of cities" [value]="c"></option>
* <option *ngFor="let c of cities" [value]="c"></option>
* </select>
* ```
*/
Expand Down
8 changes: 6 additions & 2 deletions modules/angular2/src/compiler/expression_parser/lexer.ts
Expand Up @@ -46,7 +46,11 @@ export class Token {

isKeyword(): boolean { return (this.type == TokenType.Keyword); }

isKeywordVar(): boolean { return (this.type == TokenType.Keyword && this.strValue == "var"); }
isKeywordDeprecatedVar(): boolean {
return (this.type == TokenType.Keyword && this.strValue == "var");
}

isKeywordLet(): boolean { return (this.type == TokenType.Keyword && this.strValue == "let"); }

isKeywordNull(): boolean { return (this.type == TokenType.Keyword && this.strValue == "null"); }

Expand Down Expand Up @@ -464,4 +468,4 @@ var OPERATORS = SetWrapper.createFromList([


var KEYWORDS =
SetWrapper.createFromList(['var', 'null', 'undefined', 'true', 'false', 'if', 'else']);
SetWrapper.createFromList(['var', 'let', 'null', 'undefined', 'true', 'false', 'if', 'else']);
42 changes: 27 additions & 15 deletions modules/angular2/src/compiler/expression_parser/parser.ts
Expand Up @@ -62,6 +62,10 @@ export class SplitInterpolation {
constructor(public strings: string[], public expressions: string[]) {}
}

export class TemplateBindingParseResult {
constructor(public templateBindings: TemplateBinding[], public warnings: string[]) {}
}

@Injectable()
export class Parser {
constructor(/** @internal */
Expand Down Expand Up @@ -112,7 +116,7 @@ export class Parser {
return new Quote(prefix, uninterpretedExpression, location);
}

parseTemplateBindings(input: string, location: any): TemplateBinding[] {
parseTemplateBindings(input: string, location: any): TemplateBindingParseResult {
var tokens = this._lexer.tokenize(input);
return new _ParseAST(input, location, tokens, false).parseTemplateBindings();
}
Expand Down Expand Up @@ -228,16 +232,11 @@ export class _ParseAST {
}
}

optionalKeywordVar(): boolean {
if (this.peekKeywordVar()) {
this.advance();
return true;
} else {
return false;
}
}
peekKeywordLet(): boolean { return this.next.isKeywordLet(); }

peekDeprecatedKeywordVar(): boolean { return this.next.isKeywordDeprecatedVar(); }

peekKeywordVar(): boolean { return this.next.isKeywordVar() || this.next.isOperator('#'); }
peekDeprecatedOperatorHash(): boolean { return this.next.isOperator('#'); }

expectCharacter(code: number) {
if (this.optionalCharacter(code)) return;
Expand Down Expand Up @@ -617,11 +616,23 @@ export class _ParseAST {
return result.toString();
}

parseTemplateBindings(): any[] {
var bindings = [];
parseTemplateBindings(): TemplateBindingParseResult {
var bindings: TemplateBinding[] = [];
var prefix = null;
var warnings: string[] = [];
while (this.index < this.tokens.length) {
var keyIsVar: boolean = this.optionalKeywordVar();
var keyIsVar: boolean = this.peekKeywordLet();
if (!keyIsVar && this.peekDeprecatedKeywordVar()) {
keyIsVar = true;
warnings.push(`"var" inside of expressions is deprecated. Use "let" instead!`);
}
if (!keyIsVar && this.peekDeprecatedOperatorHash()) {
keyIsVar = true;
warnings.push(`"#" inside of expressions is deprecated. Use "let" instead!`);
}
if (keyIsVar) {
this.advance();
}
var key = this.expectTemplateBindingKey();
if (!keyIsVar) {
if (prefix == null) {
Expand All @@ -639,7 +650,8 @@ export class _ParseAST {
} else {
name = '\$implicit';
}
} else if (this.next !== EOF && !this.peekKeywordVar()) {
} else if (this.next !== EOF && !this.peekKeywordLet() && !this.peekDeprecatedKeywordVar() &&
!this.peekDeprecatedOperatorHash()) {
var start = this.inputIndex;
var ast = this.parsePipe();
var source = this.input.substring(start, this.inputIndex);
Expand All @@ -650,7 +662,7 @@ export class _ParseAST {
this.optionalCharacter($COMMA);
}
}
return bindings;
return new TemplateBindingParseResult(bindings, warnings);
}

error(message: string, index: number = null) {
Expand Down
8 changes: 7 additions & 1 deletion modules/angular2/src/compiler/parse_util.ts
Expand Up @@ -21,8 +21,14 @@ export class ParseSourceSpan {
}
}

export enum ParseErrorLevel {
WARNING,
FATAL
}

export abstract class ParseError {
constructor(public span: ParseSourceSpan, public msg: string) {}
constructor(public span: ParseSourceSpan, public msg: string,
public level: ParseErrorLevel = ParseErrorLevel.FATAL) {}

toString(): string {
var source = this.span.start.file.content;
Expand Down
9 changes: 4 additions & 5 deletions modules/angular2/src/compiler/provider_parser.ts
Expand Up @@ -7,7 +7,7 @@ import {
NgContentAst,
EmbeddedTemplateAst,
ElementAst,
VariableAst,
ReferenceAst,
BoundEventAst,
BoundElementPropertyAst,
AttrAst,
Expand Down Expand Up @@ -71,7 +71,7 @@ export class ProviderElementContext {

constructor(private _viewContext: ProviderViewContext, private _parent: ProviderElementContext,
private _isViewRoot: boolean, private _directiveAsts: DirectiveAst[],
attrs: AttrAst[], vars: VariableAst[], private _sourceSpan: ParseSourceSpan) {
attrs: AttrAst[], refs: ReferenceAst[], private _sourceSpan: ParseSourceSpan) {
this._attrs = {};
attrs.forEach((attrAst) => this._attrs[attrAst.name] = attrAst.value);
var directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive);
Expand All @@ -81,9 +81,8 @@ export class ProviderElementContext {
var queriedTokens = new CompileTokenMap<boolean>();
this._allProviders.values().forEach(
(provider) => { this._addQueryReadsTo(provider.token, queriedTokens); });
vars.forEach((varAst) => {
var varToken = new CompileTokenMetadata({value: varAst.name});
this._addQueryReadsTo(varToken, queriedTokens);
refs.forEach((refAst) => {
this._addQueryReadsTo(new CompileTokenMetadata({value: refAst.name}), queriedTokens);
});
if (isPresent(queriedTokens.get(identifierToken(Identifiers.ViewContainerRef)))) {
this._hasViewContainer = true;
Expand Down
1 change: 0 additions & 1 deletion modules/angular2/src/compiler/runtime_compiler.ts
Expand Up @@ -32,7 +32,6 @@ import {
NgContentAst,
EmbeddedTemplateAst,
ElementAst,
VariableAst,
BoundEventAst,
BoundElementPropertyAst,
AttrAst,
Expand Down
21 changes: 17 additions & 4 deletions modules/angular2/src/compiler/template_ast.ts
Expand Up @@ -82,7 +82,18 @@ export class BoundEventAst implements TemplateAst {
}

/**
* A variable declaration on an element (e.g. `#var="expression"`).
* A reference declaration on an element (e.g. `let someName="expression"`).
*/
export class ReferenceAst implements TemplateAst {
constructor(public name: string, public value: CompileTokenMetadata,
public sourceSpan: ParseSourceSpan) {}
visit(visitor: TemplateAstVisitor, context: any): any {
return visitor.visitReference(this, context);
}
}

/**
* A variable declaration on a <template> (e.g. `var-someName="someLocalName"`).
*/
export class VariableAst implements TemplateAst {
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
Expand All @@ -97,7 +108,7 @@ export class VariableAst implements TemplateAst {
export class ElementAst implements TemplateAst {
constructor(public name: string, public attrs: AttrAst[],
public inputs: BoundElementPropertyAst[], public outputs: BoundEventAst[],
public exportAsVars: VariableAst[], public directives: DirectiveAst[],
public references: ReferenceAst[], public directives: DirectiveAst[],
public providers: ProviderAst[], public hasViewContainer: boolean,
public children: TemplateAst[], public ngContentIndex: number,
public sourceSpan: ParseSourceSpan) {}
Expand All @@ -111,7 +122,8 @@ export class ElementAst implements TemplateAst {
* A `<template>` element included in an Angular template.
*/
export class EmbeddedTemplateAst implements TemplateAst {
constructor(public attrs: AttrAst[], public outputs: BoundEventAst[], public vars: VariableAst[],
constructor(public attrs: AttrAst[], public outputs: BoundEventAst[],
public references: ReferenceAst[], public variables: VariableAst[],
public directives: DirectiveAst[], public providers: ProviderAst[],
public hasViewContainer: boolean, public children: TemplateAst[],
public ngContentIndex: number, public sourceSpan: ParseSourceSpan) {}
Expand Down Expand Up @@ -139,7 +151,7 @@ export class DirectiveAst implements TemplateAst {
constructor(public directive: CompileDirectiveMetadata,
public inputs: BoundDirectivePropertyAst[],
public hostProperties: BoundElementPropertyAst[], public hostEvents: BoundEventAst[],
public exportAsVars: VariableAst[], public sourceSpan: ParseSourceSpan) {}
public sourceSpan: ParseSourceSpan) {}
visit(visitor: TemplateAstVisitor, context: any): any {
return visitor.visitDirective(this, context);
}
Expand Down Expand Up @@ -211,6 +223,7 @@ export interface TemplateAstVisitor {
visitNgContent(ast: NgContentAst, context: any): any;
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any;
visitElement(ast: ElementAst, context: any): any;
visitReference(ast: ReferenceAst, context: any): any;
visitVariable(ast: VariableAst, context: any): any;
visitEvent(ast: BoundEventAst, context: any): any;
visitElementProperty(ast: BoundElementPropertyAst, context: any): any;
Expand Down

0 comments on commit 0b7e9e6

Please sign in to comment.