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 `<V>`. 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 `<V>`. 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
This commit is contained in:
parent
5d9cbd7d6f
commit
8c7e93bebe
|
@ -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 {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 {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe, TitleCasePipe} from './pipes/index';
|
||||||
export {VERSION} from './version';
|
export {VERSION} from './version';
|
||||||
export {Version} from '@angular/core';
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* 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 {isListLikeIterable} from '../facade/collection';
|
||||||
import {isPresent, stringify} from '../facade/lang';
|
import {isPresent, stringify} from '../facade/lang';
|
||||||
|
@ -41,8 +41,8 @@ import {isPresent, stringify} from '../facade/lang';
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngClass]'})
|
@Directive({selector: '[ngClass]'})
|
||||||
export class NgClass implements DoCheck {
|
export class NgClass implements DoCheck {
|
||||||
private _iterableDiffer: IterableDiffer;
|
private _iterableDiffer: IterableDiffer<string>;
|
||||||
private _keyValueDiffer: KeyValueDiffer;
|
private _keyValueDiffer: KeyValueDiffer<string, any>;
|
||||||
private _initialClasses: string[] = [];
|
private _initialClasses: string[] = [];
|
||||||
private _rawClass: string[]|Set<string>|{[klass: string]: any};
|
private _rawClass: string[]|Set<string>|{[klass: string]: any};
|
||||||
|
|
||||||
|
@ -78,39 +78,35 @@ export class NgClass implements DoCheck {
|
||||||
|
|
||||||
ngDoCheck(): void {
|
ngDoCheck(): void {
|
||||||
if (this._iterableDiffer) {
|
if (this._iterableDiffer) {
|
||||||
const changes = this._iterableDiffer.diff(this._rawClass);
|
const iterableChanges = this._iterableDiffer.diff(this._rawClass as string[]);
|
||||||
if (changes) {
|
if (iterableChanges) {
|
||||||
this._applyIterableChanges(changes);
|
this._applyIterableChanges(iterableChanges);
|
||||||
}
|
}
|
||||||
} else if (this._keyValueDiffer) {
|
} else if (this._keyValueDiffer) {
|
||||||
const changes = this._keyValueDiffer.diff(this._rawClass);
|
const keyValueChanges = this._keyValueDiffer.diff(this._rawClass as{[k: string]: any});
|
||||||
if (changes) {
|
if (keyValueChanges) {
|
||||||
this._applyKeyValueChanges(changes);
|
this._applyKeyValueChanges(keyValueChanges);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _cleanupClasses(rawClassVal: string[]|Set<string>|{[klass: string]: any}): void {
|
private _cleanupClasses(rawClassVal: string[]|{[klass: string]: any}): void {
|
||||||
this._applyClasses(rawClassVal, true);
|
this._applyClasses(rawClassVal, true);
|
||||||
this._applyInitialClasses(false);
|
this._applyInitialClasses(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyKeyValueChanges(changes: any): void {
|
private _applyKeyValueChanges(changes: KeyValueChanges<string, any>): void {
|
||||||
changes.forEachAddedItem(
|
changes.forEachAddedItem((record) => this._toggleClass(record.key, record.currentValue));
|
||||||
(record: KeyValueChangeRecord) => this._toggleClass(record.key, record.currentValue));
|
changes.forEachChangedItem((record) => this._toggleClass(record.key, record.currentValue));
|
||||||
|
changes.forEachRemovedItem((record) => {
|
||||||
changes.forEachChangedItem(
|
|
||||||
(record: KeyValueChangeRecord) => this._toggleClass(record.key, record.currentValue));
|
|
||||||
|
|
||||||
changes.forEachRemovedItem((record: KeyValueChangeRecord) => {
|
|
||||||
if (record.previousValue) {
|
if (record.previousValue) {
|
||||||
this._toggleClass(record.key, false);
|
this._toggleClass(record.key, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyIterableChanges(changes: any): void {
|
private _applyIterableChanges(changes: IterableChanges<string>): void {
|
||||||
changes.forEachAddedItem((record: CollectionChangeRecord) => {
|
changes.forEachAddedItem((record) => {
|
||||||
if (typeof record.item === 'string') {
|
if (typeof record.item === 'string') {
|
||||||
this._toggleClass(record.item, true);
|
this._toggleClass(record.item, true);
|
||||||
} else {
|
} else {
|
||||||
|
@ -119,8 +115,7 @@ export class NgClass implements DoCheck {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
changes.forEachRemovedItem(
|
changes.forEachRemovedItem((record) => this._toggleClass(record.item, false));
|
||||||
(record: CollectionChangeRecord) => this._toggleClass(record.item, false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyInitialClasses(isCleanup: boolean) {
|
private _applyInitialClasses(isCleanup: boolean) {
|
||||||
|
@ -128,7 +123,7 @@ export class NgClass implements DoCheck {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyClasses(
|
private _applyClasses(
|
||||||
rawClassVal: string[]|Set<string>|{[key: string]: any}, isCleanup: boolean) {
|
rawClassVal: string[]|Set<string>|{[klass: string]: any}, isCleanup: boolean) {
|
||||||
if (rawClassVal) {
|
if (rawClassVal) {
|
||||||
if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) {
|
if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) {
|
||||||
(<any>rawClassVal).forEach((klass: string) => this._toggleClass(klass, !isCleanup));
|
(<any>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();
|
klass = klass.trim();
|
||||||
if (klass) {
|
if (klass) {
|
||||||
klass.split(/\s+/g).forEach(
|
klass.split(/\s+/g).forEach(
|
||||||
klass => { this._renderer.setElementClass(this._ngEl.nativeElement, klass, enabled); });
|
klass => { this._renderer.setElementClass(this._ngEl.nativeElement, klass, !!enabled); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* 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';
|
import {getTypeNameForDebugging} from '../facade/lang';
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ export class NgFor implements DoCheck, OnChanges {
|
||||||
|
|
||||||
get ngForTrackBy(): TrackByFn { return this._trackByFn; }
|
get ngForTrackBy(): TrackByFn { return this._trackByFn; }
|
||||||
|
|
||||||
private _differ: IterableDiffer = null;
|
private _differ: IterableDiffer<any> = null;
|
||||||
private _trackByFn: TrackByFn;
|
private _trackByFn: TrackByFn;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -140,10 +140,10 @@ export class NgFor implements DoCheck, OnChanges {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyChanges(changes: DefaultIterableDiffer) {
|
private _applyChanges(changes: IterableChanges<any>) {
|
||||||
const insertTuples: RecordViewTuple[] = [];
|
const insertTuples: RecordViewTuple[] = [];
|
||||||
changes.forEachOperation(
|
changes.forEachOperation(
|
||||||
(item: CollectionChangeRecord, adjustedPreviousIndex: number, currentIndex: number) => {
|
(item: IterableChangeRecord<any>, adjustedPreviousIndex: number, currentIndex: number) => {
|
||||||
if (item.previousIndex == null) {
|
if (item.previousIndex == null) {
|
||||||
const view = this._viewContainer.createEmbeddedView(
|
const view = this._viewContainer.createEmbeddedView(
|
||||||
this._template, new NgForRow(null, null, null), currentIndex);
|
this._template, new NgForRow(null, null, null), currentIndex);
|
||||||
|
@ -175,7 +175,7 @@ export class NgFor implements DoCheck, OnChanges {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _perViewChange(view: EmbeddedViewRef<NgForRow>, record: CollectionChangeRecord) {
|
private _perViewChange(view: EmbeddedViewRef<NgForRow>, record: IterableChangeRecord<any>) {
|
||||||
view.context.$implicit = record.item;
|
view.context.$implicit = record.item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* 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
|
* @ngModule CommonModule
|
||||||
|
@ -33,7 +33,7 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDif
|
||||||
@Directive({selector: '[ngStyle]'})
|
@Directive({selector: '[ngStyle]'})
|
||||||
export class NgStyle implements DoCheck {
|
export class NgStyle implements DoCheck {
|
||||||
private _ngStyle: {[key: string]: string};
|
private _ngStyle: {[key: string]: string};
|
||||||
private _differ: KeyValueDiffer;
|
private _differ: KeyValueDiffer<string, string|number>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}
|
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||||
|
@ -55,20 +55,16 @@ export class NgStyle implements DoCheck {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyChanges(changes: any): void {
|
private _applyChanges(changes: KeyValueChanges<string, string|number>): void {
|
||||||
changes.forEachRemovedItem((record: KeyValueChangeRecord) => this._setStyle(record.key, null));
|
changes.forEachRemovedItem((record) => this._setStyle(record.key, null));
|
||||||
|
changes.forEachAddedItem((record) => this._setStyle(record.key, record.currentValue));
|
||||||
changes.forEachAddedItem(
|
changes.forEachChangedItem((record) => this._setStyle(record.key, record.currentValue));
|
||||||
(record: KeyValueChangeRecord) => this._setStyle(record.key, record.currentValue));
|
|
||||||
|
|
||||||
changes.forEachChangedItem(
|
|
||||||
(record: KeyValueChangeRecord) => 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('.');
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,4 +12,4 @@
|
||||||
* Change detection enables data binding in Angular.
|
* 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';
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {DefaultIterableDifferFactory} from './differs/default_iterable_differ';
|
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 {IterableDifferFactory, IterableDiffers} from './differs/iterable_differs';
|
||||||
import {KeyValueDifferFactory, KeyValueDiffers} from './differs/keyvalue_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 {SimpleChange, ValueUnwrapper, WrappedValue, devModeEqual, looseIdentical} from './change_detection_util';
|
||||||
export {ChangeDetectorRef} from './change_detector_ref';
|
export {ChangeDetectorRef} from './change_detector_ref';
|
||||||
export {ChangeDetectionStrategy, ChangeDetectorStatus, isDefaultChangeDetectionStrategy} from './constants';
|
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 {DefaultIterableDiffer} from './differs/default_iterable_differ';
|
||||||
export {DefaultKeyValueDifferFactory, KeyValueChangeRecord} from './differs/default_keyvalue_differ';
|
export {DefaultKeyValueDifferFactory} from './differs/default_keyvalue_differ';
|
||||||
export {IterableDiffer, IterableDifferFactory, IterableDiffers, TrackByFn} from './differs/iterable_differs';
|
export {CollectionChangeRecord, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, IterableDiffers, TrackByFn} from './differs/iterable_differs';
|
||||||
export {KeyValueDiffer, KeyValueDifferFactory, KeyValueDiffers} from './differs/keyvalue_differs';
|
export {KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFactory, KeyValueDiffers} from './differs/keyvalue_differs';
|
||||||
export {PipeTransform} from './pipe_transform';
|
export {PipeTransform} from './pipe_transform';
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,12 +27,12 @@ export {PipeTransform} from './pipe_transform';
|
||||||
/**
|
/**
|
||||||
* Structural diffing for `Object`s and `Map`s.
|
* 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.
|
* 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);
|
export const defaultIterableDiffers = new IterableDiffers(iterableDiff);
|
||||||
|
|
||||||
|
|
|
@ -10,41 +10,41 @@ import {isListLikeIterable, iterateListLike} from '../../facade/collection';
|
||||||
import {isBlank, looseIdentical, stringify} from '../../facade/lang';
|
import {isBlank, looseIdentical, stringify} from '../../facade/lang';
|
||||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
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 {
|
export class DefaultIterableDifferFactory implements IterableDifferFactory {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
supports(obj: Object): boolean { return isListLikeIterable(obj); }
|
supports(obj: Object): boolean { return isListLikeIterable(obj); }
|
||||||
create(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): DefaultIterableDiffer {
|
create<V>(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): DefaultIterableDiffer<V> {
|
||||||
return new DefaultIterableDiffer(trackByFn);
|
return new DefaultIterableDiffer<V>(trackByFn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const trackByIdentity = (index: number, item: any) => item;
|
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<V> implements IterableDiffer<V>, IterableChanges<V> {
|
||||||
private _length: number = null;
|
private _length: number = null;
|
||||||
private _collection: any /** TODO #9100 */ = null;
|
private _collection: V[]|Set<V>[]|any /* |Iterable<V> */ = null;
|
||||||
// Keeps track of the used records at any point in time (during & across `_check()` calls)
|
// Keeps track of the used records at any point in time (during & across `_check()` calls)
|
||||||
private _linkedRecords: _DuplicateMap = null;
|
private _linkedRecords: _DuplicateMap<V> = null;
|
||||||
// Keeps track of the removed records at any point in time during `_check()` calls.
|
// Keeps track of the removed records at any point in time during `_check()` calls.
|
||||||
private _unlinkedRecords: _DuplicateMap = null;
|
private _unlinkedRecords: _DuplicateMap<V> = null;
|
||||||
private _previousItHead: CollectionChangeRecord = null;
|
private _previousItHead: IterableChangeRecord_<V> = null;
|
||||||
private _itHead: CollectionChangeRecord = null;
|
private _itHead: IterableChangeRecord_<V> = null;
|
||||||
private _itTail: CollectionChangeRecord = null;
|
private _itTail: IterableChangeRecord_<V> = null;
|
||||||
private _additionsHead: CollectionChangeRecord = null;
|
private _additionsHead: IterableChangeRecord_<V> = null;
|
||||||
private _additionsTail: CollectionChangeRecord = null;
|
private _additionsTail: IterableChangeRecord_<V> = null;
|
||||||
private _movesHead: CollectionChangeRecord = null;
|
private _movesHead: IterableChangeRecord_<V> = null;
|
||||||
private _movesTail: CollectionChangeRecord = null;
|
private _movesTail: IterableChangeRecord_<V> = null;
|
||||||
private _removalsHead: CollectionChangeRecord = null;
|
private _removalsHead: IterableChangeRecord_<V> = null;
|
||||||
private _removalsTail: CollectionChangeRecord = null;
|
private _removalsTail: IterableChangeRecord_<V> = null;
|
||||||
// Keeps track of records where custom track by is the same, but item identity has changed
|
// Keeps track of records where custom track by is the same, but item identity has changed
|
||||||
private _identityChangesHead: CollectionChangeRecord = null;
|
private _identityChangesHead: IterableChangeRecord_<V> = null;
|
||||||
private _identityChangesTail: CollectionChangeRecord = null;
|
private _identityChangesTail: IterableChangeRecord_<V> = null;
|
||||||
|
|
||||||
constructor(private _trackByFn?: TrackByFn) {
|
constructor(private _trackByFn?: TrackByFn) {
|
||||||
this._trackByFn = this._trackByFn || trackByIdentity;
|
this._trackByFn = this._trackByFn || trackByIdentity;
|
||||||
|
@ -54,15 +54,15 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
|
|
||||||
get length(): number { return this._length; }
|
get length(): number { return this._length; }
|
||||||
|
|
||||||
forEachItem(fn: Function) {
|
forEachItem(fn: (record: IterableChangeRecord_<V>) => void) {
|
||||||
let record: CollectionChangeRecord;
|
let record: IterableChangeRecord_<V>;
|
||||||
for (record = this._itHead; record !== null; record = record._next) {
|
for (record = this._itHead; record !== null; record = record._next) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachOperation(
|
forEachOperation(
|
||||||
fn: (item: CollectionChangeRecord, previousIndex: number, currentIndex: number) => void) {
|
fn: (item: IterableChangeRecord_<V>, previousIndex: number, currentIndex: number) => void) {
|
||||||
let nextIt = this._itHead;
|
let nextIt = this._itHead;
|
||||||
let nextRemove = this._removalsHead;
|
let nextRemove = this._removalsHead;
|
||||||
let addRemoveOffset = 0;
|
let addRemoveOffset = 0;
|
||||||
|
@ -111,42 +111,42 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachPreviousItem(fn: Function) {
|
forEachPreviousItem(fn: (record: IterableChangeRecord_<V>) => void) {
|
||||||
let record: CollectionChangeRecord;
|
let record: IterableChangeRecord_<V>;
|
||||||
for (record = this._previousItHead; record !== null; record = record._nextPrevious) {
|
for (record = this._previousItHead; record !== null; record = record._nextPrevious) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachAddedItem(fn: Function) {
|
forEachAddedItem(fn: (record: IterableChangeRecord_<V>) => void) {
|
||||||
let record: CollectionChangeRecord;
|
let record: IterableChangeRecord_<V>;
|
||||||
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachMovedItem(fn: Function) {
|
forEachMovedItem(fn: (record: IterableChangeRecord_<V>) => void) {
|
||||||
let record: CollectionChangeRecord;
|
let record: IterableChangeRecord_<V>;
|
||||||
for (record = this._movesHead; record !== null; record = record._nextMoved) {
|
for (record = this._movesHead; record !== null; record = record._nextMoved) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachRemovedItem(fn: Function) {
|
forEachRemovedItem(fn: (record: IterableChangeRecord_<V>) => void) {
|
||||||
let record: CollectionChangeRecord;
|
let record: IterableChangeRecord_<V>;
|
||||||
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachIdentityChange(fn: Function) {
|
forEachIdentityChange(fn: (record: IterableChangeRecord_<V>) => void) {
|
||||||
let record: CollectionChangeRecord;
|
let record: IterableChangeRecord_<V>;
|
||||||
for (record = this._identityChangesHead; record !== null; record = record._nextIdentityChange) {
|
for (record = this._identityChangesHead; record !== null; record = record._nextIdentityChange) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diff(collection: any): DefaultIterableDiffer {
|
diff(collection: V[]|Set<V>[]|any /* |Iterable<V> */): DefaultIterableDiffer<V> {
|
||||||
if (isBlank(collection)) collection = [];
|
if (isBlank(collection)) collection = [];
|
||||||
if (!isListLikeIterable(collection)) {
|
if (!isListLikeIterable(collection)) {
|
||||||
throw new Error(`Error trying to diff '${collection}'`);
|
throw new Error(`Error trying to diff '${collection}'`);
|
||||||
|
@ -162,17 +162,17 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
onDestroy() {}
|
onDestroy() {}
|
||||||
|
|
||||||
// todo(vicb): optim for UnmodifiableListView (frozen arrays)
|
// todo(vicb): optim for UnmodifiableListView (frozen arrays)
|
||||||
check(collection: any): boolean {
|
check(collection: V[]|Set<V>[]|any /* |Iterable<V> */): boolean {
|
||||||
this._reset();
|
this._reset();
|
||||||
|
|
||||||
let record: CollectionChangeRecord = this._itHead;
|
let record: IterableChangeRecord_<V> = this._itHead;
|
||||||
let mayBeDirty: boolean = false;
|
let mayBeDirty: boolean = false;
|
||||||
let index: number;
|
let index: number;
|
||||||
let item: any;
|
let item: V;
|
||||||
let itemTrackBy: any;
|
let itemTrackBy: any;
|
||||||
if (Array.isArray(collection)) {
|
if (Array.isArray(collection)) {
|
||||||
const list = collection;
|
const list = collection as V[];
|
||||||
this._length = collection.length;
|
this._length = list.length;
|
||||||
|
|
||||||
for (let index = 0; index < this._length; index++) {
|
for (let index = 0; index < this._length; index++) {
|
||||||
item = list[index];
|
item = list[index];
|
||||||
|
@ -192,7 +192,7 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
index = 0;
|
index = 0;
|
||||||
iterateListLike(collection, (item: any /** TODO #9100 */) => {
|
iterateListLike(collection, (item: V) => {
|
||||||
itemTrackBy = this._trackByFn(index, item);
|
itemTrackBy = this._trackByFn(index, item);
|
||||||
if (record === null || !looseIdentical(record.trackById, itemTrackBy)) {
|
if (record === null || !looseIdentical(record.trackById, itemTrackBy)) {
|
||||||
record = this._mismatch(record, item, itemTrackBy, index);
|
record = this._mismatch(record, item, itemTrackBy, index);
|
||||||
|
@ -233,8 +233,8 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
*/
|
*/
|
||||||
_reset() {
|
_reset() {
|
||||||
if (this.isDirty) {
|
if (this.isDirty) {
|
||||||
let record: CollectionChangeRecord;
|
let record: IterableChangeRecord_<V>;
|
||||||
let nextRecord: CollectionChangeRecord;
|
let nextRecord: IterableChangeRecord_<V>;
|
||||||
|
|
||||||
for (record = this._previousItHead = this._itHead; record !== null; record = record._next) {
|
for (record = this._previousItHead = this._itHead; record !== null; record = record._next) {
|
||||||
record._nextPrevious = record._next;
|
record._nextPrevious = record._next;
|
||||||
|
@ -268,10 +268,10 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
_mismatch(record: CollectionChangeRecord, item: any, itemTrackBy: any, index: number):
|
_mismatch(record: IterableChangeRecord_<V>, item: V, itemTrackBy: any, index: number):
|
||||||
CollectionChangeRecord {
|
IterableChangeRecord_<V> {
|
||||||
// The previous record after which we will append the current one.
|
// The previous record after which we will append the current one.
|
||||||
let previousRecord: CollectionChangeRecord;
|
let previousRecord: IterableChangeRecord_<V>;
|
||||||
|
|
||||||
if (record === null) {
|
if (record === null) {
|
||||||
previousRecord = this._itTail;
|
previousRecord = this._itTail;
|
||||||
|
@ -301,7 +301,7 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
} else {
|
} else {
|
||||||
// It is a new item: add it.
|
// It is a new item: add it.
|
||||||
record =
|
record =
|
||||||
this._addAfter(new CollectionChangeRecord(item, itemTrackBy), previousRecord, index);
|
this._addAfter(new IterableChangeRecord_<V>(item, itemTrackBy), previousRecord, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return record;
|
return record;
|
||||||
|
@ -334,9 +334,9 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
_verifyReinsertion(record: CollectionChangeRecord, item: any, itemTrackBy: any, index: number):
|
_verifyReinsertion(record: IterableChangeRecord_<V>, item: V, itemTrackBy: any, index: number):
|
||||||
CollectionChangeRecord {
|
IterableChangeRecord_<V> {
|
||||||
const reinsertRecord: CollectionChangeRecord =
|
let reinsertRecord: IterableChangeRecord_<V> =
|
||||||
this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy);
|
this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy);
|
||||||
if (reinsertRecord !== null) {
|
if (reinsertRecord !== null) {
|
||||||
record = this._reinsertAfter(reinsertRecord, record._prev, index);
|
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
|
* @internal
|
||||||
*/
|
*/
|
||||||
_truncate(record: CollectionChangeRecord) {
|
_truncate(record: IterableChangeRecord_<V>) {
|
||||||
// Anything after that needs to be removed;
|
// Anything after that needs to be removed;
|
||||||
while (record !== null) {
|
while (record !== null) {
|
||||||
const nextRecord: CollectionChangeRecord = record._next;
|
const nextRecord: IterableChangeRecord_<V> = record._next;
|
||||||
this._addToRemovals(this._unlink(record));
|
this._addToRemovals(this._unlink(record));
|
||||||
record = nextRecord;
|
record = nextRecord;
|
||||||
}
|
}
|
||||||
|
@ -383,8 +383,9 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_reinsertAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord, index: number):
|
_reinsertAfter(
|
||||||
CollectionChangeRecord {
|
record: IterableChangeRecord_<V>, prevRecord: IterableChangeRecord_<V>,
|
||||||
|
index: number): IterableChangeRecord_<V> {
|
||||||
if (this._unlinkedRecords !== null) {
|
if (this._unlinkedRecords !== null) {
|
||||||
this._unlinkedRecords.remove(record);
|
this._unlinkedRecords.remove(record);
|
||||||
}
|
}
|
||||||
|
@ -408,8 +409,8 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_moveAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord, index: number):
|
_moveAfter(record: IterableChangeRecord_<V>, prevRecord: IterableChangeRecord_<V>, index: number):
|
||||||
CollectionChangeRecord {
|
IterableChangeRecord_<V> {
|
||||||
this._unlink(record);
|
this._unlink(record);
|
||||||
this._insertAfter(record, prevRecord, index);
|
this._insertAfter(record, prevRecord, index);
|
||||||
this._addToMoves(record, index);
|
this._addToMoves(record, index);
|
||||||
|
@ -417,8 +418,8 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_addAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord, index: number):
|
_addAfter(record: IterableChangeRecord_<V>, prevRecord: IterableChangeRecord_<V>, index: number):
|
||||||
CollectionChangeRecord {
|
IterableChangeRecord_<V> {
|
||||||
this._insertAfter(record, prevRecord, index);
|
this._insertAfter(record, prevRecord, index);
|
||||||
|
|
||||||
if (this._additionsTail === null) {
|
if (this._additionsTail === null) {
|
||||||
|
@ -435,14 +436,15 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_insertAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord, index: number):
|
_insertAfter(
|
||||||
CollectionChangeRecord {
|
record: IterableChangeRecord_<V>, prevRecord: IterableChangeRecord_<V>,
|
||||||
|
index: number): IterableChangeRecord_<V> {
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
// assert(record != prevRecord);
|
// assert(record != prevRecord);
|
||||||
// assert(record._next === null);
|
// assert(record._next === null);
|
||||||
// assert(record._prev === null);
|
// assert(record._prev === null);
|
||||||
|
|
||||||
const next: CollectionChangeRecord = prevRecord === null ? this._itHead : prevRecord._next;
|
const next: IterableChangeRecord_<V> = prevRecord === null ? this._itHead : prevRecord._next;
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
// assert(next != record);
|
// assert(next != record);
|
||||||
// assert(prevRecord != record);
|
// assert(prevRecord != record);
|
||||||
|
@ -460,7 +462,7 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._linkedRecords === null) {
|
if (this._linkedRecords === null) {
|
||||||
this._linkedRecords = new _DuplicateMap();
|
this._linkedRecords = new _DuplicateMap<V>();
|
||||||
}
|
}
|
||||||
this._linkedRecords.put(record);
|
this._linkedRecords.put(record);
|
||||||
|
|
||||||
|
@ -469,12 +471,12 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_remove(record: CollectionChangeRecord): CollectionChangeRecord {
|
_remove(record: IterableChangeRecord_<V>): IterableChangeRecord_<V> {
|
||||||
return this._addToRemovals(this._unlink(record));
|
return this._addToRemovals(this._unlink(record));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_unlink(record: CollectionChangeRecord): CollectionChangeRecord {
|
_unlink(record: IterableChangeRecord_<V>): IterableChangeRecord_<V> {
|
||||||
if (this._linkedRecords !== null) {
|
if (this._linkedRecords !== null) {
|
||||||
this._linkedRecords.remove(record);
|
this._linkedRecords.remove(record);
|
||||||
}
|
}
|
||||||
|
@ -501,7 +503,7 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_addToMoves(record: CollectionChangeRecord, toIndex: number): CollectionChangeRecord {
|
_addToMoves(record: IterableChangeRecord_<V>, toIndex: number): IterableChangeRecord_<V> {
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
// assert(record._nextMoved === null);
|
// assert(record._nextMoved === null);
|
||||||
|
|
||||||
|
@ -522,10 +524,9 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _addToRemovals(record: IterableChangeRecord_<V>): IterableChangeRecord_<V> {
|
||||||
_addToRemovals(record: CollectionChangeRecord): CollectionChangeRecord {
|
|
||||||
if (this._unlinkedRecords === null) {
|
if (this._unlinkedRecords === null) {
|
||||||
this._unlinkedRecords = new _DuplicateMap();
|
this._unlinkedRecords = new _DuplicateMap<V>();
|
||||||
}
|
}
|
||||||
this._unlinkedRecords.put(record);
|
this._unlinkedRecords.put(record);
|
||||||
record.currentIndex = null;
|
record.currentIndex = null;
|
||||||
|
@ -547,7 +548,7 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_addIdentityChange(record: CollectionChangeRecord, item: any) {
|
_addIdentityChange(record: IterableChangeRecord_<V>, item: V) {
|
||||||
record.item = item;
|
record.item = item;
|
||||||
if (this._identityChangesTail === null) {
|
if (this._identityChangesTail === null) {
|
||||||
this._identityChangesTail = this._identityChangesHead = record;
|
this._identityChangesTail = this._identityChangesHead = record;
|
||||||
|
@ -559,23 +560,23 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
|
|
||||||
|
|
||||||
toString(): string {
|
toString(): string {
|
||||||
const list: any[] /** TODO #9100 */ = [];
|
const list: IterableChangeRecord_<V>[] = [];
|
||||||
this.forEachItem((record: any /** TODO #9100 */) => list.push(record));
|
this.forEachItem((record: IterableChangeRecord_<V>) => list.push(record));
|
||||||
|
|
||||||
const previous: any[] /** TODO #9100 */ = [];
|
const previous: IterableChangeRecord_<V>[] = [];
|
||||||
this.forEachPreviousItem((record: any /** TODO #9100 */) => previous.push(record));
|
this.forEachPreviousItem((record: IterableChangeRecord_<V>) => previous.push(record));
|
||||||
|
|
||||||
const additions: any[] /** TODO #9100 */ = [];
|
const additions: IterableChangeRecord_<V>[] = [];
|
||||||
this.forEachAddedItem((record: any /** TODO #9100 */) => additions.push(record));
|
this.forEachAddedItem((record: IterableChangeRecord_<V>) => additions.push(record));
|
||||||
|
|
||||||
const moves: any[] /** TODO #9100 */ = [];
|
const moves: IterableChangeRecord_<V>[] = [];
|
||||||
this.forEachMovedItem((record: any /** TODO #9100 */) => moves.push(record));
|
this.forEachMovedItem((record: IterableChangeRecord_<V>) => moves.push(record));
|
||||||
|
|
||||||
const removals: any[] /** TODO #9100 */ = [];
|
const removals: IterableChangeRecord_<V>[] = [];
|
||||||
this.forEachRemovedItem((record: any /** TODO #9100 */) => removals.push(record));
|
this.forEachRemovedItem((record: IterableChangeRecord_<V>) => removals.push(record));
|
||||||
|
|
||||||
const identityChanges: any[] /** TODO #9100 */ = [];
|
const identityChanges: IterableChangeRecord_<V>[] = [];
|
||||||
this.forEachIdentityChange((record: any /** TODO #9100 */) => identityChanges.push(record));
|
this.forEachIdentityChange((record: IterableChangeRecord_<V>) => identityChanges.push(record));
|
||||||
|
|
||||||
return 'collection: ' + list.join(', ') + '\n' +
|
return 'collection: ' + list.join(', ') + '\n' +
|
||||||
'previous: ' + previous.join(', ') + '\n' +
|
'previous: ' + previous.join(', ') + '\n' +
|
||||||
|
@ -589,33 +590,33 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
/**
|
/**
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export class CollectionChangeRecord {
|
export class IterableChangeRecord_<V> implements IterableChangeRecord<V> {
|
||||||
currentIndex: number = null;
|
currentIndex: number = null;
|
||||||
previousIndex: number = null;
|
previousIndex: number = null;
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_nextPrevious: CollectionChangeRecord = null;
|
_nextPrevious: IterableChangeRecord_<V> = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_prev: CollectionChangeRecord = null;
|
_prev: IterableChangeRecord_<V> = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_next: CollectionChangeRecord = null;
|
_next: IterableChangeRecord_<V> = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_prevDup: CollectionChangeRecord = null;
|
_prevDup: IterableChangeRecord_<V> = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_nextDup: CollectionChangeRecord = null;
|
_nextDup: IterableChangeRecord_<V> = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_prevRemoved: CollectionChangeRecord = null;
|
_prevRemoved: IterableChangeRecord_<V> = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_nextRemoved: CollectionChangeRecord = null;
|
_nextRemoved: IterableChangeRecord_<V> = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_nextAdded: CollectionChangeRecord = null;
|
_nextAdded: IterableChangeRecord_<V> = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_nextMoved: CollectionChangeRecord = null;
|
_nextMoved: IterableChangeRecord_<V> = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_nextIdentityChange: CollectionChangeRecord = null;
|
_nextIdentityChange: IterableChangeRecord_<V> = null;
|
||||||
|
|
||||||
|
|
||||||
constructor(public item: any, public trackById: any) {}
|
constructor(public item: V, public trackById: any) {}
|
||||||
|
|
||||||
toString(): string {
|
toString(): string {
|
||||||
return this.previousIndex === this.currentIndex ? stringify(this.item) :
|
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
|
// A linked list of CollectionChangeRecords with the same IterableChangeRecord_.item
|
||||||
class _DuplicateItemRecordList {
|
class _DuplicateItemRecordList<V> {
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_head: CollectionChangeRecord = null;
|
_head: IterableChangeRecord_<V> = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_tail: CollectionChangeRecord = null;
|
_tail: IterableChangeRecord_<V> = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append the record to the list of duplicates.
|
* 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.
|
* Note: by design all records in the list of duplicates hold the same value in record.item.
|
||||||
*/
|
*/
|
||||||
add(record: CollectionChangeRecord): void {
|
add(record: IterableChangeRecord_<V>): void {
|
||||||
if (this._head === null) {
|
if (this._head === null) {
|
||||||
this._head = this._tail = record;
|
this._head = this._tail = record;
|
||||||
record._nextDup = null;
|
record._nextDup = null;
|
||||||
|
@ -652,10 +653,10 @@ class _DuplicateItemRecordList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a CollectionChangeRecord having CollectionChangeRecord.trackById == trackById and
|
// Returns a IterableChangeRecord_ having IterableChangeRecord_.trackById == trackById and
|
||||||
// CollectionChangeRecord.currentIndex >= afterIndex
|
// IterableChangeRecord_.currentIndex >= afterIndex
|
||||||
get(trackById: any, afterIndex: number): CollectionChangeRecord {
|
get(trackById: any, afterIndex: number): IterableChangeRecord_<V> {
|
||||||
let record: CollectionChangeRecord;
|
let record: IterableChangeRecord_<V>;
|
||||||
for (record = this._head; record !== null; record = record._nextDup) {
|
for (record = this._head; record !== null; record = record._nextDup) {
|
||||||
if ((afterIndex === null || afterIndex < record.currentIndex) &&
|
if ((afterIndex === null || afterIndex < record.currentIndex) &&
|
||||||
looseIdentical(record.trackById, trackById)) {
|
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.
|
* Returns whether the list of duplicates is empty.
|
||||||
*/
|
*/
|
||||||
remove(record: CollectionChangeRecord): boolean {
|
remove(record: IterableChangeRecord_<V>): boolean {
|
||||||
// todo(vicb)
|
// todo(vicb)
|
||||||
// assert(() {
|
// assert(() {
|
||||||
// // verify that the record being removed is in the list.
|
// // 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;
|
// if (identical(cursor, record)) return true;
|
||||||
// }
|
// }
|
||||||
// return false;
|
// return false;
|
||||||
//});
|
//});
|
||||||
|
|
||||||
const prev: CollectionChangeRecord = record._prevDup;
|
const prev: IterableChangeRecord_<V> = record._prevDup;
|
||||||
const next: CollectionChangeRecord = record._nextDup;
|
const next: IterableChangeRecord_<V> = record._nextDup;
|
||||||
if (prev === null) {
|
if (prev === null) {
|
||||||
this._head = next;
|
this._head = next;
|
||||||
} else {
|
} else {
|
||||||
|
@ -696,41 +697,41 @@ class _DuplicateItemRecordList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DuplicateMap {
|
class _DuplicateMap<V> {
|
||||||
map = new Map<any, _DuplicateItemRecordList>();
|
map = new Map<any, _DuplicateItemRecordList<V>>();
|
||||||
|
|
||||||
put(record: CollectionChangeRecord) {
|
put(record: IterableChangeRecord_<V>) {
|
||||||
const key = record.trackById;
|
const key = record.trackById;
|
||||||
|
|
||||||
let duplicates = this.map.get(key);
|
let duplicates = this.map.get(key);
|
||||||
if (!duplicates) {
|
if (!duplicates) {
|
||||||
duplicates = new _DuplicateItemRecordList();
|
duplicates = new _DuplicateItemRecordList<V>();
|
||||||
this.map.set(key, duplicates);
|
this.map.set(key, duplicates);
|
||||||
}
|
}
|
||||||
duplicates.add(record);
|
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.
|
* 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
|
* 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.
|
* 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_<V> {
|
||||||
const key = trackById;
|
const key = trackById;
|
||||||
const recordList = this.map.get(key);
|
const recordList = this.map.get(key);
|
||||||
return recordList ? recordList.get(trackById, afterIndex) : null;
|
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.
|
* The list of duplicates also is removed from the map if it gets empty.
|
||||||
*/
|
*/
|
||||||
remove(record: CollectionChangeRecord): CollectionChangeRecord {
|
remove(record: IterableChangeRecord_<V>): IterableChangeRecord_<V> {
|
||||||
const key = record.trackById;
|
const key = record.trackById;
|
||||||
const recordList: _DuplicateItemRecordList = this.map.get(key);
|
const recordList: _DuplicateItemRecordList<V> = this.map.get(key);
|
||||||
// Remove the list of duplicates when it gets empty
|
// Remove the list of duplicates when it gets empty
|
||||||
if (recordList.remove(record)) {
|
if (recordList.remove(record)) {
|
||||||
this.map.delete(key);
|
this.map.delete(key);
|
||||||
|
|
|
@ -10,62 +10,64 @@ import {StringMapWrapper} from '../../facade/collection';
|
||||||
import {isJsObject, looseIdentical, stringify} from '../../facade/lang';
|
import {isJsObject, looseIdentical, stringify} from '../../facade/lang';
|
||||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
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<K, V> implements KeyValueDifferFactory {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
supports(obj: any): boolean { return obj instanceof Map || isJsObject(obj); }
|
supports(obj: any): boolean { return obj instanceof Map || isJsObject(obj); }
|
||||||
|
|
||||||
create(cdRef: ChangeDetectorRef): KeyValueDiffer { return new DefaultKeyValueDiffer(); }
|
create<K, V>(cdRef: ChangeDetectorRef): KeyValueDiffer<K, V> {
|
||||||
|
return new DefaultKeyValueDiffer<K, V>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DefaultKeyValueDiffer implements KeyValueDiffer {
|
export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyValueChanges<K, V> {
|
||||||
private _records: Map<any, any> = new Map();
|
private _records: Map<K, V> = new Map<K, V>();
|
||||||
private _mapHead: KeyValueChangeRecord = null;
|
private _mapHead: KeyValueChangeRecord_<K, V> = null;
|
||||||
private _previousMapHead: KeyValueChangeRecord = null;
|
private _previousMapHead: KeyValueChangeRecord_<K, V> = null;
|
||||||
private _changesHead: KeyValueChangeRecord = null;
|
private _changesHead: KeyValueChangeRecord_<K, V> = null;
|
||||||
private _changesTail: KeyValueChangeRecord = null;
|
private _changesTail: KeyValueChangeRecord_<K, V> = null;
|
||||||
private _additionsHead: KeyValueChangeRecord = null;
|
private _additionsHead: KeyValueChangeRecord_<K, V> = null;
|
||||||
private _additionsTail: KeyValueChangeRecord = null;
|
private _additionsTail: KeyValueChangeRecord_<K, V> = null;
|
||||||
private _removalsHead: KeyValueChangeRecord = null;
|
private _removalsHead: KeyValueChangeRecord_<K, V> = null;
|
||||||
private _removalsTail: KeyValueChangeRecord = null;
|
private _removalsTail: KeyValueChangeRecord_<K, V> = null;
|
||||||
|
|
||||||
get isDirty(): boolean {
|
get isDirty(): boolean {
|
||||||
return this._additionsHead !== null || this._changesHead !== null ||
|
return this._additionsHead !== null || this._changesHead !== null ||
|
||||||
this._removalsHead !== null;
|
this._removalsHead !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachItem(fn: (r: KeyValueChangeRecord) => void) {
|
forEachItem(fn: (r: KeyValueChangeRecord<K, V>) => void) {
|
||||||
let record: KeyValueChangeRecord;
|
let record: KeyValueChangeRecord_<K, V>;
|
||||||
for (record = this._mapHead; record !== null; record = record._next) {
|
for (record = this._mapHead; record !== null; record = record._next) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachPreviousItem(fn: (r: KeyValueChangeRecord) => void) {
|
forEachPreviousItem(fn: (r: KeyValueChangeRecord<K, V>) => void) {
|
||||||
let record: KeyValueChangeRecord;
|
let record: KeyValueChangeRecord_<K, V>;
|
||||||
for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
|
for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachChangedItem(fn: (r: KeyValueChangeRecord) => void) {
|
forEachChangedItem(fn: (r: KeyValueChangeRecord<K, V>) => void) {
|
||||||
let record: KeyValueChangeRecord;
|
let record: KeyValueChangeRecord_<K, V>;
|
||||||
for (record = this._changesHead; record !== null; record = record._nextChanged) {
|
for (record = this._changesHead; record !== null; record = record._nextChanged) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachAddedItem(fn: (r: KeyValueChangeRecord) => void) {
|
forEachAddedItem(fn: (r: KeyValueChangeRecord<K, V>) => void) {
|
||||||
let record: KeyValueChangeRecord;
|
let record: KeyValueChangeRecord_<K, V>;
|
||||||
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachRemovedItem(fn: (r: KeyValueChangeRecord) => void) {
|
forEachRemovedItem(fn: (r: KeyValueChangeRecord<K, V>) => void) {
|
||||||
let record: KeyValueChangeRecord;
|
let record: KeyValueChangeRecord_<K, V>;
|
||||||
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
||||||
fn(record);
|
fn(record);
|
||||||
}
|
}
|
||||||
|
@ -86,9 +88,9 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer {
|
||||||
check(map: Map<any, any>|{[k: string]: any}): boolean {
|
check(map: Map<any, any>|{[k: string]: any}): boolean {
|
||||||
this._reset();
|
this._reset();
|
||||||
const records = this._records;
|
const records = this._records;
|
||||||
let oldSeqRecord: KeyValueChangeRecord = this._mapHead;
|
let oldSeqRecord: KeyValueChangeRecord_<K, V> = this._mapHead;
|
||||||
let lastOldSeqRecord: KeyValueChangeRecord = null;
|
let lastOldSeqRecord: KeyValueChangeRecord_<K, V> = null;
|
||||||
let lastNewSeqRecord: KeyValueChangeRecord = null;
|
let lastNewSeqRecord: KeyValueChangeRecord_<K, V> = null;
|
||||||
let seqChanged: boolean = false;
|
let seqChanged: boolean = false;
|
||||||
|
|
||||||
this._forEach(map, (value: any, key: any) => {
|
this._forEach(map, (value: any, key: any) => {
|
||||||
|
@ -106,7 +108,7 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer {
|
||||||
newSeqRecord = records.get(key);
|
newSeqRecord = records.get(key);
|
||||||
this._maybeAddToChanges(newSeqRecord, value);
|
this._maybeAddToChanges(newSeqRecord, value);
|
||||||
} else {
|
} else {
|
||||||
newSeqRecord = new KeyValueChangeRecord(key);
|
newSeqRecord = new KeyValueChangeRecord_<K, V>(key);
|
||||||
records.set(key, newSeqRecord);
|
records.set(key, newSeqRecord);
|
||||||
newSeqRecord.currentValue = value;
|
newSeqRecord.currentValue = value;
|
||||||
this._addToAdditions(newSeqRecord);
|
this._addToAdditions(newSeqRecord);
|
||||||
|
@ -134,7 +136,7 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer {
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_reset() {
|
_reset() {
|
||||||
if (this.isDirty) {
|
if (this.isDirty) {
|
||||||
let record: KeyValueChangeRecord;
|
let record: KeyValueChangeRecord_<K, V>;
|
||||||
// Record the state of the mapping
|
// Record the state of the mapping
|
||||||
for (record = this._previousMapHead = this._mapHead; record !== null; record = record._next) {
|
for (record = this._previousMapHead = this._mapHead; record !== null; record = record._next) {
|
||||||
record._nextPrevious = record._next;
|
record._nextPrevious = record._next;
|
||||||
|
@ -154,8 +156,7 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _truncate(lastRecord: KeyValueChangeRecord_<K, V>, record: KeyValueChangeRecord_<K, V>) {
|
||||||
_truncate(lastRecord: KeyValueChangeRecord, record: KeyValueChangeRecord) {
|
|
||||||
while (record !== null) {
|
while (record !== null) {
|
||||||
if (lastRecord === null) {
|
if (lastRecord === null) {
|
||||||
this._mapHead = null;
|
this._mapHead = null;
|
||||||
|
@ -168,14 +169,15 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer {
|
||||||
record = nextRecord;
|
record = nextRecord;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let rec: KeyValueChangeRecord = this._removalsHead; rec !== null; rec = rec._nextRemoved) {
|
for (let rec: KeyValueChangeRecord_<K, V> = this._removalsHead; rec !== null;
|
||||||
|
rec = rec._nextRemoved) {
|
||||||
rec.previousValue = rec.currentValue;
|
rec.previousValue = rec.currentValue;
|
||||||
rec.currentValue = null;
|
rec.currentValue = null;
|
||||||
this._records.delete(rec.key);
|
this._records.delete(rec.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _maybeAddToChanges(record: KeyValueChangeRecord, newValue: any): void {
|
private _maybeAddToChanges(record: KeyValueChangeRecord_<K, V>, newValue: any): void {
|
||||||
if (!looseIdentical(newValue, record.currentValue)) {
|
if (!looseIdentical(newValue, record.currentValue)) {
|
||||||
record.previousValue = record.currentValue;
|
record.previousValue = record.currentValue;
|
||||||
record.currentValue = newValue;
|
record.currentValue = newValue;
|
||||||
|
@ -183,14 +185,12 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _isInRemovals(record: KeyValueChangeRecord_<K, V>) {
|
||||||
_isInRemovals(record: KeyValueChangeRecord) {
|
|
||||||
return record === this._removalsHead || record._nextRemoved !== null ||
|
return record === this._removalsHead || record._nextRemoved !== null ||
|
||||||
record._prevRemoved !== null;
|
record._prevRemoved !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _addToRemovals(record: KeyValueChangeRecord_<K, V>) {
|
||||||
_addToRemovals(record: KeyValueChangeRecord) {
|
|
||||||
if (this._removalsHead === null) {
|
if (this._removalsHead === null) {
|
||||||
this._removalsHead = this._removalsTail = record;
|
this._removalsHead = this._removalsTail = record;
|
||||||
} else {
|
} else {
|
||||||
|
@ -200,8 +200,7 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _removeFromSeq(prev: KeyValueChangeRecord_<K, V>, record: KeyValueChangeRecord_<K, V>) {
|
||||||
_removeFromSeq(prev: KeyValueChangeRecord, record: KeyValueChangeRecord) {
|
|
||||||
const next = record._next;
|
const next = record._next;
|
||||||
if (prev === null) {
|
if (prev === null) {
|
||||||
this._mapHead = next;
|
this._mapHead = next;
|
||||||
|
@ -211,8 +210,7 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer {
|
||||||
record._next = null;
|
record._next = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _removeFromRemovals(record: KeyValueChangeRecord_<K, V>) {
|
||||||
_removeFromRemovals(record: KeyValueChangeRecord) {
|
|
||||||
const prev = record._prevRemoved;
|
const prev = record._prevRemoved;
|
||||||
const next = record._nextRemoved;
|
const next = record._nextRemoved;
|
||||||
if (prev === null) {
|
if (prev === null) {
|
||||||
|
@ -228,8 +226,7 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer {
|
||||||
record._prevRemoved = record._nextRemoved = null;
|
record._prevRemoved = record._nextRemoved = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _addToAdditions(record: KeyValueChangeRecord_<K, V>) {
|
||||||
_addToAdditions(record: KeyValueChangeRecord) {
|
|
||||||
if (this._additionsHead === null) {
|
if (this._additionsHead === null) {
|
||||||
this._additionsHead = this._additionsTail = record;
|
this._additionsHead = this._additionsTail = record;
|
||||||
} else {
|
} else {
|
||||||
|
@ -238,8 +235,7 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _addToChanges(record: KeyValueChangeRecord_<K, V>) {
|
||||||
_addToChanges(record: KeyValueChangeRecord) {
|
|
||||||
if (this._changesHead === null) {
|
if (this._changesHead === null) {
|
||||||
this._changesHead = this._changesTail = record;
|
this._changesHead = this._changesTail = record;
|
||||||
} else {
|
} else {
|
||||||
|
@ -254,7 +250,7 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer {
|
||||||
const changes: any[] = [];
|
const changes: any[] = [];
|
||||||
const additions: any[] = [];
|
const additions: any[] = [];
|
||||||
const removals: any[] = [];
|
const removals: any[] = [];
|
||||||
let record: KeyValueChangeRecord;
|
let record: KeyValueChangeRecord_<K, V>;
|
||||||
|
|
||||||
for (record = this._mapHead; record !== null; record = record._next) {
|
for (record = this._mapHead; record !== null; record = record._next) {
|
||||||
items.push(stringify(record));
|
items.push(stringify(record));
|
||||||
|
@ -293,24 +289,24 @@ export class DefaultKeyValueDiffer implements KeyValueDiffer {
|
||||||
/**
|
/**
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export class KeyValueChangeRecord {
|
class KeyValueChangeRecord_<K, V> implements KeyValueChangeRecord<K, V> {
|
||||||
previousValue: any = null;
|
previousValue: V = null;
|
||||||
currentValue: any = null;
|
currentValue: V = null;
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_nextPrevious: KeyValueChangeRecord = null;
|
_nextPrevious: KeyValueChangeRecord_<K, V> = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_next: KeyValueChangeRecord = null;
|
_next: KeyValueChangeRecord_<K, V> = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_nextAdded: KeyValueChangeRecord = null;
|
_nextAdded: KeyValueChangeRecord_<K, V> = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_nextRemoved: KeyValueChangeRecord = null;
|
_nextRemoved: KeyValueChangeRecord_<K, V> = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_prevRemoved: KeyValueChangeRecord = null;
|
_prevRemoved: KeyValueChangeRecord_<K, V> = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_nextChanged: KeyValueChangeRecord = null;
|
_nextChanged: KeyValueChangeRecord_<K, V> = null;
|
||||||
|
|
||||||
constructor(public key: any) {}
|
constructor(public key: K) {}
|
||||||
|
|
||||||
toString(): string {
|
toString(): string {
|
||||||
return looseIdentical(this.previousValue, this.currentValue) ?
|
return looseIdentical(this.previousValue, this.currentValue) ?
|
||||||
|
|
|
@ -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.
|
* respond to changes in an iterable by effecting equivalent changes in the DOM.
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export interface IterableDiffer {
|
export interface IterableDiffer<V> {
|
||||||
diff(object: any): any;
|
/**
|
||||||
onDestroy(): any /** TODO #9100 */;
|
* 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<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.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object describing the changes in the `Iterable` collection since last time
|
||||||
|
* `IterableDiffer#diff()` was invoked.
|
||||||
|
*
|
||||||
|
* @stable
|
||||||
|
*/
|
||||||
|
export interface IterableChanges<V> {
|
||||||
|
/**
|
||||||
|
* Iterate over all changes. `IterableChangeRecord` will contain information about changes
|
||||||
|
* to each item.
|
||||||
|
*/
|
||||||
|
forEachItem(fn: (record: IterableChangeRecord<V>) => 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<V>, 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<V>) => void): void;
|
||||||
|
|
||||||
|
/** Iterate over all added items. */
|
||||||
|
forEachAddedItem(fn: (record: IterableChangeRecord<V>) => void): void;
|
||||||
|
|
||||||
|
/** Iterate over all moved items. */
|
||||||
|
forEachMovedItem(fn: (record: IterableChangeRecord<V>) => void): void;
|
||||||
|
|
||||||
|
/** Iterate over all removed items. */
|
||||||
|
forEachRemovedItem(fn: (record: IterableChangeRecord<V>) => void): void;
|
||||||
|
|
||||||
|
/** Iterate over all items which had their identity (as computed by the `trackByFn`) changed. */
|
||||||
|
forEachIdentityChange(fn: (record: IterableChangeRecord<V>) => void): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record representing the item change information.
|
||||||
|
*
|
||||||
|
* @stable
|
||||||
|
*/
|
||||||
|
export interface IterableChangeRecord<V> {
|
||||||
|
/** 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<V> extends IterableChangeRecord<V> {}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An optional function passed into {@link NgFor} that defines how to track
|
* An optional function passed into {@link NgFor} that defines how to track
|
||||||
* items in an iterable (e.g. by index or id)
|
* 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 {
|
export interface IterableDifferFactory {
|
||||||
supports(objects: any): boolean;
|
supports(objects: any): boolean;
|
||||||
create(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): IterableDiffer;
|
create<V>(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): IterableDiffer<V>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,7 +135,11 @@ export interface IterableDifferFactory {
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export class IterableDiffers {
|
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 {
|
static create(factories: IterableDifferFactory[], parent?: IterableDiffers): IterableDiffers {
|
||||||
if (isPresent(parent)) {
|
if (isPresent(parent)) {
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Optional, Provider, SkipSelf} from '../../di';
|
import {Optional, Provider, SkipSelf} from '../../di';
|
||||||
import {ListWrapper} from '../../facade/collection';
|
|
||||||
import {isPresent} from '../../facade/lang';
|
|
||||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,9 +15,83 @@ import {ChangeDetectorRef} from '../change_detector_ref';
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export interface KeyValueDiffer {
|
export interface KeyValueDiffer<K, V> {
|
||||||
diff(object: any): any /** TODO #9100 */;
|
/**
|
||||||
onDestroy(): any /** TODO #9100 */;
|
* 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<K, V>): KeyValueChanges<K, V>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<string, V>;
|
||||||
|
// TODO(TS2.1): diff<KP extends string>(this: KeyValueDiffer<KP, V>, object: Record<KP, V>):
|
||||||
|
// KeyValueDiffer<KP, V>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object describing the changes in the `Map` or `{[k:string]: string}` since last time
|
||||||
|
* `KeyValueDiffer#diff()` was invoked.
|
||||||
|
*
|
||||||
|
* @stable
|
||||||
|
*/
|
||||||
|
export interface KeyValueChanges<K, V> {
|
||||||
|
/**
|
||||||
|
* Iterate over all changes. `KeyValueChangeRecord` will contain information about changes
|
||||||
|
* to each item.
|
||||||
|
*/
|
||||||
|
forEachItem(fn: (r: KeyValueChangeRecord<K, V>) => void): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over changes in the order of original Map showing where the original items
|
||||||
|
* have moved.
|
||||||
|
*/
|
||||||
|
forEachPreviousItem(fn: (r: KeyValueChangeRecord<K, V>) => void): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over all keys for which values have changed.
|
||||||
|
*/
|
||||||
|
forEachChangedItem(fn: (r: KeyValueChangeRecord<K, V>) => void): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over all added items.
|
||||||
|
*/
|
||||||
|
forEachAddedItem(fn: (r: KeyValueChangeRecord<K, V>) => void): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over all removed items.
|
||||||
|
*/
|
||||||
|
forEachRemovedItem(fn: (r: KeyValueChangeRecord<K, V>) => void): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record representing the item change information.
|
||||||
|
*
|
||||||
|
* @stable
|
||||||
|
*/
|
||||||
|
export interface KeyValueChangeRecord<K, V> {
|
||||||
|
/**
|
||||||
|
* 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
|
* @stable
|
||||||
*/
|
*/
|
||||||
export interface KeyValueDifferFactory {
|
export interface KeyValueDifferFactory {
|
||||||
|
/**
|
||||||
|
* Test to see if the differ knows how to diff this kind of object.
|
||||||
|
*/
|
||||||
supports(objects: any): boolean;
|
supports(objects: any): boolean;
|
||||||
create(cdRef: ChangeDetectorRef): KeyValueDiffer;
|
|
||||||
|
/**
|
||||||
|
* Create a `KeyValueDiffer`.
|
||||||
|
*/
|
||||||
|
create<K, V>(cdRef: ChangeDetectorRef): KeyValueDiffer<K, V>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,16 +116,19 @@ export interface KeyValueDifferFactory {
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export class KeyValueDiffers {
|
export class KeyValueDiffers {
|
||||||
constructor(public factories: KeyValueDifferFactory[]) {}
|
/**
|
||||||
|
* @deprecated v4.0.0 - Should be private.
|
||||||
|
*/
|
||||||
|
factories: KeyValueDifferFactory[];
|
||||||
|
|
||||||
static create(factories: KeyValueDifferFactory[], parent?: KeyValueDiffers): KeyValueDiffers {
|
constructor(factories: KeyValueDifferFactory[]) { this.factories = factories; }
|
||||||
if (isPresent(parent)) {
|
|
||||||
|
static create<S>(factories: KeyValueDifferFactory[], parent?: KeyValueDiffers): KeyValueDiffers {
|
||||||
|
if (parent) {
|
||||||
const copied = parent.factories.slice();
|
const copied = parent.factories.slice();
|
||||||
factories = factories.concat(copied);
|
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<S>(factories: KeyValueDifferFactory[]): Provider {
|
||||||
return {
|
return {
|
||||||
provide: KeyValueDiffers,
|
provide: KeyValueDiffers,
|
||||||
useFactory: (parent: KeyValueDiffers) => {
|
useFactory: (parent: KeyValueDiffers) => {
|
||||||
if (!parent) {
|
if (!parent) {
|
||||||
// Typically would occur when calling KeyValueDiffers.extend inside of dependencies passed
|
// Typically would occur when calling KeyValueDiffers.extend inside of dependencies passed
|
||||||
// to
|
// to bootstrap(), which would override default pipes instead of extending them.
|
||||||
// bootstrap(), which would override default pipes instead of extending them.
|
|
||||||
throw new Error('Cannot extend KeyValueDiffers without a parent injector');
|
throw new Error('Cannot extend KeyValueDiffers without a parent injector');
|
||||||
}
|
}
|
||||||
return KeyValueDiffers.create(factories, parent);
|
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));
|
const factory = this.factories.find(f => f.supports(kv));
|
||||||
if (isPresent(factory)) {
|
if (factory) {
|
||||||
return factory;
|
return factory;
|
||||||
} else {
|
}
|
||||||
throw new Error(`Cannot find a differ supporting object '${kv}'`);
|
throw new Error(`Cannot find a differ supporting object '${kv}'`);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,11 @@ import {kvChangesAsString} from '../../change_detection/util';
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('keyvalue differ', function() {
|
describe('keyvalue differ', function() {
|
||||||
describe('DefaultKeyValueDiffer', function() {
|
describe('DefaultKeyValueDiffer', function() {
|
||||||
let differ: DefaultKeyValueDiffer;
|
let differ: DefaultKeyValueDiffer<any, any>;
|
||||||
let m: Map<any, any>;
|
let m: Map<any, any>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
differ = new DefaultKeyValueDiffer();
|
differ = new DefaultKeyValueDiffer<string, any>();
|
||||||
m = new Map();
|
m = new Map();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -205,14 +205,8 @@ export interface ClassProvider {
|
||||||
useClass: Type<any>;
|
useClass: Type<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @deprecated */
|
||||||
export declare class CollectionChangeRecord {
|
export interface CollectionChangeRecord<V> extends IterableChangeRecord<V> {
|
||||||
currentIndex: number;
|
|
||||||
item: any;
|
|
||||||
previousIndex: number;
|
|
||||||
trackById: any;
|
|
||||||
constructor(item: any, trackById: any);
|
|
||||||
toString(): string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
|
@ -358,21 +352,21 @@ export declare class DebugNode {
|
||||||
constructor(nativeNode: any, parent: DebugNode, _debugInfo: RenderDebugInfo);
|
constructor(nativeNode: any, parent: DebugNode, _debugInfo: RenderDebugInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @deprecated */
|
||||||
export declare class DefaultIterableDiffer implements IterableDiffer {
|
export declare class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChanges<V> {
|
||||||
collection: any;
|
collection: any;
|
||||||
isDirty: boolean;
|
isDirty: boolean;
|
||||||
length: number;
|
length: number;
|
||||||
constructor(_trackByFn?: TrackByFn);
|
constructor(_trackByFn?: TrackByFn);
|
||||||
check(collection: any): boolean;
|
check(collection: V[] | Set<V>[] | any): boolean;
|
||||||
diff(collection: any): DefaultIterableDiffer;
|
diff(collection: V[] | Set<V>[] | any): DefaultIterableDiffer<V>;
|
||||||
forEachAddedItem(fn: Function): void;
|
forEachAddedItem(fn: (record: IterableChangeRecord_<V>) => void): void;
|
||||||
forEachIdentityChange(fn: Function): void;
|
forEachIdentityChange(fn: (record: IterableChangeRecord_<V>) => void): void;
|
||||||
forEachItem(fn: Function): void;
|
forEachItem(fn: (record: IterableChangeRecord_<V>) => void): void;
|
||||||
forEachMovedItem(fn: Function): void;
|
forEachMovedItem(fn: (record: IterableChangeRecord_<V>) => void): void;
|
||||||
forEachOperation(fn: (item: CollectionChangeRecord, previousIndex: number, currentIndex: number) => void): void;
|
forEachOperation(fn: (item: IterableChangeRecord_<V>, previousIndex: number, currentIndex: number) => void): void;
|
||||||
forEachPreviousItem(fn: Function): void;
|
forEachPreviousItem(fn: (record: IterableChangeRecord_<V>) => void): void;
|
||||||
forEachRemovedItem(fn: Function): void;
|
forEachRemovedItem(fn: (record: IterableChangeRecord_<V>) => void): void;
|
||||||
onDestroy(): void;
|
onDestroy(): void;
|
||||||
toString(): string;
|
toString(): string;
|
||||||
}
|
}
|
||||||
|
@ -511,20 +505,38 @@ export declare const Input: InputDecorator;
|
||||||
export declare function isDevMode(): boolean;
|
export declare function isDevMode(): boolean;
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export interface IterableDiffer {
|
export interface IterableChangeRecord<V> {
|
||||||
diff(object: any): any;
|
currentIndex: number;
|
||||||
onDestroy(): any;
|
item: V;
|
||||||
|
previousIndex: number;
|
||||||
|
trackById: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @stable */
|
||||||
|
export interface IterableChanges<V> {
|
||||||
|
forEachAddedItem(fn: (record: IterableChangeRecord<V>) => void): void;
|
||||||
|
forEachIdentityChange(fn: (record: IterableChangeRecord<V>) => void): void;
|
||||||
|
forEachItem(fn: (record: IterableChangeRecord<V>) => void): void;
|
||||||
|
forEachMovedItem(fn: (record: IterableChangeRecord<V>) => void): void;
|
||||||
|
forEachOperation(fn: (record: IterableChangeRecord<V>, previousIndex: number, currentIndex: number) => void): void;
|
||||||
|
forEachPreviousItem(fn: (record: IterableChangeRecord<V>) => void): void;
|
||||||
|
forEachRemovedItem(fn: (record: IterableChangeRecord<V>) => void): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @stable */
|
||||||
|
export interface IterableDiffer<V> {
|
||||||
|
diff(object: V[] | Set<V> | any): IterableChanges<V>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export interface IterableDifferFactory {
|
export interface IterableDifferFactory {
|
||||||
create(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): IterableDiffer;
|
create<V>(cdRef: ChangeDetectorRef, trackByFn?: TrackByFn): IterableDiffer<V>;
|
||||||
supports(objects: any): boolean;
|
supports(objects: any): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare class IterableDiffers {
|
export declare class IterableDiffers {
|
||||||
factories: IterableDifferFactory[];
|
/** @deprecated */ factories: IterableDifferFactory[];
|
||||||
constructor(factories: IterableDifferFactory[]);
|
constructor(factories: IterableDifferFactory[]);
|
||||||
find(iterable: any): IterableDifferFactory;
|
find(iterable: any): IterableDifferFactory;
|
||||||
static create(factories: IterableDifferFactory[], parent?: IterableDiffers): IterableDiffers;
|
static create(factories: IterableDifferFactory[], parent?: IterableDiffers): IterableDiffers;
|
||||||
|
@ -535,33 +547,42 @@ export declare class IterableDiffers {
|
||||||
export declare function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSequenceMetadata;
|
export declare function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSequenceMetadata;
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare class KeyValueChangeRecord {
|
export interface KeyValueChangeRecord<K, V> {
|
||||||
currentValue: any;
|
currentValue: V;
|
||||||
key: any;
|
key: K;
|
||||||
previousValue: any;
|
previousValue: V;
|
||||||
constructor(key: any);
|
|
||||||
toString(): string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export interface KeyValueDiffer {
|
export interface KeyValueChanges<K, V> {
|
||||||
diff(object: any): any;
|
forEachAddedItem(fn: (r: KeyValueChangeRecord<K, V>) => void): void;
|
||||||
onDestroy(): any;
|
forEachChangedItem(fn: (r: KeyValueChangeRecord<K, V>) => void): void;
|
||||||
|
forEachItem(fn: (r: KeyValueChangeRecord<K, V>) => void): void;
|
||||||
|
forEachPreviousItem(fn: (r: KeyValueChangeRecord<K, V>) => void): void;
|
||||||
|
forEachRemovedItem(fn: (r: KeyValueChangeRecord<K, V>) => void): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @stable */
|
||||||
|
export interface KeyValueDiffer<K, V> {
|
||||||
|
diff(object: Map<K, V>): KeyValueChanges<K, V>;
|
||||||
|
diff(object: {
|
||||||
|
[key: string]: V;
|
||||||
|
}): KeyValueChanges<string, V>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export interface KeyValueDifferFactory {
|
export interface KeyValueDifferFactory {
|
||||||
create(cdRef: ChangeDetectorRef): KeyValueDiffer;
|
create<K, V>(cdRef: ChangeDetectorRef): KeyValueDiffer<K, V>;
|
||||||
supports(objects: any): boolean;
|
supports(objects: any): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare class KeyValueDiffers {
|
export declare class KeyValueDiffers {
|
||||||
factories: KeyValueDifferFactory[];
|
/** @deprecated */ factories: KeyValueDifferFactory[];
|
||||||
constructor(factories: KeyValueDifferFactory[]);
|
constructor(factories: KeyValueDifferFactory[]);
|
||||||
find(kv: Object): KeyValueDifferFactory;
|
find(kv: any): KeyValueDifferFactory;
|
||||||
static create(factories: KeyValueDifferFactory[], parent?: KeyValueDiffers): KeyValueDiffers;
|
static create<S>(factories: KeyValueDifferFactory[], parent?: KeyValueDiffers): KeyValueDiffers;
|
||||||
static extend(factories: KeyValueDifferFactory[]): Provider;
|
static extend<S>(factories: KeyValueDifferFactory[]): Provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @experimental */
|
/** @experimental */
|
||||||
|
|
Loading…
Reference in New Issue