/
iterable_differs.ts
217 lines (192 loc) · 6.89 KB
/
iterable_differs.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ɵɵdefineInjectable} from '../../di/interface/defs';
import {StaticProvider} from '../../di/interface/provider';
import {Optional, SkipSelf} from '../../di/metadata';
import {DefaultIterableDifferFactory} from '../differs/default_iterable_differ';
/**
* A type describing supported iterable types.
*
* @publicApi
*/
export type NgIterable<T> = Array<T>|Iterable<T>;
/**
* A strategy for tracking changes over time to an iterable. Used by {@link NgForOf} to
* respond to changes in an iterable by effecting equivalent changes in the DOM.
*
* @publicApi
*/
export interface IterableDiffer<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: NgIterable<V>|undefined|null): IterableChanges<V>|null;
}
/**
* An object describing the changes in the `Iterable` collection since last time
* `IterableDiffer#diff()` was invoked.
*
* @publicApi
*/
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|null,
currentIndex: number|null) => 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 `TrackByFunction`)
* changed.
*/
forEachIdentityChange(fn: (record: IterableChangeRecord<V>) => void): void;
}
/**
* Record representing the item change information.
*
* @publicApi
*/
export interface IterableChangeRecord<V> {
/** Current index of the item in `Iterable` or null if removed. */
readonly currentIndex: number|null;
/** Previous index of the item in `Iterable` or null if added. */
readonly previousIndex: number|null;
/** The item. */
readonly item: V;
/** Track by identity as computed by the `TrackByFunction`. */
readonly trackById: any;
}
/**
* An optional function passed into the `NgForOf` directive that defines how to track
* changes for items in an iterable.
* The function takes the iteration index and item ID.
* When supplied, Angular tracks changes by the return value of the function.
*
* @publicApi
*/
export interface TrackByFunction<T> {
(index: number, item: T): any;
}
/**
* Provides a factory for {@link IterableDiffer}.
*
* @publicApi
*/
export interface IterableDifferFactory {
supports(objects: any): boolean;
create<V>(trackByFn?: TrackByFunction<V>): IterableDiffer<V>;
}
/**
* A repository of different iterable diffing strategies used by NgFor, NgClass, and others.
*
* @publicApi
*/
export class IterableDiffers {
/** @nocollapse */
static ɵprov = ɵɵdefineInjectable({
token: IterableDiffers,
providedIn: 'root',
factory: () => new IterableDiffers([new DefaultIterableDifferFactory()])
});
/**
* @deprecated v4.0.0 - Should be private
*/
factories: IterableDifferFactory[];
constructor(factories: IterableDifferFactory[]) {
this.factories = factories;
}
static create(factories: IterableDifferFactory[], parent?: IterableDiffers): IterableDiffers {
if (parent != null) {
const copied = parent.factories.slice();
factories = factories.concat(copied);
}
return new IterableDiffers(factories);
}
/**
* Takes an array of {@link IterableDifferFactory} and returns a provider used to extend the
* inherited {@link IterableDiffers} instance with the provided factories and return a new
* {@link IterableDiffers} instance.
*
* @usageNotes
* ### Example
*
* 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.
*
* ```
* @Component({
* viewProviders: [
* IterableDiffers.extend([new ImmutableListDiffer()])
* ]
* })
* ```
*/
static extend(factories: IterableDifferFactory[]): StaticProvider {
return {
provide: IterableDiffers,
useFactory: (parent: IterableDiffers) => {
if (!parent) {
// Typically would occur when calling IterableDiffers.extend inside of dependencies passed
// to
// bootstrap(), which would override default pipes instead of extending them.
throw new Error('Cannot extend IterableDiffers without a parent injector');
}
return IterableDiffers.create(factories, parent);
},
// Dependency technically isn't optional, but we can provide a better error message this way.
deps: [[IterableDiffers, new SkipSelf(), new Optional()]]
};
}
find(iterable: any): IterableDifferFactory {
const factory = this.factories.find(f => f.supports(iterable));
if (factory != null) {
return factory;
} else {
throw new Error(`Cannot find a differ supporting object '${iterable}' of type '${
getTypeNameForDebugging(iterable)}'`);
}
}
}
export function getTypeNameForDebugging(type: any): string {
return type['name'] || typeof type;
}