Skip to content

Commit

Permalink
feat(common): rename underlying NgFor class and add a type parameter (
Browse files Browse the repository at this point in the history
angular#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 angular#12398
  • Loading branch information
chuckjaz committed Jan 30, 2017
1 parent 6cca812 commit 3a4b5e1
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 53 deletions.
2 changes: 1 addition & 1 deletion modules/@angular/common/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
12 changes: 10 additions & 2 deletions modules/@angular/common/src/common_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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 {
}
11 changes: 9 additions & 2 deletions modules/@angular/common/src/directives/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -21,6 +21,7 @@ export {
NgClass,
NgComponentOutlet,
NgFor,
NgForOf,
NgIf,
NgPlural,
NgPluralCase,
Expand All @@ -32,14 +33,15 @@ export {
};



/**
* A collection of Angular directives that are likely to be used in each and every Angular
* application.
*/
export const COMMON_DIRECTIVES: Provider[] = [
NgClass,
NgComponentOutlet,
NgFor,
NgForOf,
NgIf,
NgTemplateOutlet,
NgStyle,
Expand All @@ -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];
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand All @@ -23,13 +23,13 @@ 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
*
* `NgFor` 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.
* * `first` will be set to a boolean value indicating whether the item is the first one in the
Expand All @@ -41,7 +41,7 @@ export class NgForRow {
*
* ### Change Propagation
*
* When the contents of the iterator changes, `NgFor` makes the corresponding changes to the DOM:
* 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.
Expand All @@ -50,10 +50,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
Expand All @@ -62,7 +61,7 @@ export class NgForRow {
* 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, `NgFor` supports `trackBy` option.
* 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.
*
Expand All @@ -86,11 +85,15 @@ export class NgForRow {
*
* @stable
*/
@Directive({selector: '[ngFor][ngForOf]'})
export class NgFor implements DoCheck, OnChanges {
@Input() ngForOf: any;
@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: TrackByFn) {
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) {
Expand All @@ -102,17 +105,17 @@ export class NgFor implements DoCheck, OnChanges {
this._trackByFn = fn;
}

get ngForTrackBy(): TrackByFn { return this._trackByFn; }
get ngForTrackBy(): TrackByFunction<T> { return this._trackByFn; }

private _differ: IterableDiffer<any> = null;
private _trackByFn: TrackByFn;
private _differ: IterableDiffer<T> = null;
private _trackByFn: TrackByFunction<T>;

constructor(
private _viewContainer: ViewContainerRef, private _template: TemplateRef<NgForRow>,
private _viewContainer: ViewContainerRef, private _template: TemplateRef<NgForOfRow<T>>,
private _differs: IterableDiffers, private _cdr: ChangeDetectorRef) {}

@Input()
set ngForTemplate(value: TemplateRef<NgForRow>) {
set ngForTemplate(value: TemplateRef<NgForOfRow<T>>) { // TODO: TemplateRef<Partial<NgForRow<T>>>
if (value) {
this._template = value;
}
Expand Down Expand Up @@ -140,21 +143,21 @@ export class NgFor implements DoCheck, OnChanges {
}
}

private _applyChanges(changes: IterableChanges<any>) {
const insertTuples: RecordViewTuple[] = [];
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 NgForRow(null, null, null), currentIndex);
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<NgForRow>>view);
const tuple = new RecordViewTuple(item, <EmbeddedViewRef<NgForOfRow<T>>>view);
insertTuples.push(tuple);
}
});
Expand All @@ -164,22 +167,87 @@ export class NgFor implements DoCheck, OnChanges {
}

for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
const viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(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<NgForRow>>this._viewContainer.get(record.currentIndex);
const viewRef = <EmbeddedViewRef<NgForOfRow<T>>>this._viewContainer.get(record.currentIndex);
viewRef.context.$implicit = record.item;
});
}

private _perViewChange(view: EmbeddedViewRef<NgForRow>, record: IterableChangeRecord<any>) {
private _perViewChange(view: EmbeddedViewRef<NgForOfRow<T>>, record: IterableChangeRecord<any>) {
view.context.$implicit = record.item;
}
}

class RecordViewTuple {
constructor(public record: any, public view: EmbeddedViewRef<NgForRow>) {}
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
*
* `NgFor` 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, `NgFor` 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, `NgFor` 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.
*
* @deprecated Use `NgForOf<T>` instead.
*/
export class NgFor extends NgForOf<any> {}
2 changes: 1 addition & 1 deletion modules/@angular/core/src/change_detection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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, 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);
}
}
Expand Down Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -112,13 +118,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}.
Expand All @@ -127,7 +139,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>;
}

/**
Expand Down
Loading

0 comments on commit 3a4b5e1

Please sign in to comment.