From 8c7e93bebeb4a25c44f4239fec211d1c061b4aaf Mon Sep 17 00:00:00 2001 From: Joao Dias Date: Thu, 27 Oct 2016 20:28:11 +0200 Subject: [PATCH] fix(core): Add type information to differs CHANGES: - Remove unused `onDestroy` method on the `KeyValueDiffer` and `IterableDiffer`. DEPRECATION: - `CollectionChangeRecord` is renamed to `IterableChangeRecord`. `CollectionChangeRecord` is aliased to `IterableChangeRecord` and is marked as `@deprecated`. It will be removed in `v5.x.x`. - Deprecate `DefaultIterableDiffer` as it is private class which was erroneously exposed. - Deprecate `KeyValueDiffers#factories` as it is private field which was erroneously exposed. - Deprecate `IterableDiffers#factories` as it is private field which was erroneously exposed. BREAKING CHANGE: - `IterableChangeRecord` is now an interface and parameterized on ``. This should not be an issue unless your code does `new IterableChangeRecord` which it should not have a reason to do. - `KeyValueChangeRecord` is now an interface and parameterized on ``. This should not be an issue unless your code does `new IterableChangeRecord` which it should not have a reason to do. Original PR #12570 Fixes #13382 --- modules/@angular/common/src/common.ts | 1 - .../common/src/directives/ng_class.ts | 45 ++-- .../@angular/common/src/directives/ng_for.ts | 10 +- .../common/src/directives/ng_style.ts | 22 +- modules/@angular/core/src/change_detection.ts | 2 +- .../src/change_detection/change_detection.ts | 14 +- .../differs/default_iterable_differ.ts | 233 +++++++++--------- .../differs/default_keyvalue_differ.ts | 106 ++++---- .../differs/iterable_differs.ts | 109 +++++++- .../differs/keyvalue_differs.ts | 118 +++++++-- .../differs/default_keyvalue_differ_spec.ts | 4 +- tools/public_api_guard/core/index.d.ts | 97 +++++--- 12 files changed, 471 insertions(+), 290 deletions(-) diff --git a/modules/@angular/common/src/common.ts b/modules/@angular/common/src/common.ts index 74cd089c05..19e1680a2d 100644 --- a/modules/@angular/common/src/common.ts +++ b/modules/@angular/common/src/common.ts @@ -17,4 +17,3 @@ export {CommonModule} from './common_module'; export {NgClass, NgFor, 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'; -export {Version} from '@angular/core'; diff --git a/modules/@angular/common/src/directives/ng_class.ts b/modules/@angular/common/src/directives/ng_class.ts index 3d079bf0a6..24431cb592 100644 --- a/modules/@angular/common/src/directives/ng_class.ts +++ b/modules/@angular/common/src/directives/ng_class.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core'; +import {Directive, DoCheck, ElementRef, Input, IterableChanges, IterableDiffer, IterableDiffers, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core'; import {isListLikeIterable} from '../facade/collection'; import {isPresent, stringify} from '../facade/lang'; @@ -41,8 +41,8 @@ import {isPresent, stringify} from '../facade/lang'; */ @Directive({selector: '[ngClass]'}) export class NgClass implements DoCheck { - private _iterableDiffer: IterableDiffer; - private _keyValueDiffer: KeyValueDiffer; + private _iterableDiffer: IterableDiffer; + private _keyValueDiffer: KeyValueDiffer; private _initialClasses: string[] = []; private _rawClass: string[]|Set|{[klass: string]: any}; @@ -78,39 +78,35 @@ export class NgClass implements DoCheck { ngDoCheck(): void { if (this._iterableDiffer) { - const changes = this._iterableDiffer.diff(this._rawClass); - if (changes) { - this._applyIterableChanges(changes); + const iterableChanges = this._iterableDiffer.diff(this._rawClass as string[]); + if (iterableChanges) { + this._applyIterableChanges(iterableChanges); } } else if (this._keyValueDiffer) { - const changes = this._keyValueDiffer.diff(this._rawClass); - if (changes) { - this._applyKeyValueChanges(changes); + const keyValueChanges = this._keyValueDiffer.diff(this._rawClass as{[k: string]: any}); + if (keyValueChanges) { + this._applyKeyValueChanges(keyValueChanges); } } } - private _cleanupClasses(rawClassVal: string[]|Set|{[klass: string]: any}): void { + private _cleanupClasses(rawClassVal: string[]|{[klass: string]: any}): void { this._applyClasses(rawClassVal, true); this._applyInitialClasses(false); } - private _applyKeyValueChanges(changes: any): void { - changes.forEachAddedItem( - (record: KeyValueChangeRecord) => this._toggleClass(record.key, record.currentValue)); - - changes.forEachChangedItem( - (record: KeyValueChangeRecord) => this._toggleClass(record.key, record.currentValue)); - - changes.forEachRemovedItem((record: KeyValueChangeRecord) => { + private _applyKeyValueChanges(changes: KeyValueChanges): void { + changes.forEachAddedItem((record) => this._toggleClass(record.key, record.currentValue)); + changes.forEachChangedItem((record) => this._toggleClass(record.key, record.currentValue)); + changes.forEachRemovedItem((record) => { if (record.previousValue) { this._toggleClass(record.key, false); } }); } - private _applyIterableChanges(changes: any): void { - changes.forEachAddedItem((record: CollectionChangeRecord) => { + private _applyIterableChanges(changes: IterableChanges): void { + changes.forEachAddedItem((record) => { if (typeof record.item === 'string') { this._toggleClass(record.item, true); } else { @@ -119,8 +115,7 @@ export class NgClass implements DoCheck { } }); - changes.forEachRemovedItem( - (record: CollectionChangeRecord) => this._toggleClass(record.item, false)); + changes.forEachRemovedItem((record) => this._toggleClass(record.item, false)); } private _applyInitialClasses(isCleanup: boolean) { @@ -128,7 +123,7 @@ export class NgClass implements DoCheck { } private _applyClasses( - rawClassVal: string[]|Set|{[key: string]: any}, isCleanup: boolean) { + rawClassVal: string[]|Set|{[klass: string]: any}, isCleanup: boolean) { if (rawClassVal) { if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) { (rawClassVal).forEach((klass: string) => this._toggleClass(klass, !isCleanup)); @@ -140,11 +135,11 @@ export class NgClass implements DoCheck { } } - private _toggleClass(klass: string, enabled: boolean): void { + private _toggleClass(klass: string, enabled: any): void { klass = klass.trim(); if (klass) { klass.split(/\s+/g).forEach( - klass => { this._renderer.setElementClass(this._ngEl.nativeElement, klass, enabled); }); + klass => { this._renderer.setElementClass(this._ngEl.nativeElement, klass, !!enabled); }); } } } diff --git a/modules/@angular/common/src/directives/ng_for.ts b/modules/@angular/common/src/directives/ng_for.ts index 0f837403eb..8624f0a92c 100644 --- a/modules/@angular/common/src/directives/ng_for.ts +++ b/modules/@angular/common/src/directives/ng_for.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, Directive, DoCheck, EmbeddedViewRef, Input, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef, isDevMode} from '@angular/core'; +import {ChangeDetectorRef, Directive, DoCheck, EmbeddedViewRef, Input, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef, isDevMode} from '@angular/core'; import {getTypeNameForDebugging} from '../facade/lang'; @@ -104,7 +104,7 @@ export class NgFor implements DoCheck, OnChanges { get ngForTrackBy(): TrackByFn { return this._trackByFn; } - private _differ: IterableDiffer = null; + private _differ: IterableDiffer = null; private _trackByFn: TrackByFn; constructor( @@ -140,10 +140,10 @@ export class NgFor implements DoCheck, OnChanges { } } - private _applyChanges(changes: DefaultIterableDiffer) { + private _applyChanges(changes: IterableChanges) { const insertTuples: RecordViewTuple[] = []; changes.forEachOperation( - (item: CollectionChangeRecord, adjustedPreviousIndex: number, currentIndex: number) => { + (item: IterableChangeRecord, adjustedPreviousIndex: number, currentIndex: number) => { if (item.previousIndex == null) { const view = this._viewContainer.createEmbeddedView( this._template, new NgForRow(null, null, null), currentIndex); @@ -175,7 +175,7 @@ export class NgFor implements DoCheck, OnChanges { }); } - private _perViewChange(view: EmbeddedViewRef, record: CollectionChangeRecord) { + private _perViewChange(view: EmbeddedViewRef, record: IterableChangeRecord) { view.context.$implicit = record.item; } } diff --git a/modules/@angular/common/src/directives/ng_style.ts b/modules/@angular/common/src/directives/ng_style.ts index ed1a21d12b..7949751177 100644 --- a/modules/@angular/common/src/directives/ng_style.ts +++ b/modules/@angular/common/src/directives/ng_style.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core'; +import {Directive, DoCheck, ElementRef, Input, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core'; /** * @ngModule CommonModule @@ -33,7 +33,7 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDif @Directive({selector: '[ngStyle]'}) export class NgStyle implements DoCheck { private _ngStyle: {[key: string]: string}; - private _differ: KeyValueDiffer; + private _differ: KeyValueDiffer; constructor( private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {} @@ -55,20 +55,16 @@ export class NgStyle implements DoCheck { } } - private _applyChanges(changes: any): void { - changes.forEachRemovedItem((record: KeyValueChangeRecord) => this._setStyle(record.key, null)); - - changes.forEachAddedItem( - (record: KeyValueChangeRecord) => this._setStyle(record.key, record.currentValue)); - - changes.forEachChangedItem( - (record: KeyValueChangeRecord) => this._setStyle(record.key, record.currentValue)); + private _applyChanges(changes: KeyValueChanges): void { + changes.forEachRemovedItem((record) => this._setStyle(record.key, null)); + changes.forEachAddedItem((record) => this._setStyle(record.key, record.currentValue)); + changes.forEachChangedItem((record) => this._setStyle(record.key, record.currentValue)); } - private _setStyle(nameAndUnit: string, value: string): void { + private _setStyle(nameAndUnit: string, value: string|number): void { const [name, unit] = nameAndUnit.split('.'); - value = value && unit ? `${value}${unit}` : value; + value = value != null && unit ? `${value}${unit}` : value; - this._renderer.setElementStyle(this._ngEl.nativeElement, name, value); + this._renderer.setElementStyle(this._ngEl.nativeElement, name, value as string); } } diff --git a/modules/@angular/core/src/change_detection.ts b/modules/@angular/core/src/change_detection.ts index eb69cbe908..95a2fdc10e 100644 --- a/modules/@angular/core/src/change_detection.ts +++ b/modules/@angular/core/src/change_detection.ts @@ -12,4 +12,4 @@ * Change detection enables data binding in Angular. */ -export {ChangeDetectionStrategy, ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, IterableDiffer, IterableDifferFactory, IterableDiffers, KeyValueChangeRecord, 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, PipeTransform, SimpleChange, SimpleChanges, TrackByFn, WrappedValue} from './change_detection/change_detection'; diff --git a/modules/@angular/core/src/change_detection/change_detection.ts b/modules/@angular/core/src/change_detection/change_detection.ts index 506111d38c..887bc9a14d 100644 --- a/modules/@angular/core/src/change_detection/change_detection.ts +++ b/modules/@angular/core/src/change_detection/change_detection.ts @@ -7,7 +7,7 @@ */ import {DefaultIterableDifferFactory} from './differs/default_iterable_differ'; -import {DefaultKeyValueDifferFactory, KeyValueChangeRecord} from './differs/default_keyvalue_differ'; +import {DefaultKeyValueDifferFactory} from './differs/default_keyvalue_differ'; import {IterableDifferFactory, IterableDiffers} from './differs/iterable_differs'; import {KeyValueDifferFactory, KeyValueDiffers} from './differs/keyvalue_differs'; @@ -15,11 +15,11 @@ export {SimpleChanges} from '../metadata/lifecycle_hooks'; export {SimpleChange, ValueUnwrapper, WrappedValue, devModeEqual, looseIdentical} from './change_detection_util'; export {ChangeDetectorRef} from './change_detector_ref'; export {ChangeDetectionStrategy, ChangeDetectorStatus, isDefaultChangeDetectionStrategy} from './constants'; -export {CollectionChangeRecord, DefaultIterableDifferFactory} from './differs/default_iterable_differ'; +export {DefaultIterableDifferFactory} from './differs/default_iterable_differ'; export {DefaultIterableDiffer} from './differs/default_iterable_differ'; -export {DefaultKeyValueDifferFactory, KeyValueChangeRecord} from './differs/default_keyvalue_differ'; -export {IterableDiffer, IterableDifferFactory, IterableDiffers, TrackByFn} from './differs/iterable_differs'; -export {KeyValueDiffer, KeyValueDifferFactory, KeyValueDiffers} from './differs/keyvalue_differs'; +export {DefaultKeyValueDifferFactory} from './differs/default_keyvalue_differ'; +export {CollectionChangeRecord, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, IterableDiffers, TrackByFn} from './differs/iterable_differs'; +export {KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFactory, KeyValueDiffers} from './differs/keyvalue_differs'; export {PipeTransform} from './pipe_transform'; @@ -27,12 +27,12 @@ export {PipeTransform} from './pipe_transform'; /** * Structural diffing for `Object`s and `Map`s. */ -export const keyValDiff: KeyValueDifferFactory[] = [new DefaultKeyValueDifferFactory()]; +const keyValDiff: KeyValueDifferFactory[] = [new DefaultKeyValueDifferFactory()]; /** * Structural diffing for `Iterable` types such as `Array`s. */ -export const iterableDiff: IterableDifferFactory[] = [new DefaultIterableDifferFactory()]; +const iterableDiff: IterableDifferFactory[] = [new DefaultIterableDifferFactory()]; export const defaultIterableDiffers = new IterableDiffers(iterableDiff); diff --git a/modules/@angular/core/src/change_detection/differs/default_iterable_differ.ts b/modules/@angular/core/src/change_detection/differs/default_iterable_differ.ts index 08c1155208..f059f3f5b1 100644 --- a/modules/@angular/core/src/change_detection/differs/default_iterable_differ.ts +++ b/modules/@angular/core/src/change_detection/differs/default_iterable_differ.ts @@ -10,41 +10,41 @@ import {isListLikeIterable, iterateListLike} from '../../facade/collection'; import {isBlank, looseIdentical, stringify} from '../../facade/lang'; import {ChangeDetectorRef} from '../change_detector_ref'; -import {IterableDiffer, IterableDifferFactory, TrackByFn} from './iterable_differs'; +import {IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, TrackByFn} from './iterable_differs'; export class DefaultIterableDifferFactory implements IterableDifferFactory { constructor() {} supports(obj: Object): boolean { return isListLikeIterable(obj); } - create(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): DefaultIterableDiffer { - return new DefaultIterableDiffer(trackByFn); + create(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): DefaultIterableDiffer { + return new DefaultIterableDiffer(trackByFn); } } const trackByIdentity = (index: number, item: any) => item; /** - * @stable + * @deprecated v4.0.0 - Should not be part of public API. */ -export class DefaultIterableDiffer implements IterableDiffer { +export class DefaultIterableDiffer implements IterableDiffer, IterableChanges { private _length: number = null; - private _collection: any /** TODO #9100 */ = null; + private _collection: V[]|Set[]|any /* |Iterable */ = null; // Keeps track of the used records at any point in time (during & across `_check()` calls) - private _linkedRecords: _DuplicateMap = null; + private _linkedRecords: _DuplicateMap = null; // Keeps track of the removed records at any point in time during `_check()` calls. - private _unlinkedRecords: _DuplicateMap = null; - private _previousItHead: CollectionChangeRecord = null; - private _itHead: CollectionChangeRecord = null; - private _itTail: CollectionChangeRecord = null; - private _additionsHead: CollectionChangeRecord = null; - private _additionsTail: CollectionChangeRecord = null; - private _movesHead: CollectionChangeRecord = null; - private _movesTail: CollectionChangeRecord = null; - private _removalsHead: CollectionChangeRecord = null; - private _removalsTail: CollectionChangeRecord = null; + private _unlinkedRecords: _DuplicateMap = null; + private _previousItHead: IterableChangeRecord_ = null; + private _itHead: IterableChangeRecord_ = null; + private _itTail: IterableChangeRecord_ = null; + private _additionsHead: IterableChangeRecord_ = null; + private _additionsTail: IterableChangeRecord_ = null; + private _movesHead: IterableChangeRecord_ = null; + private _movesTail: IterableChangeRecord_ = null; + private _removalsHead: IterableChangeRecord_ = null; + private _removalsTail: IterableChangeRecord_ = null; // Keeps track of records where custom track by is the same, but item identity has changed - private _identityChangesHead: CollectionChangeRecord = null; - private _identityChangesTail: CollectionChangeRecord = null; + private _identityChangesHead: IterableChangeRecord_ = null; + private _identityChangesTail: IterableChangeRecord_ = null; constructor(private _trackByFn?: TrackByFn) { this._trackByFn = this._trackByFn || trackByIdentity; @@ -54,15 +54,15 @@ export class DefaultIterableDiffer implements IterableDiffer { get length(): number { return this._length; } - forEachItem(fn: Function) { - let record: CollectionChangeRecord; + forEachItem(fn: (record: IterableChangeRecord_) => void) { + let record: IterableChangeRecord_; for (record = this._itHead; record !== null; record = record._next) { fn(record); } } forEachOperation( - fn: (item: CollectionChangeRecord, previousIndex: number, currentIndex: number) => void) { + fn: (item: IterableChangeRecord_, previousIndex: number, currentIndex: number) => void) { let nextIt = this._itHead; let nextRemove = this._removalsHead; let addRemoveOffset = 0; @@ -111,42 +111,42 @@ export class DefaultIterableDiffer implements IterableDiffer { } } - forEachPreviousItem(fn: Function) { - let record: CollectionChangeRecord; + forEachPreviousItem(fn: (record: IterableChangeRecord_) => void) { + let record: IterableChangeRecord_; for (record = this._previousItHead; record !== null; record = record._nextPrevious) { fn(record); } } - forEachAddedItem(fn: Function) { - let record: CollectionChangeRecord; + forEachAddedItem(fn: (record: IterableChangeRecord_) => void) { + let record: IterableChangeRecord_; for (record = this._additionsHead; record !== null; record = record._nextAdded) { fn(record); } } - forEachMovedItem(fn: Function) { - let record: CollectionChangeRecord; + forEachMovedItem(fn: (record: IterableChangeRecord_) => void) { + let record: IterableChangeRecord_; for (record = this._movesHead; record !== null; record = record._nextMoved) { fn(record); } } - forEachRemovedItem(fn: Function) { - let record: CollectionChangeRecord; + forEachRemovedItem(fn: (record: IterableChangeRecord_) => void) { + let record: IterableChangeRecord_; for (record = this._removalsHead; record !== null; record = record._nextRemoved) { fn(record); } } - forEachIdentityChange(fn: Function) { - let record: CollectionChangeRecord; + forEachIdentityChange(fn: (record: IterableChangeRecord_) => void) { + let record: IterableChangeRecord_; for (record = this._identityChangesHead; record !== null; record = record._nextIdentityChange) { fn(record); } } - diff(collection: any): DefaultIterableDiffer { + diff(collection: V[]|Set[]|any /* |Iterable */): DefaultIterableDiffer { if (isBlank(collection)) collection = []; if (!isListLikeIterable(collection)) { throw new Error(`Error trying to diff '${collection}'`); @@ -162,17 +162,17 @@ export class DefaultIterableDiffer implements IterableDiffer { onDestroy() {} // todo(vicb): optim for UnmodifiableListView (frozen arrays) - check(collection: any): boolean { + check(collection: V[]|Set[]|any /* |Iterable */): boolean { this._reset(); - let record: CollectionChangeRecord = this._itHead; + let record: IterableChangeRecord_ = this._itHead; let mayBeDirty: boolean = false; let index: number; - let item: any; + let item: V; let itemTrackBy: any; if (Array.isArray(collection)) { - const list = collection; - this._length = collection.length; + const list = collection as V[]; + this._length = list.length; for (let index = 0; index < this._length; index++) { item = list[index]; @@ -192,7 +192,7 @@ export class DefaultIterableDiffer implements IterableDiffer { } } else { index = 0; - iterateListLike(collection, (item: any /** TODO #9100 */) => { + iterateListLike(collection, (item: V) => { itemTrackBy = this._trackByFn(index, item); if (record === null || !looseIdentical(record.trackById, itemTrackBy)) { record = this._mismatch(record, item, itemTrackBy, index); @@ -233,8 +233,8 @@ export class DefaultIterableDiffer implements IterableDiffer { */ _reset() { if (this.isDirty) { - let record: CollectionChangeRecord; - let nextRecord: CollectionChangeRecord; + let record: IterableChangeRecord_; + let nextRecord: IterableChangeRecord_; for (record = this._previousItHead = this._itHead; record !== null; record = record._next) { record._nextPrevious = record._next; @@ -268,10 +268,10 @@ export class DefaultIterableDiffer implements IterableDiffer { * * @internal */ - _mismatch(record: CollectionChangeRecord, item: any, itemTrackBy: any, index: number): - CollectionChangeRecord { + _mismatch(record: IterableChangeRecord_, item: V, itemTrackBy: any, index: number): + IterableChangeRecord_ { // The previous record after which we will append the current one. - let previousRecord: CollectionChangeRecord; + let previousRecord: IterableChangeRecord_; if (record === null) { previousRecord = this._itTail; @@ -301,7 +301,7 @@ export class DefaultIterableDiffer implements IterableDiffer { } else { // It is a new item: add it. record = - this._addAfter(new CollectionChangeRecord(item, itemTrackBy), previousRecord, index); + this._addAfter(new IterableChangeRecord_(item, itemTrackBy), previousRecord, index); } } return record; @@ -334,9 +334,9 @@ export class DefaultIterableDiffer implements IterableDiffer { * * @internal */ - _verifyReinsertion(record: CollectionChangeRecord, item: any, itemTrackBy: any, index: number): - CollectionChangeRecord { - const reinsertRecord: CollectionChangeRecord = + _verifyReinsertion(record: IterableChangeRecord_, item: V, itemTrackBy: any, index: number): + IterableChangeRecord_ { + let reinsertRecord: IterableChangeRecord_ = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy); if (reinsertRecord !== null) { record = this._reinsertAfter(reinsertRecord, record._prev, index); @@ -348,16 +348,16 @@ export class DefaultIterableDiffer implements IterableDiffer { } /** - * Get rid of any excess {@link CollectionChangeRecord}s from the previous collection + * Get rid of any excess {@link IterableChangeRecord_}s from the previous collection * - * - `record` The first excess {@link CollectionChangeRecord}. + * - `record` The first excess {@link IterableChangeRecord_}. * * @internal */ - _truncate(record: CollectionChangeRecord) { + _truncate(record: IterableChangeRecord_) { // Anything after that needs to be removed; while (record !== null) { - const nextRecord: CollectionChangeRecord = record._next; + const nextRecord: IterableChangeRecord_ = record._next; this._addToRemovals(this._unlink(record)); record = nextRecord; } @@ -383,8 +383,9 @@ export class DefaultIterableDiffer implements IterableDiffer { } /** @internal */ - _reinsertAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord, index: number): - CollectionChangeRecord { + _reinsertAfter( + record: IterableChangeRecord_, prevRecord: IterableChangeRecord_, + index: number): IterableChangeRecord_ { if (this._unlinkedRecords !== null) { this._unlinkedRecords.remove(record); } @@ -408,8 +409,8 @@ export class DefaultIterableDiffer implements IterableDiffer { } /** @internal */ - _moveAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord, index: number): - CollectionChangeRecord { + _moveAfter(record: IterableChangeRecord_, prevRecord: IterableChangeRecord_, index: number): + IterableChangeRecord_ { this._unlink(record); this._insertAfter(record, prevRecord, index); this._addToMoves(record, index); @@ -417,8 +418,8 @@ export class DefaultIterableDiffer implements IterableDiffer { } /** @internal */ - _addAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord, index: number): - CollectionChangeRecord { + _addAfter(record: IterableChangeRecord_, prevRecord: IterableChangeRecord_, index: number): + IterableChangeRecord_ { this._insertAfter(record, prevRecord, index); if (this._additionsTail === null) { @@ -435,14 +436,15 @@ export class DefaultIterableDiffer implements IterableDiffer { } /** @internal */ - _insertAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord, index: number): - CollectionChangeRecord { + _insertAfter( + record: IterableChangeRecord_, prevRecord: IterableChangeRecord_, + index: number): IterableChangeRecord_ { // todo(vicb) // assert(record != prevRecord); // assert(record._next === null); // assert(record._prev === null); - const next: CollectionChangeRecord = prevRecord === null ? this._itHead : prevRecord._next; + const next: IterableChangeRecord_ = prevRecord === null ? this._itHead : prevRecord._next; // todo(vicb) // assert(next != record); // assert(prevRecord != record); @@ -460,7 +462,7 @@ export class DefaultIterableDiffer implements IterableDiffer { } if (this._linkedRecords === null) { - this._linkedRecords = new _DuplicateMap(); + this._linkedRecords = new _DuplicateMap(); } this._linkedRecords.put(record); @@ -469,12 +471,12 @@ export class DefaultIterableDiffer implements IterableDiffer { } /** @internal */ - _remove(record: CollectionChangeRecord): CollectionChangeRecord { + _remove(record: IterableChangeRecord_): IterableChangeRecord_ { return this._addToRemovals(this._unlink(record)); } /** @internal */ - _unlink(record: CollectionChangeRecord): CollectionChangeRecord { + _unlink(record: IterableChangeRecord_): IterableChangeRecord_ { if (this._linkedRecords !== null) { this._linkedRecords.remove(record); } @@ -501,7 +503,7 @@ export class DefaultIterableDiffer implements IterableDiffer { } /** @internal */ - _addToMoves(record: CollectionChangeRecord, toIndex: number): CollectionChangeRecord { + _addToMoves(record: IterableChangeRecord_, toIndex: number): IterableChangeRecord_ { // todo(vicb) // assert(record._nextMoved === null); @@ -522,10 +524,9 @@ export class DefaultIterableDiffer implements IterableDiffer { return record; } - /** @internal */ - _addToRemovals(record: CollectionChangeRecord): CollectionChangeRecord { + private _addToRemovals(record: IterableChangeRecord_): IterableChangeRecord_ { if (this._unlinkedRecords === null) { - this._unlinkedRecords = new _DuplicateMap(); + this._unlinkedRecords = new _DuplicateMap(); } this._unlinkedRecords.put(record); record.currentIndex = null; @@ -547,7 +548,7 @@ export class DefaultIterableDiffer implements IterableDiffer { } /** @internal */ - _addIdentityChange(record: CollectionChangeRecord, item: any) { + _addIdentityChange(record: IterableChangeRecord_, item: V) { record.item = item; if (this._identityChangesTail === null) { this._identityChangesTail = this._identityChangesHead = record; @@ -559,23 +560,23 @@ export class DefaultIterableDiffer implements IterableDiffer { toString(): string { - const list: any[] /** TODO #9100 */ = []; - this.forEachItem((record: any /** TODO #9100 */) => list.push(record)); + const list: IterableChangeRecord_[] = []; + this.forEachItem((record: IterableChangeRecord_) => list.push(record)); - const previous: any[] /** TODO #9100 */ = []; - this.forEachPreviousItem((record: any /** TODO #9100 */) => previous.push(record)); + const previous: IterableChangeRecord_[] = []; + this.forEachPreviousItem((record: IterableChangeRecord_) => previous.push(record)); - const additions: any[] /** TODO #9100 */ = []; - this.forEachAddedItem((record: any /** TODO #9100 */) => additions.push(record)); + const additions: IterableChangeRecord_[] = []; + this.forEachAddedItem((record: IterableChangeRecord_) => additions.push(record)); - const moves: any[] /** TODO #9100 */ = []; - this.forEachMovedItem((record: any /** TODO #9100 */) => moves.push(record)); + const moves: IterableChangeRecord_[] = []; + this.forEachMovedItem((record: IterableChangeRecord_) => moves.push(record)); - const removals: any[] /** TODO #9100 */ = []; - this.forEachRemovedItem((record: any /** TODO #9100 */) => removals.push(record)); + const removals: IterableChangeRecord_[] = []; + this.forEachRemovedItem((record: IterableChangeRecord_) => removals.push(record)); - const identityChanges: any[] /** TODO #9100 */ = []; - this.forEachIdentityChange((record: any /** TODO #9100 */) => identityChanges.push(record)); + const identityChanges: IterableChangeRecord_[] = []; + this.forEachIdentityChange((record: IterableChangeRecord_) => identityChanges.push(record)); return 'collection: ' + list.join(', ') + '\n' + 'previous: ' + previous.join(', ') + '\n' + @@ -589,33 +590,33 @@ export class DefaultIterableDiffer implements IterableDiffer { /** * @stable */ -export class CollectionChangeRecord { +export class IterableChangeRecord_ implements IterableChangeRecord { currentIndex: number = null; previousIndex: number = null; /** @internal */ - _nextPrevious: CollectionChangeRecord = null; + _nextPrevious: IterableChangeRecord_ = null; /** @internal */ - _prev: CollectionChangeRecord = null; + _prev: IterableChangeRecord_ = null; /** @internal */ - _next: CollectionChangeRecord = null; + _next: IterableChangeRecord_ = null; /** @internal */ - _prevDup: CollectionChangeRecord = null; + _prevDup: IterableChangeRecord_ = null; /** @internal */ - _nextDup: CollectionChangeRecord = null; + _nextDup: IterableChangeRecord_ = null; /** @internal */ - _prevRemoved: CollectionChangeRecord = null; + _prevRemoved: IterableChangeRecord_ = null; /** @internal */ - _nextRemoved: CollectionChangeRecord = null; + _nextRemoved: IterableChangeRecord_ = null; /** @internal */ - _nextAdded: CollectionChangeRecord = null; + _nextAdded: IterableChangeRecord_ = null; /** @internal */ - _nextMoved: CollectionChangeRecord = null; + _nextMoved: IterableChangeRecord_ = null; /** @internal */ - _nextIdentityChange: CollectionChangeRecord = null; + _nextIdentityChange: IterableChangeRecord_ = null; - constructor(public item: any, public trackById: any) {} + constructor(public item: V, public trackById: any) {} toString(): string { return this.previousIndex === this.currentIndex ? stringify(this.item) : @@ -624,19 +625,19 @@ export class CollectionChangeRecord { } } -// A linked list of CollectionChangeRecords with the same CollectionChangeRecord.item -class _DuplicateItemRecordList { +// A linked list of CollectionChangeRecords with the same IterableChangeRecord_.item +class _DuplicateItemRecordList { /** @internal */ - _head: CollectionChangeRecord = null; + _head: IterableChangeRecord_ = null; /** @internal */ - _tail: CollectionChangeRecord = null; + _tail: IterableChangeRecord_ = null; /** * Append the record to the list of duplicates. * * Note: by design all records in the list of duplicates hold the same value in record.item. */ - add(record: CollectionChangeRecord): void { + add(record: IterableChangeRecord_): void { if (this._head === null) { this._head = this._tail = record; record._nextDup = null; @@ -652,10 +653,10 @@ class _DuplicateItemRecordList { } } - // Returns a CollectionChangeRecord having CollectionChangeRecord.trackById == trackById and - // CollectionChangeRecord.currentIndex >= afterIndex - get(trackById: any, afterIndex: number): CollectionChangeRecord { - let record: CollectionChangeRecord; + // Returns a IterableChangeRecord_ having IterableChangeRecord_.trackById == trackById and + // IterableChangeRecord_.currentIndex >= afterIndex + get(trackById: any, afterIndex: number): IterableChangeRecord_ { + let record: IterableChangeRecord_; for (record = this._head; record !== null; record = record._nextDup) { if ((afterIndex === null || afterIndex < record.currentIndex) && looseIdentical(record.trackById, trackById)) { @@ -666,22 +667,22 @@ class _DuplicateItemRecordList { } /** - * Remove one {@link CollectionChangeRecord} from the list of duplicates. + * Remove one {@link IterableChangeRecord_} from the list of duplicates. * * Returns whether the list of duplicates is empty. */ - remove(record: CollectionChangeRecord): boolean { + remove(record: IterableChangeRecord_): boolean { // todo(vicb) // assert(() { // // verify that the record being removed is in the list. - // for (CollectionChangeRecord cursor = _head; cursor != null; cursor = cursor._nextDup) { + // for (IterableChangeRecord_ cursor = _head; cursor != null; cursor = cursor._nextDup) { // if (identical(cursor, record)) return true; // } // return false; //}); - const prev: CollectionChangeRecord = record._prevDup; - const next: CollectionChangeRecord = record._nextDup; + const prev: IterableChangeRecord_ = record._prevDup; + const next: IterableChangeRecord_ = record._nextDup; if (prev === null) { this._head = next; } else { @@ -696,41 +697,41 @@ class _DuplicateItemRecordList { } } -class _DuplicateMap { - map = new Map(); +class _DuplicateMap { + map = new Map>(); - put(record: CollectionChangeRecord) { + put(record: IterableChangeRecord_) { const key = record.trackById; let duplicates = this.map.get(key); if (!duplicates) { - duplicates = new _DuplicateItemRecordList(); + duplicates = new _DuplicateItemRecordList(); this.map.set(key, duplicates); } duplicates.add(record); } /** - * Retrieve the `value` using key. Because the CollectionChangeRecord value may be one which we + * Retrieve the `value` using key. Because the IterableChangeRecord_ value may be one which we * have already iterated over, we use the afterIndex to pretend it is not there. * * Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we * have any more `a`s needs to return the last `a` not the first or second. */ - get(trackById: any, afterIndex: number = null): CollectionChangeRecord { + get(trackById: any, afterIndex: number = null): IterableChangeRecord_ { const key = trackById; const recordList = this.map.get(key); return recordList ? recordList.get(trackById, afterIndex) : null; } /** - * Removes a {@link CollectionChangeRecord} from the list of duplicates. + * Removes a {@link IterableChangeRecord_} from the list of duplicates. * * The list of duplicates also is removed from the map if it gets empty. */ - remove(record: CollectionChangeRecord): CollectionChangeRecord { + remove(record: IterableChangeRecord_): IterableChangeRecord_ { const key = record.trackById; - const recordList: _DuplicateItemRecordList = this.map.get(key); + const recordList: _DuplicateItemRecordList = this.map.get(key); // Remove the list of duplicates when it gets empty if (recordList.remove(record)) { this.map.delete(key); diff --git a/modules/@angular/core/src/change_detection/differs/default_keyvalue_differ.ts b/modules/@angular/core/src/change_detection/differs/default_keyvalue_differ.ts index f3d2d13b83..d47d50d944 100644 --- a/modules/@angular/core/src/change_detection/differs/default_keyvalue_differ.ts +++ b/modules/@angular/core/src/change_detection/differs/default_keyvalue_differ.ts @@ -10,62 +10,64 @@ import {StringMapWrapper} from '../../facade/collection'; import {isJsObject, looseIdentical, stringify} from '../../facade/lang'; import {ChangeDetectorRef} from '../change_detector_ref'; -import {KeyValueDiffer, KeyValueDifferFactory} from './keyvalue_differs'; +import {KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFactory} from './keyvalue_differs'; -export class DefaultKeyValueDifferFactory implements KeyValueDifferFactory { +export class DefaultKeyValueDifferFactory implements KeyValueDifferFactory { constructor() {} supports(obj: any): boolean { return obj instanceof Map || isJsObject(obj); } - create(cdRef: ChangeDetectorRef): KeyValueDiffer { return new DefaultKeyValueDiffer(); } + create(cdRef: ChangeDetectorRef): KeyValueDiffer { + return new DefaultKeyValueDiffer(); + } } -export class DefaultKeyValueDiffer implements KeyValueDiffer { - private _records: Map = new Map(); - private _mapHead: KeyValueChangeRecord = null; - private _previousMapHead: KeyValueChangeRecord = null; - private _changesHead: KeyValueChangeRecord = null; - private _changesTail: KeyValueChangeRecord = null; - private _additionsHead: KeyValueChangeRecord = null; - private _additionsTail: KeyValueChangeRecord = null; - private _removalsHead: KeyValueChangeRecord = null; - private _removalsTail: KeyValueChangeRecord = null; +export class DefaultKeyValueDiffer implements KeyValueDiffer, KeyValueChanges { + private _records: Map = new Map(); + private _mapHead: KeyValueChangeRecord_ = null; + private _previousMapHead: KeyValueChangeRecord_ = null; + private _changesHead: KeyValueChangeRecord_ = null; + private _changesTail: KeyValueChangeRecord_ = null; + private _additionsHead: KeyValueChangeRecord_ = null; + private _additionsTail: KeyValueChangeRecord_ = null; + private _removalsHead: KeyValueChangeRecord_ = null; + private _removalsTail: KeyValueChangeRecord_ = null; get isDirty(): boolean { return this._additionsHead !== null || this._changesHead !== null || this._removalsHead !== null; } - forEachItem(fn: (r: KeyValueChangeRecord) => void) { - let record: KeyValueChangeRecord; + forEachItem(fn: (r: KeyValueChangeRecord) => void) { + let record: KeyValueChangeRecord_; for (record = this._mapHead; record !== null; record = record._next) { fn(record); } } - forEachPreviousItem(fn: (r: KeyValueChangeRecord) => void) { - let record: KeyValueChangeRecord; + forEachPreviousItem(fn: (r: KeyValueChangeRecord) => void) { + let record: KeyValueChangeRecord_; for (record = this._previousMapHead; record !== null; record = record._nextPrevious) { fn(record); } } - forEachChangedItem(fn: (r: KeyValueChangeRecord) => void) { - let record: KeyValueChangeRecord; + forEachChangedItem(fn: (r: KeyValueChangeRecord) => void) { + let record: KeyValueChangeRecord_; for (record = this._changesHead; record !== null; record = record._nextChanged) { fn(record); } } - forEachAddedItem(fn: (r: KeyValueChangeRecord) => void) { - let record: KeyValueChangeRecord; + forEachAddedItem(fn: (r: KeyValueChangeRecord) => void) { + let record: KeyValueChangeRecord_; for (record = this._additionsHead; record !== null; record = record._nextAdded) { fn(record); } } - forEachRemovedItem(fn: (r: KeyValueChangeRecord) => void) { - let record: KeyValueChangeRecord; + forEachRemovedItem(fn: (r: KeyValueChangeRecord) => void) { + let record: KeyValueChangeRecord_; for (record = this._removalsHead; record !== null; record = record._nextRemoved) { fn(record); } @@ -86,9 +88,9 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer { check(map: Map|{[k: string]: any}): boolean { this._reset(); const records = this._records; - let oldSeqRecord: KeyValueChangeRecord = this._mapHead; - let lastOldSeqRecord: KeyValueChangeRecord = null; - let lastNewSeqRecord: KeyValueChangeRecord = null; + let oldSeqRecord: KeyValueChangeRecord_ = this._mapHead; + let lastOldSeqRecord: KeyValueChangeRecord_ = null; + let lastNewSeqRecord: KeyValueChangeRecord_ = null; let seqChanged: boolean = false; this._forEach(map, (value: any, key: any) => { @@ -106,7 +108,7 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer { newSeqRecord = records.get(key); this._maybeAddToChanges(newSeqRecord, value); } else { - newSeqRecord = new KeyValueChangeRecord(key); + newSeqRecord = new KeyValueChangeRecord_(key); records.set(key, newSeqRecord); newSeqRecord.currentValue = value; this._addToAdditions(newSeqRecord); @@ -134,7 +136,7 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer { /** @internal */ _reset() { if (this.isDirty) { - let record: KeyValueChangeRecord; + let record: KeyValueChangeRecord_; // Record the state of the mapping for (record = this._previousMapHead = this._mapHead; record !== null; record = record._next) { record._nextPrevious = record._next; @@ -154,8 +156,7 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer { } } - /** @internal */ - _truncate(lastRecord: KeyValueChangeRecord, record: KeyValueChangeRecord) { + private _truncate(lastRecord: KeyValueChangeRecord_, record: KeyValueChangeRecord_) { while (record !== null) { if (lastRecord === null) { this._mapHead = null; @@ -168,14 +169,15 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer { record = nextRecord; } - for (let rec: KeyValueChangeRecord = this._removalsHead; rec !== null; rec = rec._nextRemoved) { + for (let rec: KeyValueChangeRecord_ = this._removalsHead; rec !== null; + rec = rec._nextRemoved) { rec.previousValue = rec.currentValue; rec.currentValue = null; this._records.delete(rec.key); } } - private _maybeAddToChanges(record: KeyValueChangeRecord, newValue: any): void { + private _maybeAddToChanges(record: KeyValueChangeRecord_, newValue: any): void { if (!looseIdentical(newValue, record.currentValue)) { record.previousValue = record.currentValue; record.currentValue = newValue; @@ -183,14 +185,12 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer { } } - /** @internal */ - _isInRemovals(record: KeyValueChangeRecord) { + private _isInRemovals(record: KeyValueChangeRecord_) { return record === this._removalsHead || record._nextRemoved !== null || record._prevRemoved !== null; } - /** @internal */ - _addToRemovals(record: KeyValueChangeRecord) { + private _addToRemovals(record: KeyValueChangeRecord_) { if (this._removalsHead === null) { this._removalsHead = this._removalsTail = record; } else { @@ -200,8 +200,7 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer { } } - /** @internal */ - _removeFromSeq(prev: KeyValueChangeRecord, record: KeyValueChangeRecord) { + private _removeFromSeq(prev: KeyValueChangeRecord_, record: KeyValueChangeRecord_) { const next = record._next; if (prev === null) { this._mapHead = next; @@ -211,8 +210,7 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer { record._next = null; } - /** @internal */ - _removeFromRemovals(record: KeyValueChangeRecord) { + private _removeFromRemovals(record: KeyValueChangeRecord_) { const prev = record._prevRemoved; const next = record._nextRemoved; if (prev === null) { @@ -228,8 +226,7 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer { record._prevRemoved = record._nextRemoved = null; } - /** @internal */ - _addToAdditions(record: KeyValueChangeRecord) { + private _addToAdditions(record: KeyValueChangeRecord_) { if (this._additionsHead === null) { this._additionsHead = this._additionsTail = record; } else { @@ -238,8 +235,7 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer { } } - /** @internal */ - _addToChanges(record: KeyValueChangeRecord) { + private _addToChanges(record: KeyValueChangeRecord_) { if (this._changesHead === null) { this._changesHead = this._changesTail = record; } else { @@ -254,7 +250,7 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer { const changes: any[] = []; const additions: any[] = []; const removals: any[] = []; - let record: KeyValueChangeRecord; + let record: KeyValueChangeRecord_; for (record = this._mapHead; record !== null; record = record._next) { items.push(stringify(record)); @@ -293,24 +289,24 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer { /** * @stable */ -export class KeyValueChangeRecord { - previousValue: any = null; - currentValue: any = null; +class KeyValueChangeRecord_ implements KeyValueChangeRecord { + previousValue: V = null; + currentValue: V = null; /** @internal */ - _nextPrevious: KeyValueChangeRecord = null; + _nextPrevious: KeyValueChangeRecord_ = null; /** @internal */ - _next: KeyValueChangeRecord = null; + _next: KeyValueChangeRecord_ = null; /** @internal */ - _nextAdded: KeyValueChangeRecord = null; + _nextAdded: KeyValueChangeRecord_ = null; /** @internal */ - _nextRemoved: KeyValueChangeRecord = null; + _nextRemoved: KeyValueChangeRecord_ = null; /** @internal */ - _prevRemoved: KeyValueChangeRecord = null; + _prevRemoved: KeyValueChangeRecord_ = null; /** @internal */ - _nextChanged: KeyValueChangeRecord = null; + _nextChanged: KeyValueChangeRecord_ = null; - constructor(public key: any) {} + constructor(public key: K) {} toString(): string { return looseIdentical(this.previousValue, this.currentValue) ? diff --git a/modules/@angular/core/src/change_detection/differs/iterable_differs.ts b/modules/@angular/core/src/change_detection/differs/iterable_differs.ts index ddfe12ab02..24d1cb93e4 100644 --- a/modules/@angular/core/src/change_detection/differs/iterable_differs.ts +++ b/modules/@angular/core/src/change_detection/differs/iterable_differs.ts @@ -12,16 +12,105 @@ import {ChangeDetectorRef} from '../change_detector_ref'; /** - * A strategy for tracking changes over time to an iterable. Used for {@link NgFor} to + * A strategy for tracking changes over time to an iterable. Used by {@link NgFor} to * respond to changes in an iterable by effecting equivalent changes in the DOM. * * @stable */ -export interface IterableDiffer { - diff(object: any): any; - onDestroy(): any /** TODO #9100 */; +export interface IterableDiffer { + /** + * Compute a difference between the previous state and the new `object` state. + * + * @param object containing the new value. + * @returns an object describing the difference. The return value is only valid until the next + * `diff()` invocation. + */ + diff(object: V[]|Set|any /* |Iterable */): IterableChanges; + // 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. } +/** + * An object describing the changes in the `Iterable` collection since last time + * `IterableDiffer#diff()` was invoked. + * + * @stable + */ +export interface IterableChanges { + /** + * Iterate over all changes. `IterableChangeRecord` will contain information about changes + * to each item. + */ + forEachItem(fn: (record: IterableChangeRecord) => void): void; + + /** + * Iterate over a set of operations which when applied to the original `Iterable` will produce the + * new `Iterable`. + * + * NOTE: These are not necessarily the actual operations which were applied to the original + * `Iterable`, rather these are a set of computed operations which may not be the same as the + * ones applied. + * + * @param record A change which needs to be applied + * @param previousIndex The `IterableChangeRecord#previousIndex` of the `record` refers to the + * original `Iterable` location, where as `previousIndex` refers to the transient location + * of the item, after applying the operations up to this point. + * @param currentIndex The `IterableChangeRecord#currentIndex` of the `record` refers to the + * original `Iterable` location, where as `currentIndex` refers to the transient location + * of the item, after applying the operations up to this point. + */ + forEachOperation( + fn: (record: IterableChangeRecord, previousIndex: number, currentIndex: number) => void): + void; + + /** + * Iterate over changes in the order of original `Iterable` showing where the original items + * have moved. + */ + forEachPreviousItem(fn: (record: IterableChangeRecord) => void): void; + + /** Iterate over all added items. */ + forEachAddedItem(fn: (record: IterableChangeRecord) => void): void; + + /** Iterate over all moved items. */ + forEachMovedItem(fn: (record: IterableChangeRecord) => void): void; + + /** Iterate over all removed items. */ + forEachRemovedItem(fn: (record: IterableChangeRecord) => void): void; + + /** Iterate over all items which had their identity (as computed by the `trackByFn`) changed. */ + forEachIdentityChange(fn: (record: IterableChangeRecord) => void): void; +} + +/** + * Record representing the item change information. + * + * @stable + */ +export interface IterableChangeRecord { + /** Current index of the item in `Iterable` or null if removed. */ + // TODO(TS2.1): make readonly once we move to TS v2.1 + /* readonly */ currentIndex: number; + + /** Previous index of the item in `Iterable` or null if added. */ + // TODO(TS2.1): make readonly once we move to TS v2.1 + /* readonly */ previousIndex: number; + + /** The item. */ + // TODO(TS2.1): make readonly once we move to TS v2.1 + /* readonly */ item: V; + + /** Track by identity as computed by the `trackByFn`. */ + // TODO(TS2.1): make readonly once we move to TS v2.1 + /* readonly */ trackById: any; +} + +/** + * @deprecated v4.0.0 - Use IterableChangeRecord instead. + */ +export interface CollectionChangeRecord extends IterableChangeRecord {} + + /** * An optional function passed into {@link NgFor} that defines how to track * items in an iterable (e.g. by index or id) @@ -38,7 +127,7 @@ export interface TrackByFn { (index: number, item: any): any; } */ export interface IterableDifferFactory { supports(objects: any): boolean; - create(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): IterableDiffer; + create(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): IterableDiffer; } /** @@ -46,7 +135,11 @@ export interface IterableDifferFactory { * @stable */ export class IterableDiffers { - constructor(public factories: IterableDifferFactory[]) {} + /** + * @deprecated v4.0.0 - Should be private + */ + factories: IterableDifferFactory[]; + constructor(factories: IterableDifferFactory[]) { this.factories = factories; } static create(factories: IterableDifferFactory[], parent?: IterableDiffers): IterableDiffers { if (isPresent(parent)) { @@ -64,8 +157,8 @@ export class IterableDiffers { * {@link IterableDiffers} instance. * * The following example shows how to extend an existing list of factories, - * which will only be applied to the injector for this component and its children. - * This step is all that's required to make a new {@link IterableDiffer} available. + * which will only be applied to the injector for this component and its children. + * This step is all that's required to make a new {@link IterableDiffer} available. * * ### Example * diff --git a/modules/@angular/core/src/change_detection/differs/keyvalue_differs.ts b/modules/@angular/core/src/change_detection/differs/keyvalue_differs.ts index d79fb486b5..28dfd2c152 100644 --- a/modules/@angular/core/src/change_detection/differs/keyvalue_differs.ts +++ b/modules/@angular/core/src/change_detection/differs/keyvalue_differs.ts @@ -7,8 +7,6 @@ */ import {Optional, Provider, SkipSelf} from '../../di'; -import {ListWrapper} from '../../facade/collection'; -import {isPresent} from '../../facade/lang'; import {ChangeDetectorRef} from '../change_detector_ref'; @@ -17,9 +15,83 @@ import {ChangeDetectorRef} from '../change_detector_ref'; * * @stable */ -export interface KeyValueDiffer { - diff(object: any): any /** TODO #9100 */; - onDestroy(): any /** TODO #9100 */; +export interface KeyValueDiffer { + /** + * Compute a difference between the previous state and the new `object` state. + * + * @param object containing the new value. + * @returns an object describing the difference. The return value is only valid until the next + * `diff()` invocation. + */ + diff(object: Map): KeyValueChanges; + + /** + * Compute a difference between the previous state and the new `object` state. + * + * @param object containing the new value. + * @returns an object describing the difference. The return value is only valid until the next + * `diff()` invocation. + */ + diff(object: {[key: string]: V}): KeyValueChanges; + // TODO(TS2.1): diff(this: KeyValueDiffer, object: Record): + // KeyValueDiffer; +} + +/** + * An object describing the changes in the `Map` or `{[k:string]: string}` since last time + * `KeyValueDiffer#diff()` was invoked. + * + * @stable + */ +export interface KeyValueChanges { + /** + * Iterate over all changes. `KeyValueChangeRecord` will contain information about changes + * to each item. + */ + forEachItem(fn: (r: KeyValueChangeRecord) => void): void; + + /** + * Iterate over changes in the order of original Map showing where the original items + * have moved. + */ + forEachPreviousItem(fn: (r: KeyValueChangeRecord) => void): void; + + /** + * Iterate over all keys for which values have changed. + */ + forEachChangedItem(fn: (r: KeyValueChangeRecord) => void): void; + + /** + * Iterate over all added items. + */ + forEachAddedItem(fn: (r: KeyValueChangeRecord) => void): void; + + /** + * Iterate over all removed items. + */ + forEachRemovedItem(fn: (r: KeyValueChangeRecord) => void): void; +} + +/** + * Record representing the item change information. + * + * @stable + */ +export interface KeyValueChangeRecord { + /** + * Current key in the Map. + */ + /* readonly */ key: K; + + /** + * Current value for the key or `undefined` if removed. + */ + /* readonly */ currentValue: V; + + /** + * Previous value for the key or `undefined` if added. + */ + /* readonly */ previousValue: V; } /** @@ -28,8 +100,15 @@ export interface KeyValueDiffer { * @stable */ export interface KeyValueDifferFactory { + /** + * Test to see if the differ knows how to diff this kind of object. + */ supports(objects: any): boolean; - create(cdRef: ChangeDetectorRef): KeyValueDiffer; + + /** + * Create a `KeyValueDiffer`. + */ + create(cdRef: ChangeDetectorRef): KeyValueDiffer; } /** @@ -37,16 +116,19 @@ export interface KeyValueDifferFactory { * @stable */ export class KeyValueDiffers { - constructor(public factories: KeyValueDifferFactory[]) {} + /** + * @deprecated v4.0.0 - Should be private. + */ + factories: KeyValueDifferFactory[]; - static create(factories: KeyValueDifferFactory[], parent?: KeyValueDiffers): KeyValueDiffers { - if (isPresent(parent)) { + constructor(factories: KeyValueDifferFactory[]) { this.factories = factories; } + + static create(factories: KeyValueDifferFactory[], parent?: KeyValueDiffers): KeyValueDiffers { + if (parent) { const copied = parent.factories.slice(); factories = factories.concat(copied); - return new KeyValueDiffers(factories); - } else { - return new KeyValueDiffers(factories); } + return new KeyValueDiffers(factories); } /** @@ -68,14 +150,13 @@ export class KeyValueDiffers { * }) * ``` */ - static extend(factories: KeyValueDifferFactory[]): Provider { + static extend(factories: KeyValueDifferFactory[]): Provider { return { provide: KeyValueDiffers, useFactory: (parent: KeyValueDiffers) => { if (!parent) { // Typically would occur when calling KeyValueDiffers.extend inside of dependencies passed - // to - // bootstrap(), which would override default pipes instead of extending them. + // to bootstrap(), which would override default pipes instead of extending them. throw new Error('Cannot extend KeyValueDiffers without a parent injector'); } return KeyValueDiffers.create(factories, parent); @@ -85,12 +166,11 @@ export class KeyValueDiffers { }; } - find(kv: Object): KeyValueDifferFactory { + find(kv: any): KeyValueDifferFactory { const factory = this.factories.find(f => f.supports(kv)); - if (isPresent(factory)) { + if (factory) { return factory; - } else { - throw new Error(`Cannot find a differ supporting object '${kv}'`); } + throw new Error(`Cannot find a differ supporting object '${kv}'`); } } diff --git a/modules/@angular/core/test/change_detection/differs/default_keyvalue_differ_spec.ts b/modules/@angular/core/test/change_detection/differs/default_keyvalue_differ_spec.ts index fe7b98354b..d745ba699b 100644 --- a/modules/@angular/core/test/change_detection/differs/default_keyvalue_differ_spec.ts +++ b/modules/@angular/core/test/change_detection/differs/default_keyvalue_differ_spec.ts @@ -14,11 +14,11 @@ import {kvChangesAsString} from '../../change_detection/util'; export function main() { describe('keyvalue differ', function() { describe('DefaultKeyValueDiffer', function() { - let differ: DefaultKeyValueDiffer; + let differ: DefaultKeyValueDiffer; let m: Map; beforeEach(() => { - differ = new DefaultKeyValueDiffer(); + differ = new DefaultKeyValueDiffer(); m = new Map(); }); diff --git a/tools/public_api_guard/core/index.d.ts b/tools/public_api_guard/core/index.d.ts index 65fad43c6a..eea35cc569 100644 --- a/tools/public_api_guard/core/index.d.ts +++ b/tools/public_api_guard/core/index.d.ts @@ -205,14 +205,8 @@ export interface ClassProvider { useClass: Type; } -/** @stable */ -export declare class CollectionChangeRecord { - currentIndex: number; - item: any; - previousIndex: number; - trackById: any; - constructor(item: any, trackById: any); - toString(): string; +/** @deprecated */ +export interface CollectionChangeRecord extends IterableChangeRecord { } /** @stable */ @@ -358,21 +352,21 @@ export declare class DebugNode { constructor(nativeNode: any, parent: DebugNode, _debugInfo: RenderDebugInfo); } -/** @stable */ -export declare class DefaultIterableDiffer implements IterableDiffer { +/** @deprecated */ +export declare class DefaultIterableDiffer implements IterableDiffer, IterableChanges { collection: any; isDirty: boolean; length: number; constructor(_trackByFn?: TrackByFn); - check(collection: any): boolean; - diff(collection: any): DefaultIterableDiffer; - forEachAddedItem(fn: Function): void; - forEachIdentityChange(fn: Function): void; - forEachItem(fn: Function): void; - forEachMovedItem(fn: Function): void; - forEachOperation(fn: (item: CollectionChangeRecord, previousIndex: number, currentIndex: number) => void): void; - forEachPreviousItem(fn: Function): void; - forEachRemovedItem(fn: Function): void; + check(collection: V[] | Set[] | any): boolean; + diff(collection: V[] | Set[] | any): DefaultIterableDiffer; + forEachAddedItem(fn: (record: IterableChangeRecord_) => void): void; + forEachIdentityChange(fn: (record: IterableChangeRecord_) => void): void; + forEachItem(fn: (record: IterableChangeRecord_) => void): void; + forEachMovedItem(fn: (record: IterableChangeRecord_) => void): void; + forEachOperation(fn: (item: IterableChangeRecord_, previousIndex: number, currentIndex: number) => void): void; + forEachPreviousItem(fn: (record: IterableChangeRecord_) => void): void; + forEachRemovedItem(fn: (record: IterableChangeRecord_) => void): void; onDestroy(): void; toString(): string; } @@ -511,20 +505,38 @@ export declare const Input: InputDecorator; export declare function isDevMode(): boolean; /** @stable */ -export interface IterableDiffer { - diff(object: any): any; - onDestroy(): any; +export interface IterableChangeRecord { + currentIndex: number; + item: V; + previousIndex: number; + trackById: any; +} + +/** @stable */ +export interface IterableChanges { + forEachAddedItem(fn: (record: IterableChangeRecord) => void): void; + forEachIdentityChange(fn: (record: IterableChangeRecord) => void): void; + forEachItem(fn: (record: IterableChangeRecord) => void): void; + forEachMovedItem(fn: (record: IterableChangeRecord) => void): void; + forEachOperation(fn: (record: IterableChangeRecord, previousIndex: number, currentIndex: number) => void): void; + forEachPreviousItem(fn: (record: IterableChangeRecord) => void): void; + forEachRemovedItem(fn: (record: IterableChangeRecord) => void): void; +} + +/** @stable */ +export interface IterableDiffer { + diff(object: V[] | Set | any): IterableChanges; } /** @stable */ export interface IterableDifferFactory { - create(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): IterableDiffer; + create(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): IterableDiffer; supports(objects: any): boolean; } /** @stable */ export declare class IterableDiffers { - factories: IterableDifferFactory[]; + /** @deprecated */ factories: IterableDifferFactory[]; constructor(factories: IterableDifferFactory[]); find(iterable: any): IterableDifferFactory; static create(factories: IterableDifferFactory[], parent?: IterableDiffers): IterableDiffers; @@ -535,33 +547,42 @@ export declare class IterableDiffers { export declare function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSequenceMetadata; /** @stable */ -export declare class KeyValueChangeRecord { - currentValue: any; - key: any; - previousValue: any; - constructor(key: any); - toString(): string; +export interface KeyValueChangeRecord { + currentValue: V; + key: K; + previousValue: V; } /** @stable */ -export interface KeyValueDiffer { - diff(object: any): any; - onDestroy(): any; +export interface KeyValueChanges { + forEachAddedItem(fn: (r: KeyValueChangeRecord) => void): void; + forEachChangedItem(fn: (r: KeyValueChangeRecord) => void): void; + forEachItem(fn: (r: KeyValueChangeRecord) => void): void; + forEachPreviousItem(fn: (r: KeyValueChangeRecord) => void): void; + forEachRemovedItem(fn: (r: KeyValueChangeRecord) => void): void; +} + +/** @stable */ +export interface KeyValueDiffer { + diff(object: Map): KeyValueChanges; + diff(object: { + [key: string]: V; + }): KeyValueChanges; } /** @stable */ export interface KeyValueDifferFactory { - create(cdRef: ChangeDetectorRef): KeyValueDiffer; + create(cdRef: ChangeDetectorRef): KeyValueDiffer; supports(objects: any): boolean; } /** @stable */ export declare class KeyValueDiffers { - factories: KeyValueDifferFactory[]; + /** @deprecated */ factories: KeyValueDifferFactory[]; constructor(factories: KeyValueDifferFactory[]); - find(kv: Object): KeyValueDifferFactory; - static create(factories: KeyValueDifferFactory[], parent?: KeyValueDiffers): KeyValueDiffers; - static extend(factories: KeyValueDifferFactory[]): Provider; + find(kv: any): KeyValueDifferFactory; + static create(factories: KeyValueDifferFactory[], parent?: KeyValueDiffers): KeyValueDiffers; + static extend(factories: KeyValueDifferFactory[]): Provider; } /** @experimental */