Skip to content

Commit a10c02c

Browse files
committed
feat(iterable_differ): support immutable lists
Closes #7127
1 parent 9936e34 commit a10c02c

4 files changed

Lines changed: 65 additions & 20 deletions

File tree

modules/angular2/src/core/change_detection/differs/default_iterable_differ.ts

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ export class DefaultIterableDiffer implements IterableDiffer {
109109

110110
onDestroy() {}
111111

112-
// todo(vicb): optim for UnmodifiableListView (frozen arrays)
113112
check(collection: any): boolean {
114113
this._reset();
115114

@@ -119,24 +118,27 @@ export class DefaultIterableDiffer implements IterableDiffer {
119118
var item;
120119
var itemTrackBy;
121120
if (isArray(collection)) {
122-
var list = collection;
123-
this._length = collection.length;
124-
125-
for (index = 0; index < this._length; index++) {
126-
item = list[index];
127-
itemTrackBy = this._trackByFn(index, item);
128-
if (record === null || !looseIdentical(record.trackById, itemTrackBy)) {
129-
record = this._mismatch(record, item, itemTrackBy, index);
130-
mayBeDirty = true;
131-
} else {
132-
if (mayBeDirty) {
133-
// TODO(misko): can we limit this to duplicates only?
134-
record = this._verifyReinsertion(record, item, itemTrackBy, index);
121+
if (collection !== this._collection || !ListWrapper.isImmutable(collection)) {
122+
var list = collection;
123+
this._length = collection.length;
124+
125+
for (index = 0; index < this._length; index++) {
126+
item = list[index];
127+
itemTrackBy = this._trackByFn(index, item);
128+
if (record === null || !looseIdentical(record.trackById, itemTrackBy)) {
129+
record = this._mismatch(record, item, itemTrackBy, index);
130+
mayBeDirty = true;
131+
} else {
132+
if (mayBeDirty) {
133+
// TODO(misko): can we limit this to duplicates only?
134+
record = this._verifyReinsertion(record, item, itemTrackBy, index);
135+
}
136+
if (!looseIdentical(record.item, item)) this._addIdentityChange(record, item);
135137
}
136-
if (!looseIdentical(record.item, item)) this._addIdentityChange(record, item);
137-
}
138138

139-
record = record._next;
139+
record = record._next;
140+
}
141+
this._truncate(record);
140142
}
141143
} else {
142144
index = 0;
@@ -156,9 +158,9 @@ export class DefaultIterableDiffer implements IterableDiffer {
156158
index++;
157159
});
158160
this._length = index;
161+
this._truncate(record);
159162
}
160163

161-
this._truncate(record);
162164
this._collection = collection;
163165
return this.isDirty;
164166
}

modules/angular2/src/facade/collection.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
library facade.collection;
22

3-
import 'dart:collection' show IterableBase;
3+
import 'dart:collection' show IterableBase, UnmodifiableListView;
44
import 'dart:convert' show JsonEncoder;
55
export 'dart:core' show Iterator, Map, List, Set;
66
import 'dart:math' show max, min;
@@ -106,6 +106,9 @@ class ListWrapper {
106106
static List createFixedSize(int size) => new List(size);
107107
static List createGrowableSize(int size) =>
108108
new List.generate(size, (_) => null, growable: true);
109+
static UnmodifiableListView createImmutable(List input) {
110+
return new UnmodifiableListView(input);
111+
}
109112

110113
static bool contains(List m, k) => m.contains(k);
111114
static int indexOf(List list, value, [int startIndex = 0]) =>
@@ -221,6 +224,10 @@ class ListWrapper {
221224
}
222225
return solution;
223226
}
227+
228+
static bool isImmutable(List l) {
229+
return l is UnmodifiableListView;
230+
}
224231
}
225232

226233
bool isListLikeIterable(obj) => obj is Iterable;

modules/angular2/src/facade/collection.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,11 @@ export class ListWrapper {
178178
static createFixedSize(size: number): any[] { return new Array(size); }
179179
static createGrowableSize(size: number): any[] { return new Array(size); }
180180
static clone<T>(array: T[]): T[] { return array.slice(0); }
181+
static createImmutable<T>(array: T[]): T[] {
182+
var result = ListWrapper.clone(array);
183+
Object.seal(result);
184+
return result;
185+
}
181186
static forEachWithIndex<T>(array: T[], fn: (t: T, n: number) => void) {
182187
for (var i = 0; i < array.length; i++) {
183188
fn(array[i], i);
@@ -265,6 +270,8 @@ export class ListWrapper {
265270
}
266271
return solution;
267272
}
273+
274+
static isImmutable(list: any[]): boolean { return Object.isSealed(list); }
268275
}
269276

270277
export function isListLikeIterable(obj: any): boolean {

modules/angular2/test/core/change_detection/differs/default_iterable_differ_spec.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ class ComplexItem {
3131
toString() { return `{id: ${this.id}, color: ${this.color}}` }
3232
}
3333

34-
// todo(vicb): UnmodifiableListView / frozen object when implemented
3534
export function main() {
3635
describe('iterable differ', function() {
3736
describe('DefaultIterableDiffer', function() {
@@ -314,6 +313,36 @@ export function main() {
314313
}));
315314
});
316315

316+
it('should not diff immutable collections if they are the same', () => {
317+
// Note: Use trackBy to know if diffing happened
318+
var trackByCount = 0;
319+
var trackBy = (index: number, item: any): any => {
320+
trackByCount++;
321+
return item;
322+
};
323+
var differ = new DefaultIterableDiffer(trackBy);
324+
var l1 = ListWrapper.createImmutable([1]);
325+
326+
differ.check(l1);
327+
expect(trackByCount).toBe(1);
328+
expect(differ.toString())
329+
.toEqual(
330+
iterableChangesAsString({collection: ['1[null->0]'], additions: ['1[null->0]']}));
331+
332+
333+
trackByCount = 0;
334+
differ.check(l1);
335+
expect(trackByCount).toBe(0);
336+
expect(differ.toString())
337+
.toEqual(iterableChangesAsString({collection: ['1'], previous: ['1']}));
338+
339+
trackByCount = 0;
340+
differ.check(l1);
341+
expect(trackByCount).toBe(0);
342+
expect(differ.toString())
343+
.toEqual(iterableChangesAsString({collection: ['1'], previous: ['1']}));
344+
});
345+
317346
describe('diff', () => {
318347
it('should return self when there is a change',
319348
() => { expect(differ.diff(['a', 'b'])).toBe(differ); });

0 commit comments

Comments
 (0)