feat(common): support as
syntax in template/* bindings (#15025)
* feat(common): support `as` syntax in template/* bindings Closes #15020 Showing the new and the equivalent old syntax. - `*ngIf="exp as var1”` => `*ngIf="exp; let var1 = ngIf”` - `*ngFor="var item of itemsStream |async as items”` => `*ngFor="var item of itemsStream |async; let items = ngForOf”` * feat(common): convert ngIf to use `*ngIf="exp as local“` syntax * feat(common): convert ngForOf to use `*ngFor=“let i of exp as local“` syntax * feat(common): expose NgForOfContext and NgIfContext
This commit is contained in:

committed by
Chuck Jazdzewski

parent
5fe2d8fd80
commit
c10c060d20
@ -19,7 +19,7 @@ export enum TokenType {
|
||||
Error
|
||||
}
|
||||
|
||||
const KEYWORDS = ['var', 'let', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this'];
|
||||
const KEYWORDS = ['var', 'let', 'as', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this'];
|
||||
|
||||
@CompilerInjectable()
|
||||
export class Lexer {
|
||||
@ -58,6 +58,8 @@ export class Token {
|
||||
|
||||
isKeywordLet(): boolean { return this.type == TokenType.Keyword && this.strValue == 'let'; }
|
||||
|
||||
isKeywordAs(): boolean { return this.type == TokenType.Keyword && this.strValue == 'as'; }
|
||||
|
||||
isKeywordNull(): boolean { return this.type == TokenType.Keyword && this.strValue == 'null'; }
|
||||
|
||||
isKeywordUndefined(): boolean {
|
||||
|
@ -267,6 +267,7 @@ export class _ParseAST {
|
||||
}
|
||||
|
||||
peekKeywordLet(): boolean { return this.next.isKeywordLet(); }
|
||||
peekKeywordAs(): boolean { return this.next.isKeywordAs(); }
|
||||
|
||||
expectCharacter(code: number) {
|
||||
if (this.optionalCharacter(code)) return;
|
||||
@ -686,11 +687,12 @@ export class _ParseAST {
|
||||
const warnings: string[] = [];
|
||||
while (this.index < this.tokens.length) {
|
||||
const start = this.inputIndex;
|
||||
const keyIsVar: boolean = this.peekKeywordLet();
|
||||
let keyIsVar: boolean = this.peekKeywordLet();
|
||||
if (keyIsVar) {
|
||||
this.advance();
|
||||
}
|
||||
let key = this.expectTemplateBindingKey();
|
||||
let rawKey = this.expectTemplateBindingKey();
|
||||
let key = rawKey;
|
||||
if (!keyIsVar) {
|
||||
if (prefix == null) {
|
||||
prefix = key;
|
||||
@ -707,6 +709,12 @@ export class _ParseAST {
|
||||
} else {
|
||||
name = '\$implicit';
|
||||
}
|
||||
} else if (this.peekKeywordAs()) {
|
||||
const letStart = this.inputIndex;
|
||||
this.advance(); // consume `as`
|
||||
name = rawKey;
|
||||
key = this.expectTemplateBindingKey(); // read local var name
|
||||
keyIsVar = true;
|
||||
} else if (this.next !== EOF && !this.peekKeywordLet()) {
|
||||
const start = this.inputIndex;
|
||||
const ast = this.parsePipe();
|
||||
@ -714,6 +722,12 @@ export class _ParseAST {
|
||||
expression = new ASTWithSource(ast, source, this.location, this.errors);
|
||||
}
|
||||
bindings.push(new TemplateBinding(this.span(start), key, keyIsVar, name, expression));
|
||||
if (this.peekKeywordAs() && !keyIsVar) {
|
||||
const letStart = this.inputIndex;
|
||||
this.advance(); // consume `as`
|
||||
const letName = this.expectTemplateBindingKey(); // read local var name
|
||||
bindings.push(new TemplateBinding(this.span(letStart), letName, true, key, null));
|
||||
}
|
||||
if (!this.optionalCharacter(chars.$SEMICOLON)) {
|
||||
this.optionalCharacter(chars.$COMMA);
|
||||
}
|
||||
|
@ -429,6 +429,16 @@ export function main() {
|
||||
]);
|
||||
});
|
||||
|
||||
it('should support as notation', () => {
|
||||
let bindings = parseTemplateBindings('ngIf exp as local', 'location');
|
||||
expect(keyValues(bindings)).toEqual(['ngIf=exp in location', 'let local=ngIf']);
|
||||
|
||||
bindings = parseTemplateBindings('ngFor let item of items as iter; index as i', 'L');
|
||||
expect(keyValues(bindings)).toEqual([
|
||||
'ngFor', 'let item=$implicit', 'ngForOf=items in L', 'let iter=ngForOf', 'let i=index'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse pipes', () => {
|
||||
const bindings = parseTemplateBindings('key value|pipe');
|
||||
const ast = bindings[0].expression.ast;
|
||||
|
@ -1334,6 +1334,18 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
|
||||
expect(humanizeTplAst(parse('<div data-*ngIf="let a=b">', []))).toEqual(targetAst);
|
||||
});
|
||||
|
||||
it('should parse variables via as ...', () => {
|
||||
const targetAst = [
|
||||
[EmbeddedTemplateAst],
|
||||
[VariableAst, 'local', 'ngIf'],
|
||||
[DirectiveAst, ngIf],
|
||||
[BoundDirectivePropertyAst, 'ngIf', 'expr'],
|
||||
[ElementAst, 'div'],
|
||||
];
|
||||
|
||||
expect(humanizeTplAst(parse('<div *ngIf="expr as local">', [ngIf]))).toEqual(targetAst);
|
||||
});
|
||||
|
||||
describe('directives', () => {
|
||||
it('should locate directives in property bindings', () => {
|
||||
const dirA =
|
||||
|
Reference in New Issue
Block a user