feat(common): rename underlying `NgFor` class and add a type parameter (#14104)
Note, this affects the underlying class and should not affect usage. DEPRECATION: - the `NgFor` class is now deprecated. Use `NgForOf<T>` instead. IMPORTANT: Only the `NgFor` class is deprecated, not the `ngFor` directive. The `*ngFor` and related directives are unaffected by this change as references to the `NgFor` class generated from templates will be automatically converted to references to `NgForOf<T>` without requiring any template modifications. - `TrackByFn` is now deprecated. Use `TrackByFunction<T>` instead. Migration: - Replace direct references to the `NgFor` class to `NgForOf<any>`. - Replace references to `TrackByFn` to `TrackByFunction<any>`. BREAKING CHANGE: A definition of `Iterable<T>` is now required to correctly compile Angular applications. Support for `Iterable<T>` is not required at runtime but a type definition `Iterable<T>` must be available. `NgFor`, and now `NgForOf<T>`, already supports `Iterable<T>` at runtime. With this change the type definition is updated to reflect this support. Migration: - add "es2015.iterable.ts" to your tsconfig.json "libs" fields. Part of #12398 PR Close #14104
This commit is contained in:
parent
69e14b500b
commit
86b2b2504f
|
@ -14,6 +14,6 @@
|
|||
export * from './location/index';
|
||||
export {NgLocaleLocalization, NgLocalization} from './localization';
|
||||
export {CommonModule} from './common_module';
|
||||
export {NgClass, NgFor, NgIf, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet, NgComponentOutlet} from './directives/index';
|
||||
export {NgClass, NgFor, NgForOf, NgIf, 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 {VERSION} from './version';
|
||||
|
|
|
@ -8,14 +8,15 @@
|
|||
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
import {COMMON_DIRECTIVES} from './directives/index';
|
||||
import {COMMON_DEPRECATED_DIRECTIVES, COMMON_DIRECTIVES} from './directives/index';
|
||||
import {NgLocaleLocalization, NgLocalization} from './localization';
|
||||
import {COMMON_PIPES} from './pipes/index';
|
||||
|
||||
|
||||
// Note: This does not contain the location providers,
|
||||
// as they need some platform specific implementations to work.
|
||||
/**
|
||||
* The module that includes all the basic Angular directives like {@link NgIf}, {@link NgFor}, ...
|
||||
* The module that includes all the basic Angular directives like {@link NgIf}, {@link NgForOf}, ...
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
|
@ -28,3 +29,10 @@ import {COMMON_PIPES} from './pipes/index';
|
|||
})
|
||||
export class CommonModule {
|
||||
}
|
||||
|
||||
/**
|
||||
* A module to contain deprecated directives.
|
||||
*/
|
||||
@NgModule({declarations: [COMMON_DEPRECATED_DIRECTIVES], exports: [COMMON_DEPRECATED_DIRECTIVES]})
|
||||
export class CommonDeprecatedModule {
|
||||
}
|
|
@ -10,7 +10,7 @@ import {Provider} from '@angular/core';
|
|||
|
||||
import {NgClass} from './ng_class';
|
||||
import {NgComponentOutlet} from './ng_component_outlet';
|
||||
import {NgFor} from './ng_for';
|
||||
import {NgFor, NgForOf} from './ng_for_of';
|
||||
import {NgIf} from './ng_if';
|
||||
import {NgPlural, NgPluralCase} from './ng_plural';
|
||||
import {NgStyle} from './ng_style';
|
||||
|
@ -21,6 +21,7 @@ export {
|
|||
NgClass,
|
||||
NgComponentOutlet,
|
||||
NgFor,
|
||||
NgForOf,
|
||||
NgIf,
|
||||
NgPlural,
|
||||
NgPluralCase,
|
||||
|
@ -32,6 +33,7 @@ export {
|
|||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A collection of Angular directives that are likely to be used in each and every Angular
|
||||
* application.
|
||||
|
@ -39,7 +41,7 @@ export {
|
|||
export const COMMON_DIRECTIVES: Provider[] = [
|
||||
NgClass,
|
||||
NgComponentOutlet,
|
||||
NgFor,
|
||||
NgForOf,
|
||||
NgIf,
|
||||
NgTemplateOutlet,
|
||||
NgStyle,
|
||||
|
@ -49,3 +51,8 @@ export const COMMON_DIRECTIVES: Provider[] = [
|
|||
NgPlural,
|
||||
NgPluralCase,
|
||||
];
|
||||
|
||||
/**
|
||||
* A colletion of deprecated directives that are no longer part of the core module.
|
||||
*/
|
||||
export const COMMON_DEPRECATED_DIRECTIVES: Provider[] = [NgFor];
|
|
@ -6,12 +6,12 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ChangeDetectorRef, Directive, DoCheck, EmbeddedViewRef, Input, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef, 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';
|
||||
|
||||
import {getTypeNameForDebugging} from '../facade/lang';
|
||||
|
||||
export class NgForRow {
|
||||
constructor(public $implicit: any, public index: number, public count: number) {}
|
||||
export class NgForOfRow<T> {
|
||||
constructor(public $implicit: T, public index: number, public count: number) {}
|
||||
|
||||
get first(): boolean { return this.index === 0; }
|
||||
|
||||
|
@ -23,9 +23,177 @@ export class NgForRow {
|
|||
}
|
||||
|
||||
/**
|
||||
* The `NgFor` directive instantiates a template once per item from an iterable. The context for
|
||||
* each instantiated template inherits from the outer context with the given loop variable set
|
||||
* to the current item from the iterable.
|
||||
* The `NgForOf` directive instantiates a template once per item from an iterable. The context
|
||||
* for each instantiated template inherits from the outer context with the given loop variable
|
||||
* set to the current item from the iterable.
|
||||
*
|
||||
* ### 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.
|
||||
* * `first` will be set to a boolean value indicating whether the item is the first one in the
|
||||
* iteration.
|
||||
* * `last` will be set to a boolean value indicating whether the item is the last one in the
|
||||
* iteration.
|
||||
* * `even` will be set to a boolean value indicating whether this item has an even index.
|
||||
* * `odd` will be set to a boolean value indicating whether this item has an odd index.
|
||||
*
|
||||
* ### Change Propagation
|
||||
*
|
||||
* When the contents of the iterator changes, `NgForOf` makes the corresponding changes to the DOM:
|
||||
*
|
||||
* * When an item is added, a new instance of the template is added to the DOM.
|
||||
* * When an item is removed, its template instance is removed from the DOM.
|
||||
* * When items are reordered, their respective templates are reordered in the DOM.
|
||||
* * Otherwise, the DOM element for that item will remain the same.
|
||||
*
|
||||
* Angular uses object identity to track insertions and deletions within the iterator and reproduce
|
||||
* those changes in the DOM. This has important implications for animations and any stateful
|
||||
* controls (such as `<input>` elements which accept user input) that are present. Inserted rows can
|
||||
* be animated in, deleted rows can be animated out, and unchanged rows retain any unsaved state
|
||||
* such as user input.
|
||||
*
|
||||
* It is possible for the identities of elements in the iterator to change while the data does not.
|
||||
* This can happen, for example, if the iterator produced from an RPC to the server, and that
|
||||
* RPC is re-run. Even if the data hasn't changed, the second response will produce objects with
|
||||
* different identities, and Angular will tear down the entire DOM and rebuild it (as if all old
|
||||
* elements were deleted and all new elements inserted). This is an expensive operation and should
|
||||
* be avoided if possible.
|
||||
*
|
||||
* To customize the default tracking algorithm, `NgForOf` supports `trackBy` option.
|
||||
* `trackBy` takes a function which has two arguments: `index` and `item`.
|
||||
* If `trackBy` is given, Angular tracks changes by the return value of the function.
|
||||
*
|
||||
* ### Syntax
|
||||
*
|
||||
* - `<li *ngFor="let item of items; let i = index; trackBy: trackByFn">...</li>`
|
||||
* - `<li template="ngFor let item of items; let i = index; trackBy: trackByFn">...</li>`
|
||||
*
|
||||
* With `<template>` element:
|
||||
*
|
||||
* ```
|
||||
* <template ngFor let-item [ngForOf]="items" let-i="index" [ngForTrackBy]="trackByFn">
|
||||
* <li>...</li>
|
||||
* </template>
|
||||
* ```
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* See a [live demo](http://plnkr.co/edit/KVuXxDp0qinGDyo307QW?p=preview) for a more detailed
|
||||
* example.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[ngFor][ngForOf]',
|
||||
providers: [{provide: forwardRef(() => NgFor), useExisting: forwardRef(() => NgForOf)}]
|
||||
})
|
||||
export class NgForOf<T> implements DoCheck,
|
||||
OnChanges {
|
||||
@Input() ngForOf: NgIterable<T>;
|
||||
@Input()
|
||||
set ngForTrackBy(fn: TrackByFunction<T>) {
|
||||
if (isDevMode() && fn != null && typeof fn !== 'function') {
|
||||
// TODO(vicb): use a log service once there is a public one available
|
||||
if (<any>console && <any>console.warn) {
|
||||
console.warn(
|
||||
`trackBy must be a function, but received ${JSON.stringify(fn)}. ` +
|
||||
`See https://angular.io/docs/ts/latest/api/common/index/NgFor-directive.html#!#change-propagation for more information.`);
|
||||
}
|
||||
}
|
||||
this._trackByFn = fn;
|
||||
}
|
||||
|
||||
get ngForTrackBy(): TrackByFunction<T> { return this._trackByFn; }
|
||||
|
||||
private _differ: IterableDiffer<T> = null;
|
||||
private _trackByFn: TrackByFunction<T>;
|
||||
|
||||
constructor(
|
||||
private _viewContainer: ViewContainerRef, private _template: TemplateRef<NgForOfRow<T>>,
|
||||
private _differs: IterableDiffers, private _cdr: ChangeDetectorRef) {}
|
||||
|
||||
@Input()
|
||||
set ngForTemplate(value: TemplateRef<NgForOfRow<T>>) {
|
||||
// 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,
|
||||
// should be acceptable.
|
||||
if (value) {
|
||||
this._template = value;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if ('ngForOf' in changes) {
|
||||
// React on ngForOf changes only once all inputs have been initialized
|
||||
const value = changes['ngForOf'].currentValue;
|
||||
if (!this._differ && value) {
|
||||
try {
|
||||
this._differ = this._differs.find(value).create(this._cdr, this.ngForTrackBy);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Cannot find a differ supporting object '${value}' of type '${getTypeNameForDebugging(value)}'. NgFor only supports binding to Iterables such as Arrays.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngDoCheck(): void {
|
||||
if (this._differ) {
|
||||
const changes = this._differ.diff(this.ngForOf);
|
||||
if (changes) this._applyChanges(changes);
|
||||
}
|
||||
}
|
||||
|
||||
private _applyChanges(changes: IterableChanges<T>) {
|
||||
const insertTuples: RecordViewTuple<T>[] = [];
|
||||
changes.forEachOperation(
|
||||
(item: IterableChangeRecord<any>, adjustedPreviousIndex: number, currentIndex: number) => {
|
||||
if (item.previousIndex == null) {
|
||||
const view = this._viewContainer.createEmbeddedView(
|
||||
this._template, new NgForOfRow(null, null, null), currentIndex);
|
||||
const tuple = new RecordViewTuple(item, view);
|
||||
insertTuples.push(tuple);
|
||||
} else if (currentIndex == null) {
|
||||
this._viewContainer.remove(adjustedPreviousIndex);
|
||||
} else {
|
||||
const view = this._viewContainer.get(adjustedPreviousIndex);
|
||||
this._viewContainer.move(view, currentIndex);
|
||||
const tuple = new RecordViewTuple(item, <EmbeddedViewRef<NgForOfRow<T>>>view);
|
||||
insertTuples.push(tuple);
|
||||
}
|
||||
});
|
||||
|
||||
for (let i = 0; i < insertTuples.length; i++) {
|
||||
this._perViewChange(insertTuples[i].view, insertTuples[i].record);
|
||||
}
|
||||
|
||||
for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
||||
const viewRef = <EmbeddedViewRef<NgForOfRow<T>>>this._viewContainer.get(i);
|
||||
viewRef.context.index = i;
|
||||
viewRef.context.count = ilen;
|
||||
}
|
||||
|
||||
changes.forEachIdentityChange((record: any) => {
|
||||
const viewRef = <EmbeddedViewRef<NgForOfRow<T>>>this._viewContainer.get(record.currentIndex);
|
||||
viewRef.context.$implicit = record.item;
|
||||
});
|
||||
}
|
||||
|
||||
private _perViewChange(view: EmbeddedViewRef<NgForOfRow<T>>, record: IterableChangeRecord<any>) {
|
||||
view.context.$implicit = record.item;
|
||||
}
|
||||
}
|
||||
|
||||
class RecordViewTuple<T> {
|
||||
constructor(public record: any, public view: EmbeddedViewRef<NgForOfRow<T>>) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `NgFor` directive instantiates a template once per item from an iterable. The context
|
||||
* for each instantiated template inherits from the outer context with the given loop variable
|
||||
* set to the current item from the iterable.
|
||||
*
|
||||
* ### Local Variables
|
||||
*
|
||||
|
@ -50,10 +218,9 @@ export class NgForRow {
|
|||
*
|
||||
* Angular uses object identity to track insertions and deletions within the iterator and reproduce
|
||||
* those changes in the DOM. This has important implications for animations and any stateful
|
||||
* controls
|
||||
* (such as `<input>` elements which accept user input) that are present. Inserted rows can be
|
||||
* animated in, deleted rows can be animated out, and unchanged rows retain any unsaved state such
|
||||
* as user input.
|
||||
* controls (such as `<input>` elements which accept user input) that are present. Inserted rows can
|
||||
* be animated in, deleted rows can be animated out, and unchanged rows retain any unsaved state
|
||||
* such as user input.
|
||||
*
|
||||
* It is possible for the identities of elements in the iterator to change while the data does not.
|
||||
* This can happen, for example, if the iterator produced from an RPC to the server, and that
|
||||
|
@ -84,102 +251,6 @@ export class NgForRow {
|
|||
* See a [live demo](http://plnkr.co/edit/KVuXxDp0qinGDyo307QW?p=preview) for a more detailed
|
||||
* example.
|
||||
*
|
||||
* @stable
|
||||
* @deprecated v4.0.0 - Use `NgForOf<T>` instead.
|
||||
*/
|
||||
@Directive({selector: '[ngFor][ngForOf]'})
|
||||
export class NgFor implements DoCheck, OnChanges {
|
||||
@Input() ngForOf: any;
|
||||
@Input()
|
||||
set ngForTrackBy(fn: TrackByFn) {
|
||||
if (isDevMode() && fn != null && typeof fn !== 'function') {
|
||||
// TODO(vicb): use a log service once there is a public one available
|
||||
if (<any>console && <any>console.warn) {
|
||||
console.warn(
|
||||
`trackBy must be a function, but received ${JSON.stringify(fn)}. ` +
|
||||
`See https://angular.io/docs/ts/latest/api/common/index/NgFor-directive.html#!#change-propagation for more information.`);
|
||||
}
|
||||
}
|
||||
this._trackByFn = fn;
|
||||
}
|
||||
|
||||
get ngForTrackBy(): TrackByFn { return this._trackByFn; }
|
||||
|
||||
private _differ: IterableDiffer<any> = null;
|
||||
private _trackByFn: TrackByFn;
|
||||
|
||||
constructor(
|
||||
private _viewContainer: ViewContainerRef, private _template: TemplateRef<NgForRow>,
|
||||
private _differs: IterableDiffers, private _cdr: ChangeDetectorRef) {}
|
||||
|
||||
@Input()
|
||||
set ngForTemplate(value: TemplateRef<NgForRow>) {
|
||||
if (value) {
|
||||
this._template = value;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if ('ngForOf' in changes) {
|
||||
// React on ngForOf changes only once all inputs have been initialized
|
||||
const value = changes['ngForOf'].currentValue;
|
||||
if (!this._differ && value) {
|
||||
try {
|
||||
this._differ = this._differs.find(value).create(this._cdr, this.ngForTrackBy);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Cannot find a differ supporting object '${value}' of type '${getTypeNameForDebugging(value)}'. NgFor only supports binding to Iterables such as Arrays.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngDoCheck(): void {
|
||||
if (this._differ) {
|
||||
const changes = this._differ.diff(this.ngForOf);
|
||||
if (changes) this._applyChanges(changes);
|
||||
}
|
||||
}
|
||||
|
||||
private _applyChanges(changes: IterableChanges<any>) {
|
||||
const insertTuples: RecordViewTuple[] = [];
|
||||
changes.forEachOperation(
|
||||
(item: IterableChangeRecord<any>, adjustedPreviousIndex: number, currentIndex: number) => {
|
||||
if (item.previousIndex == null) {
|
||||
const view = this._viewContainer.createEmbeddedView(
|
||||
this._template, new NgForRow(null, null, null), currentIndex);
|
||||
const tuple = new RecordViewTuple(item, view);
|
||||
insertTuples.push(tuple);
|
||||
} else if (currentIndex == null) {
|
||||
this._viewContainer.remove(adjustedPreviousIndex);
|
||||
} else {
|
||||
const view = this._viewContainer.get(adjustedPreviousIndex);
|
||||
this._viewContainer.move(view, currentIndex);
|
||||
const tuple = new RecordViewTuple(item, <EmbeddedViewRef<NgForRow>>view);
|
||||
insertTuples.push(tuple);
|
||||
}
|
||||
});
|
||||
|
||||
for (let i = 0; i < insertTuples.length; i++) {
|
||||
this._perViewChange(insertTuples[i].view, insertTuples[i].record);
|
||||
}
|
||||
|
||||
for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
||||
const viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
|
||||
viewRef.context.index = i;
|
||||
viewRef.context.count = ilen;
|
||||
}
|
||||
|
||||
changes.forEachIdentityChange((record: any) => {
|
||||
const viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
|
||||
viewRef.context.$implicit = record.item;
|
||||
});
|
||||
}
|
||||
|
||||
private _perViewChange(view: EmbeddedViewRef<NgForRow>, record: IterableChangeRecord<any>) {
|
||||
view.context.$implicit = record.item;
|
||||
}
|
||||
}
|
||||
|
||||
class RecordViewTuple {
|
||||
constructor(public record: any, public view: EmbeddedViewRef<NgForRow>) {}
|
||||
}
|
||||
export class NgFor extends NgForOf<any> {}
|
|
@ -12,4 +12,4 @@
|
|||
* Change detection enables data binding in Angular.
|
||||
*/
|
||||
|
||||
export {ChangeDetectionStrategy, ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, IterableDiffers, KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFactory, KeyValueDiffers, PipeTransform, SimpleChange, SimpleChanges, TrackByFn, WrappedValue} from './change_detection/change_detection';
|
||||
export {ChangeDetectionStrategy, ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, IterableDiffers, KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFactory, KeyValueDiffers, NgIterable, PipeTransform, SimpleChange, SimpleChanges, TrackByFn, TrackByFunction, WrappedValue} from './change_detection/change_detection';
|
||||
|
|
|
@ -18,7 +18,7 @@ export {ChangeDetectionStrategy, ChangeDetectorStatus, isDefaultChangeDetectionS
|
|||
export {DefaultIterableDifferFactory} from './differs/default_iterable_differ';
|
||||
export {DefaultIterableDiffer} from './differs/default_iterable_differ';
|
||||
export {DefaultKeyValueDifferFactory} from './differs/default_keyvalue_differ';
|
||||
export {CollectionChangeRecord, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, IterableDiffers, TrackByFn} from './differs/iterable_differs';
|
||||
export {CollectionChangeRecord, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, IterableDiffers, NgIterable, TrackByFn, TrackByFunction} from './differs/iterable_differs';
|
||||
export {KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFactory, KeyValueDiffers} from './differs/keyvalue_differs';
|
||||
export {PipeTransform} from './pipe_transform';
|
||||
|
||||
|
|
|
@ -10,13 +10,13 @@ import {isListLikeIterable, iterateListLike} from '../../facade/collection';
|
|||
import {isBlank, looseIdentical, stringify} from '../../facade/lang';
|
||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||
|
||||
import {IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, TrackByFn} from './iterable_differs';
|
||||
import {IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, NgIterable, TrackByFunction} from './iterable_differs';
|
||||
|
||||
|
||||
export class DefaultIterableDifferFactory implements IterableDifferFactory {
|
||||
constructor() {}
|
||||
supports(obj: Object): boolean { return isListLikeIterable(obj); }
|
||||
create<V>(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): DefaultIterableDiffer<V> {
|
||||
create<V>(cdRef: ChangeDetectorRef, trackByFn?: TrackByFunction<any>): DefaultIterableDiffer<V> {
|
||||
return new DefaultIterableDiffer<V>(trackByFn);
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ const trackByIdentity = (index: number, item: any) => item;
|
|||
*/
|
||||
export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChanges<V> {
|
||||
private _length: number = null;
|
||||
private _collection: V[]|Set<V>[]|any /* |Iterable<V> */ = null;
|
||||
private _collection: NgIterable<V> = null;
|
||||
// Keeps track of the used records at any point in time (during & across `_check()` calls)
|
||||
private _linkedRecords: _DuplicateMap<V> = null;
|
||||
// Keeps track of the removed records at any point in time during `_check()` calls.
|
||||
|
@ -46,7 +46,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
|||
private _identityChangesHead: IterableChangeRecord_<V> = null;
|
||||
private _identityChangesTail: IterableChangeRecord_<V> = null;
|
||||
|
||||
constructor(private _trackByFn?: TrackByFn) {
|
||||
constructor(private _trackByFn?: TrackByFunction<V>) {
|
||||
this._trackByFn = this._trackByFn || trackByIdentity;
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
|||
}
|
||||
}
|
||||
|
||||
diff(collection: V[]|Set<V>[]|any /* |Iterable<V> */): DefaultIterableDiffer<V> {
|
||||
diff(collection: NgIterable<V>): DefaultIterableDiffer<V> {
|
||||
if (isBlank(collection)) collection = [];
|
||||
if (!isListLikeIterable(collection)) {
|
||||
throw new Error(`Error trying to diff '${collection}'`);
|
||||
|
@ -162,7 +162,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
|||
onDestroy() {}
|
||||
|
||||
// todo(vicb): optim for UnmodifiableListView (frozen arrays)
|
||||
check(collection: V[]|Set<V>[]|any /* |Iterable<V> */): boolean {
|
||||
check(collection: NgIterable<V>): boolean {
|
||||
this._reset();
|
||||
|
||||
let record: IterableChangeRecord_<V> = this._itHead;
|
||||
|
@ -171,11 +171,10 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
|
|||
let item: V;
|
||||
let itemTrackBy: any;
|
||||
if (Array.isArray(collection)) {
|
||||
const list = collection as V[];
|
||||
this._length = list.length;
|
||||
this._length = collection.length;
|
||||
|
||||
for (let index = 0; index < this._length; index++) {
|
||||
item = list[index];
|
||||
item = collection[index];
|
||||
itemTrackBy = this._trackByFn(index, item);
|
||||
if (record === null || !looseIdentical(record.trackById, itemTrackBy)) {
|
||||
record = this._mismatch(record, item, itemTrackBy, index);
|
||||
|
|
|
@ -10,6 +10,12 @@ import {Optional, Provider, SkipSelf} from '../../di';
|
|||
import {getTypeNameForDebugging, isPresent} from '../../facade/lang';
|
||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||
|
||||
/**
|
||||
* A type describing supported interable types.
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export type NgIterable<T> = Array<T>| Iterable<T>;
|
||||
|
||||
/**
|
||||
* A strategy for tracking changes over time to an iterable. Used by {@link NgFor} to
|
||||
|
@ -25,9 +31,7 @@ export interface IterableDiffer<V> {
|
|||
* @returns an object describing the difference. The return value is only valid until the next
|
||||
* `diff()` invocation.
|
||||
*/
|
||||
diff(object: V[]|Set<V>|any /* |Iterable<V> */): IterableChanges<V>;
|
||||
// TODO(misko): We can't use Iterable as it would break the .d.ts file for ES5 users.
|
||||
// Iterable is not apart of @types/es6-collections since it requires Symbol.
|
||||
diff(object: NgIterable<V>): IterableChanges<V>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -112,13 +116,19 @@ export interface CollectionChangeRecord<V> extends IterableChangeRecord<V> {}
|
|||
|
||||
|
||||
/**
|
||||
* An optional function passed into {@link NgFor} that defines how to track
|
||||
* items in an iterable (e.g. by index or id)
|
||||
* Nolonger used.
|
||||
*
|
||||
* @stable
|
||||
* @deprecated v4.0.0 - Use TrackByFunction instead
|
||||
*/
|
||||
export interface TrackByFn { (index: number, item: any): any; }
|
||||
|
||||
/**
|
||||
* An optional function passed into {@link NgForOf} that defines how to track
|
||||
* items in an iterable (e.g. fby index or id)
|
||||
*
|
||||
* @stable
|
||||
*/
|
||||
export interface TrackByFunction<T> { (index: number, item: T): any; }
|
||||
|
||||
/**
|
||||
* Provides a factory for {@link IterableDiffer}.
|
||||
|
@ -127,7 +137,7 @@ export interface TrackByFn { (index: number, item: any): any; }
|
|||
*/
|
||||
export interface IterableDifferFactory {
|
||||
supports(objects: any): boolean;
|
||||
create<V>(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): IterableDiffer<V>;
|
||||
create<V>(cdRef: ChangeDetectorRef, trackByFn?: TrackByFunction<V>): IterableDiffer<V>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -728,7 +728,8 @@ function getVarDeclarations(info: TemplateInfo, path: TemplateAstPath): SymbolDe
|
|||
const value = context.get(variable.value);
|
||||
if (value) {
|
||||
type = value.type;
|
||||
if (info.template.query.getTypeKind(type) === BuiltinType.Any) {
|
||||
let kind = info.template.query.getTypeKind(type);
|
||||
if (kind === BuiltinType.Any || kind == BuiltinType.Unbound) {
|
||||
// The any type is not very useful here. For special cases, such as ngFor, we can do
|
||||
// better.
|
||||
type = refinedVariableType(type, info, current);
|
||||
|
@ -753,8 +754,10 @@ function getVarDeclarations(info: TemplateInfo, path: TemplateAstPath): SymbolDe
|
|||
function refinedVariableType(
|
||||
type: Symbol, info: TemplateInfo, templateElement: EmbeddedTemplateAst): Symbol {
|
||||
// Special case the ngFor directive
|
||||
const ngForDirective =
|
||||
templateElement.directives.find(d => identifierName(d.directive.type) == 'NgFor');
|
||||
const ngForDirective = templateElement.directives.find(d => {
|
||||
const name = identifierName(d.directive.type);
|
||||
return name == 'NgFor' || name == 'NgForOf';
|
||||
});
|
||||
if (ngForDirective) {
|
||||
const ngForOfBinding = ngForDirective.inputs.find(i => i.directiveName == 'ngForOf');
|
||||
if (ngForOfBinding) {
|
||||
|
|
|
@ -189,6 +189,11 @@ export enum BuiltinType {
|
|||
*/
|
||||
Null,
|
||||
|
||||
/**
|
||||
* the type is an unbound type parameter.
|
||||
*/
|
||||
Unbound,
|
||||
|
||||
/**
|
||||
* Not a built-in type.
|
||||
*/
|
||||
|
|
|
@ -1263,6 +1263,8 @@ function typeKindOf(type: ts.Type): BuiltinType {
|
|||
}
|
||||
}
|
||||
return candidate;
|
||||
} else if (type.flags & ts.TypeFlags.TypeParameter) {
|
||||
return BuiltinType.Unbound;
|
||||
}
|
||||
}
|
||||
return BuiltinType.Other;
|
||||
|
|
|
@ -128,12 +128,16 @@ export declare class NgComponentOutlet implements OnChanges, OnDestroy {
|
|||
ngOnDestroy(): void;
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
export declare class NgFor extends NgForOf<any> {
|
||||
}
|
||||
|
||||
/** @stable */
|
||||
export declare class NgFor implements DoCheck, OnChanges {
|
||||
ngForOf: any;
|
||||
ngForTemplate: TemplateRef<NgForRow>;
|
||||
ngForTrackBy: TrackByFn;
|
||||
constructor(_viewContainer: ViewContainerRef, _template: TemplateRef<NgForRow>, _differs: IterableDiffers, _cdr: ChangeDetectorRef);
|
||||
export declare class NgForOf<T> implements DoCheck, OnChanges {
|
||||
ngForOf: NgIterable<T>;
|
||||
ngForTemplate: TemplateRef<NgForOfRow<T>>;
|
||||
ngForTrackBy: TrackByFunction<T>;
|
||||
constructor(_viewContainer: ViewContainerRef, _template: TemplateRef<NgForOfRow<T>>, _differs: IterableDiffers, _cdr: ChangeDetectorRef);
|
||||
ngDoCheck(): void;
|
||||
ngOnChanges(changes: SimpleChanges): void;
|
||||
}
|
||||
|
|
|
@ -378,12 +378,12 @@ export declare class DebugNode {
|
|||
|
||||
/** @deprecated */
|
||||
export declare class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChanges<V> {
|
||||
collection: any;
|
||||
collection: NgIterable<V>;
|
||||
isDirty: boolean;
|
||||
length: number;
|
||||
constructor(_trackByFn?: TrackByFn);
|
||||
check(collection: V[] | Set<V>[] | any): boolean;
|
||||
diff(collection: V[] | Set<V>[] | any): DefaultIterableDiffer<V>;
|
||||
constructor(_trackByFn?: TrackByFunction<V>);
|
||||
check(collection: NgIterable<V>): boolean;
|
||||
diff(collection: NgIterable<V>): DefaultIterableDiffer<V>;
|
||||
forEachAddedItem(fn: (record: IterableChangeRecord_<V>) => void): void;
|
||||
forEachIdentityChange(fn: (record: IterableChangeRecord_<V>) => void): void;
|
||||
forEachItem(fn: (record: IterableChangeRecord_<V>) => void): void;
|
||||
|
@ -556,12 +556,12 @@ export interface IterableChanges<V> {
|
|||
|
||||
/** @stable */
|
||||
export interface IterableDiffer<V> {
|
||||
diff(object: V[] | Set<V> | any): IterableChanges<V>;
|
||||
diff(object: NgIterable<V>): IterableChanges<V>;
|
||||
}
|
||||
|
||||
/** @stable */
|
||||
export interface IterableDifferFactory {
|
||||
create<V>(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): IterableDiffer<V>;
|
||||
create<V>(cdRef: ChangeDetectorRef, trackByFn?: TrackByFunction<V>): IterableDiffer<V>;
|
||||
supports(objects: any): boolean;
|
||||
}
|
||||
|
||||
|
@ -639,6 +639,9 @@ export interface ModuleWithProviders {
|
|||
providers?: Provider[];
|
||||
}
|
||||
|
||||
/** @stable */
|
||||
export declare type NgIterable<T> = Array<T> | Iterable<T>;
|
||||
|
||||
/** @stable */
|
||||
export declare const NgModule: NgModuleDecorator;
|
||||
|
||||
|
@ -976,11 +979,16 @@ export declare class TestabilityRegistry {
|
|||
registerApplication(token: any, testability: Testability): void;
|
||||
}
|
||||
|
||||
/** @stable */
|
||||
/** @deprecated */
|
||||
export interface TrackByFn {
|
||||
(index: number, item: any): any;
|
||||
}
|
||||
|
||||
/** @stable */
|
||||
export interface TrackByFunction<T> {
|
||||
(index: number, item: T): any;
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare function transition(stateChangeExpr: string | ((fromState: string, toState: string) => boolean), steps: AnimationMetadata | AnimationMetadata[]): AnimationStateTransitionMetadata;
|
||||
|
||||
|
|
Loading…
Reference in New Issue