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:
parent
5fe2d8fd80
commit
c10c060d20
|
@ -14,7 +14,7 @@
|
||||||
export * from './location/index';
|
export * from './location/index';
|
||||||
export {NgLocaleLocalization, NgLocalization} from './localization';
|
export {NgLocaleLocalization, NgLocalization} from './localization';
|
||||||
export {CommonModule} from './common_module';
|
export {CommonModule} from './common_module';
|
||||||
export {NgClass, NgFor, NgForOf, NgIf, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet, NgComponentOutlet} from './directives/index';
|
export {NgClass, NgFor, NgForOf, NgForOfContext, NgIf, NgIfContext, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet, NgComponentOutlet} from './directives/index';
|
||||||
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe, TitleCasePipe} from './pipes/index';
|
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe, TitleCasePipe} from './pipes/index';
|
||||||
export {PLATFORM_BROWSER_ID as ɵPLATFORM_BROWSER_ID, PLATFORM_SERVER_ID as ɵPLATFORM_SERVER_ID, PLATFORM_WORKER_APP_ID as ɵPLATFORM_WORKER_APP_ID, PLATFORM_WORKER_UI_ID as ɵPLATFORM_WORKER_UI_ID, isPlatformBrowser, isPlatformServer, isPlatformWorkerApp, isPlatformWorkerUi} from './platform_id';
|
export {PLATFORM_BROWSER_ID as ɵPLATFORM_BROWSER_ID, PLATFORM_SERVER_ID as ɵPLATFORM_SERVER_ID, PLATFORM_WORKER_APP_ID as ɵPLATFORM_WORKER_APP_ID, PLATFORM_WORKER_UI_ID as ɵPLATFORM_WORKER_UI_ID, isPlatformBrowser, isPlatformServer, isPlatformWorkerApp, isPlatformWorkerUi} from './platform_id';
|
||||||
export {VERSION} from './version';
|
export {VERSION} from './version';
|
||||||
|
|
|
@ -10,8 +10,8 @@ import {Provider} from '@angular/core';
|
||||||
|
|
||||||
import {NgClass} from './ng_class';
|
import {NgClass} from './ng_class';
|
||||||
import {NgComponentOutlet} from './ng_component_outlet';
|
import {NgComponentOutlet} from './ng_component_outlet';
|
||||||
import {NgFor, NgForOf} from './ng_for_of';
|
import {NgFor, NgForOf, NgForOfContext} from './ng_for_of';
|
||||||
import {NgIf} from './ng_if';
|
import {NgIf, NgIfContext} from './ng_if';
|
||||||
import {NgPlural, NgPluralCase} from './ng_plural';
|
import {NgPlural, NgPluralCase} from './ng_plural';
|
||||||
import {NgStyle} from './ng_style';
|
import {NgStyle} from './ng_style';
|
||||||
import {NgSwitch, NgSwitchCase, NgSwitchDefault} from './ng_switch';
|
import {NgSwitch, NgSwitchCase, NgSwitchDefault} from './ng_switch';
|
||||||
|
@ -22,7 +22,9 @@ export {
|
||||||
NgComponentOutlet,
|
NgComponentOutlet,
|
||||||
NgFor,
|
NgFor,
|
||||||
NgForOf,
|
NgForOf,
|
||||||
|
NgForOfContext,
|
||||||
NgIf,
|
NgIf,
|
||||||
|
NgIfContext,
|
||||||
NgPlural,
|
NgPlural,
|
||||||
NgPluralCase,
|
NgPluralCase,
|
||||||
NgStyle,
|
NgStyle,
|
||||||
|
|
|
@ -8,8 +8,13 @@
|
||||||
|
|
||||||
import {ChangeDetectorRef, Directive, DoCheck, EmbeddedViewRef, Input, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDiffers, NgIterable, OnChanges, SimpleChanges, TemplateRef, TrackByFunction, ViewContainerRef, forwardRef, isDevMode} from '@angular/core';
|
import {ChangeDetectorRef, Directive, DoCheck, EmbeddedViewRef, Input, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDiffers, NgIterable, OnChanges, SimpleChanges, TemplateRef, TrackByFunction, ViewContainerRef, forwardRef, isDevMode} from '@angular/core';
|
||||||
|
|
||||||
export class NgForOfRow<T> {
|
/**
|
||||||
constructor(public $implicit: T, public index: number, public count: number) {}
|
* @stable
|
||||||
|
*/
|
||||||
|
export class NgForOfContext<T> {
|
||||||
|
constructor(
|
||||||
|
public $implicit: T, public ngForOf: NgIterable<T>, public index: number,
|
||||||
|
public count: number) {}
|
||||||
|
|
||||||
get first(): boolean { return this.index === 0; }
|
get first(): boolean { return this.index === 0; }
|
||||||
|
|
||||||
|
@ -29,13 +34,21 @@ export class NgForOfRow<T> {
|
||||||
*
|
*
|
||||||
* `NgForOf` provides several exported values that can be aliased to local variables:
|
* `NgForOf` provides several exported values that can be aliased to local variables:
|
||||||
*
|
*
|
||||||
* * `index` will be set to the current loop iteration for each template context.
|
* - `$implicit: T`: The value of the individual items in the iterable (`ngForOf`).
|
||||||
* * `first` will be set to a boolean value indicating whether the item is the first one in the
|
* - `ngForOf: NgIterable<T>`: The value of the iterable expression. Useful when the expression is
|
||||||
* iteration.
|
* more complex then a property access, for example when using the async pipe (`userStreams |
|
||||||
* * `last` will be set to a boolean value indicating whether the item is the last one in the
|
* async`).
|
||||||
* iteration.
|
* - `index: number`: The index of the current item in the iterable.
|
||||||
* * `even` will be set to a boolean value indicating whether this item has an even index.
|
* - `first: boolean`: True when the item is the first item in the iterable.
|
||||||
* * `odd` will be set to a boolean value indicating whether this item has an odd index.
|
* - `last: boolean`: True when the item is the last item in the iterable.
|
||||||
|
* - `even: boolean`: True when the item has an even index in the iterable.
|
||||||
|
* - `odd: boolean`: True when the item has an odd index in the iterable.
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* <li *ngFor="let user of userObservable | async as users; indexes as i; first as isFirst">
|
||||||
|
* {{i}}/{{users.length}}. {{user}} <span *ngIf="isFirst">default</span>
|
||||||
|
* </li>
|
||||||
|
* ```
|
||||||
*
|
*
|
||||||
* ### Change Propagation
|
* ### Change Propagation
|
||||||
*
|
*
|
||||||
|
@ -105,11 +118,11 @@ export class NgForOf<T> implements DoCheck, OnChanges {
|
||||||
private _trackByFn: TrackByFunction<T>;
|
private _trackByFn: TrackByFunction<T>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _viewContainer: ViewContainerRef, private _template: TemplateRef<NgForOfRow<T>>,
|
private _viewContainer: ViewContainerRef, private _template: TemplateRef<NgForOfContext<T>>,
|
||||||
private _differs: IterableDiffers) {}
|
private _differs: IterableDiffers) {}
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
set ngForTemplate(value: TemplateRef<NgForOfRow<T>>) {
|
set ngForTemplate(value: TemplateRef<NgForOfContext<T>>) {
|
||||||
// TODO(TS2.1): make TemplateRef<Partial<NgForRowOf<T>>> once we move to TS v2.1
|
// TODO(TS2.1): make TemplateRef<Partial<NgForRowOf<T>>> once we move to TS v2.1
|
||||||
// The current type is too restrictive; a template that just uses index, for example,
|
// The current type is too restrictive; a template that just uses index, for example,
|
||||||
// should be acceptable.
|
// should be acceptable.
|
||||||
|
@ -146,7 +159,7 @@ export class NgForOf<T> implements DoCheck, OnChanges {
|
||||||
(item: IterableChangeRecord<any>, adjustedPreviousIndex: number, currentIndex: number) => {
|
(item: IterableChangeRecord<any>, adjustedPreviousIndex: number, currentIndex: number) => {
|
||||||
if (item.previousIndex == null) {
|
if (item.previousIndex == null) {
|
||||||
const view = this._viewContainer.createEmbeddedView(
|
const view = this._viewContainer.createEmbeddedView(
|
||||||
this._template, new NgForOfRow(null, null, null), currentIndex);
|
this._template, new NgForOfContext(null, this.ngForOf, null, null), currentIndex);
|
||||||
const tuple = new RecordViewTuple(item, view);
|
const tuple = new RecordViewTuple(item, view);
|
||||||
insertTuples.push(tuple);
|
insertTuples.push(tuple);
|
||||||
} else if (currentIndex == null) {
|
} else if (currentIndex == null) {
|
||||||
|
@ -154,7 +167,7 @@ export class NgForOf<T> implements DoCheck, OnChanges {
|
||||||
} else {
|
} else {
|
||||||
const view = this._viewContainer.get(adjustedPreviousIndex);
|
const view = this._viewContainer.get(adjustedPreviousIndex);
|
||||||
this._viewContainer.move(view, currentIndex);
|
this._viewContainer.move(view, currentIndex);
|
||||||
const tuple = new RecordViewTuple(item, <EmbeddedViewRef<NgForOfRow<T>>>view);
|
const tuple = new RecordViewTuple(item, <EmbeddedViewRef<NgForOfContext<T>>>view);
|
||||||
insertTuples.push(tuple);
|
insertTuples.push(tuple);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -164,24 +177,26 @@ export class NgForOf<T> implements DoCheck, OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
||||||
const viewRef = <EmbeddedViewRef<NgForOfRow<T>>>this._viewContainer.get(i);
|
const viewRef = <EmbeddedViewRef<NgForOfContext<T>>>this._viewContainer.get(i);
|
||||||
viewRef.context.index = i;
|
viewRef.context.index = i;
|
||||||
viewRef.context.count = ilen;
|
viewRef.context.count = ilen;
|
||||||
}
|
}
|
||||||
|
|
||||||
changes.forEachIdentityChange((record: any) => {
|
changes.forEachIdentityChange((record: any) => {
|
||||||
const viewRef = <EmbeddedViewRef<NgForOfRow<T>>>this._viewContainer.get(record.currentIndex);
|
const viewRef =
|
||||||
|
<EmbeddedViewRef<NgForOfContext<T>>>this._viewContainer.get(record.currentIndex);
|
||||||
viewRef.context.$implicit = record.item;
|
viewRef.context.$implicit = record.item;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _perViewChange(view: EmbeddedViewRef<NgForOfRow<T>>, record: IterableChangeRecord<any>) {
|
private _perViewChange(
|
||||||
|
view: EmbeddedViewRef<NgForOfContext<T>>, record: IterableChangeRecord<any>) {
|
||||||
view.context.$implicit = record.item;
|
view.context.$implicit = record.item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RecordViewTuple<T> {
|
class RecordViewTuple<T> {
|
||||||
constructor(public record: any, public view: EmbeddedViewRef<NgForOfRow<T>>) {}
|
constructor(public record: any, public view: EmbeddedViewRef<NgForOfContext<T>>) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -61,7 +61,7 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '
|
||||||
* A better way to do this is to use `ngIf` and store the result of the condition in a local
|
* A better way to do this is to use `ngIf` and store the result of the condition in a local
|
||||||
* variable as shown in the the example below:
|
* variable as shown in the the example below:
|
||||||
*
|
*
|
||||||
* {@example common/ngIf/ts/module.ts region='NgIfLet'}
|
* {@example common/ngIf/ts/module.ts region='NgIfAs'}
|
||||||
*
|
*
|
||||||
* Notice that:
|
* Notice that:
|
||||||
* - We use only one `async` pipe and hence only one subscription gets created.
|
* - We use only one `async` pipe and hence only one subscription gets created.
|
||||||
|
@ -93,7 +93,7 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '
|
||||||
*
|
*
|
||||||
* Form with storing the value locally:
|
* Form with storing the value locally:
|
||||||
* ```
|
* ```
|
||||||
* <div *ngIf="condition; else elseBlock; let value">{{value}}</div>
|
* <div *ngIf="condition as value; else elseBlock">{{value}}</div>
|
||||||
* <ng-template #elseBlock>...</ng-template>
|
* <ng-template #elseBlock>...</ng-template>
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
@ -113,7 +113,7 @@ export class NgIf {
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
set ngIf(condition: any) {
|
set ngIf(condition: any) {
|
||||||
this._context.$implicit = condition;
|
this._context.$implicit = this._context.ngIf = condition;
|
||||||
this._updateView();
|
this._updateView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,4 +154,10 @@ export class NgIf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NgIfContext { public $implicit: any = null; }
|
/**
|
||||||
|
* @stable
|
||||||
|
*/
|
||||||
|
export class NgIfContext {
|
||||||
|
public $implicit: any = null;
|
||||||
|
public ngIf: any = null;
|
||||||
|
}
|
||||||
|
|
|
@ -181,6 +181,14 @@ export function main() {
|
||||||
detectChangesAndExpectText('0|even|1|2|even|');
|
detectChangesAndExpectText('0|even|1|2|even|');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should allow of saving the collection', async(() => {
|
||||||
|
const template =
|
||||||
|
'<ul><li *ngFor="let item of [1,2,3] as items; index as i">{{i}}/{{items.length}} - {{item}};</li></ul>';
|
||||||
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
|
detectChangesAndExpectText('0/3 - 1;1/3 - 2;2/3 - 3;');
|
||||||
|
}));
|
||||||
|
|
||||||
it('should display indices correctly', async(() => {
|
it('should display indices correctly', async(() => {
|
||||||
const template = '<span *ngFor ="let item of items; let i=index">{{i.toString()}}</span>';
|
const template = '<span *ngFor ="let item of items; let i=index">{{i.toString()}}</span>';
|
||||||
fixture = createTestComponent(template);
|
fixture = createTestComponent(template);
|
||||||
|
|
|
@ -189,7 +189,7 @@ export function main() {
|
||||||
expect(fixture.nativeElement).toHaveText('FALSE2');
|
expect(fixture.nativeElement).toHaveText('FALSE2');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should support binding to variable', async(() => {
|
it('should support binding to variable using let', async(() => {
|
||||||
const template = '<span *ngIf="booleanCondition; else elseBlock; let v">{{v}}</span>' +
|
const template = '<span *ngIf="booleanCondition; else elseBlock; let v">{{v}}</span>' +
|
||||||
'<ng-template #elseBlock let-v>{{v}}</ng-template>';
|
'<ng-template #elseBlock let-v>{{v}}</ng-template>';
|
||||||
|
|
||||||
|
@ -198,6 +198,20 @@ export function main() {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.nativeElement).toHaveText('true');
|
expect(fixture.nativeElement).toHaveText('true');
|
||||||
|
|
||||||
|
getComponent().booleanCondition = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('false');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should support binding to variable using as', async(() => {
|
||||||
|
const template = '<span *ngIf="booleanCondition as v; else elseBlock">{{v}}</span>' +
|
||||||
|
'<ng-template #elseBlock let-v>{{v}}</ng-template>';
|
||||||
|
|
||||||
|
fixture = createTestComponent(template);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('true');
|
||||||
|
|
||||||
getComponent().booleanCondition = false;
|
getComponent().booleanCondition = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.nativeElement).toHaveText('false');
|
expect(fixture.nativeElement).toHaveText('false');
|
||||||
|
|
|
@ -19,7 +19,7 @@ export enum TokenType {
|
||||||
Error
|
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()
|
@CompilerInjectable()
|
||||||
export class Lexer {
|
export class Lexer {
|
||||||
|
@ -58,6 +58,8 @@ export class Token {
|
||||||
|
|
||||||
isKeywordLet(): boolean { return this.type == TokenType.Keyword && this.strValue == 'let'; }
|
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'; }
|
isKeywordNull(): boolean { return this.type == TokenType.Keyword && this.strValue == 'null'; }
|
||||||
|
|
||||||
isKeywordUndefined(): boolean {
|
isKeywordUndefined(): boolean {
|
||||||
|
|
|
@ -267,6 +267,7 @@ export class _ParseAST {
|
||||||
}
|
}
|
||||||
|
|
||||||
peekKeywordLet(): boolean { return this.next.isKeywordLet(); }
|
peekKeywordLet(): boolean { return this.next.isKeywordLet(); }
|
||||||
|
peekKeywordAs(): boolean { return this.next.isKeywordAs(); }
|
||||||
|
|
||||||
expectCharacter(code: number) {
|
expectCharacter(code: number) {
|
||||||
if (this.optionalCharacter(code)) return;
|
if (this.optionalCharacter(code)) return;
|
||||||
|
@ -686,11 +687,12 @@ export class _ParseAST {
|
||||||
const warnings: string[] = [];
|
const warnings: string[] = [];
|
||||||
while (this.index < this.tokens.length) {
|
while (this.index < this.tokens.length) {
|
||||||
const start = this.inputIndex;
|
const start = this.inputIndex;
|
||||||
const keyIsVar: boolean = this.peekKeywordLet();
|
let keyIsVar: boolean = this.peekKeywordLet();
|
||||||
if (keyIsVar) {
|
if (keyIsVar) {
|
||||||
this.advance();
|
this.advance();
|
||||||
}
|
}
|
||||||
let key = this.expectTemplateBindingKey();
|
let rawKey = this.expectTemplateBindingKey();
|
||||||
|
let key = rawKey;
|
||||||
if (!keyIsVar) {
|
if (!keyIsVar) {
|
||||||
if (prefix == null) {
|
if (prefix == null) {
|
||||||
prefix = key;
|
prefix = key;
|
||||||
|
@ -707,6 +709,12 @@ export class _ParseAST {
|
||||||
} else {
|
} else {
|
||||||
name = '\$implicit';
|
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()) {
|
} else if (this.next !== EOF && !this.peekKeywordLet()) {
|
||||||
const start = this.inputIndex;
|
const start = this.inputIndex;
|
||||||
const ast = this.parsePipe();
|
const ast = this.parsePipe();
|
||||||
|
@ -714,6 +722,12 @@ export class _ParseAST {
|
||||||
expression = new ASTWithSource(ast, source, this.location, this.errors);
|
expression = new ASTWithSource(ast, source, this.location, this.errors);
|
||||||
}
|
}
|
||||||
bindings.push(new TemplateBinding(this.span(start), key, keyIsVar, name, expression));
|
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)) {
|
if (!this.optionalCharacter(chars.$SEMICOLON)) {
|
||||||
this.optionalCharacter(chars.$COMMA);
|
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', () => {
|
it('should parse pipes', () => {
|
||||||
const bindings = parseTemplateBindings('key value|pipe');
|
const bindings = parseTemplateBindings('key value|pipe');
|
||||||
const ast = bindings[0].expression.ast;
|
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);
|
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', () => {
|
describe('directives', () => {
|
||||||
it('should locate directives in property bindings', () => {
|
it('should locate directives in property bindings', () => {
|
||||||
const dirA =
|
const dirA =
|
||||||
|
|
|
@ -73,19 +73,19 @@ class NgIfThenElse implements OnInit {
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
// #docregion NgIfLet
|
// #docregion NgIfAs
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ng-if-let',
|
selector: 'ng-if-let',
|
||||||
template: `
|
template: `
|
||||||
<button (click)="nextUser()">Next User</button>
|
<button (click)="nextUser()">Next User</button>
|
||||||
<br>
|
<br>
|
||||||
<div *ngIf="userObservable | async; else loading; let user">
|
<div *ngIf="userObservable | async as user; else loading">
|
||||||
Hello {{user.last}}, {{user.first}}!
|
Hello {{user.last}}, {{user.first}}!
|
||||||
</div>
|
</div>
|
||||||
<ng-template #loading let-user>Waiting... (user is {{user|json}})</ng-template>
|
<ng-template #loading let-user>Waiting... (user is {{user|json}})</ng-template>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
class NgIfLet {
|
class NgIfAs {
|
||||||
userObservable = new Subject<{first: string, last: string}>();
|
userObservable = new Subject<{first: string, last: string}>();
|
||||||
first = ['John', 'Mike', 'Mary', 'Bob'];
|
first = ['John', 'Mike', 'Mary', 'Bob'];
|
||||||
firstIndex = 0;
|
firstIndex = 0;
|
||||||
|
@ -121,7 +121,7 @@ class ExampleApp {
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [BrowserModule],
|
imports: [BrowserModule],
|
||||||
declarations: [ExampleApp, NgIfSimple, NgIfElse, NgIfThenElse, NgIfLet],
|
declarations: [ExampleApp, NgIfSimple, NgIfElse, NgIfThenElse, NgIfAs],
|
||||||
bootstrap: [ExampleApp]
|
bootstrap: [ExampleApp]
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
|
|
|
@ -151,13 +151,26 @@ export declare const NgFor: typeof NgForOf;
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare class NgForOf<T> implements DoCheck, OnChanges {
|
export declare class NgForOf<T> implements DoCheck, OnChanges {
|
||||||
ngForOf: NgIterable<T>;
|
ngForOf: NgIterable<T>;
|
||||||
ngForTemplate: TemplateRef<NgForOfRow<T>>;
|
ngForTemplate: TemplateRef<NgForOfContext<T>>;
|
||||||
ngForTrackBy: TrackByFunction<T>;
|
ngForTrackBy: TrackByFunction<T>;
|
||||||
constructor(_viewContainer: ViewContainerRef, _template: TemplateRef<NgForOfRow<T>>, _differs: IterableDiffers);
|
constructor(_viewContainer: ViewContainerRef, _template: TemplateRef<NgForOfContext<T>>, _differs: IterableDiffers);
|
||||||
ngDoCheck(): void;
|
ngDoCheck(): void;
|
||||||
ngOnChanges(changes: SimpleChanges): void;
|
ngOnChanges(changes: SimpleChanges): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @stable */
|
||||||
|
export declare class NgForOfContext<T> {
|
||||||
|
$implicit: T;
|
||||||
|
count: number;
|
||||||
|
readonly even: boolean;
|
||||||
|
readonly first: boolean;
|
||||||
|
index: number;
|
||||||
|
readonly last: boolean;
|
||||||
|
ngForOf: NgIterable<T>;
|
||||||
|
readonly odd: boolean;
|
||||||
|
constructor($implicit: T, ngForOf: NgIterable<T>, index: number, count: number);
|
||||||
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare class NgIf {
|
export declare class NgIf {
|
||||||
ngIf: any;
|
ngIf: any;
|
||||||
|
@ -166,6 +179,12 @@ export declare class NgIf {
|
||||||
constructor(_viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext>);
|
constructor(_viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @stable */
|
||||||
|
export declare class NgIfContext {
|
||||||
|
$implicit: any;
|
||||||
|
ngIf: any;
|
||||||
|
}
|
||||||
|
|
||||||
/** @experimental */
|
/** @experimental */
|
||||||
export declare class NgLocaleLocalization extends NgLocalization {
|
export declare class NgLocaleLocalization extends NgLocalization {
|
||||||
protected locale: string;
|
protected locale: string;
|
||||||
|
|
Loading…
Reference in New Issue