feat(ngFor): add custom trackBy function support

Make it possible to track items in iterables in custom ways (e.g. by ID or index), rather than simply by identity.

Closes #6779
This commit is contained in:
Kara Erickson
2016-02-01 18:31:26 -08:00
committed by Kara
parent cfef76f683
commit cee2318110
10 changed files with 192 additions and 62 deletions

View File

@ -14,11 +14,17 @@ import {
} from 'angular2/src/core/change_detection/differs/default_iterable_differ';
import {NumberWrapper} from 'angular2/src/facade/lang';
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
import {ListWrapper} from 'angular2/src/facade/collection';
import {TestIterable} from '../../../core/change_detection/iterable';
import {iterableChangesAsString} from '../../../core/change_detection/util';
class ItemWithId {
constructor(private id: string) {}
toString() { return `{id: ${this.id}}` }
}
// todo(vicb): UnmodifiableListView / frozen object when implemented
export function main() {
describe('iterable differ', function() {
@ -254,6 +260,7 @@ export function main() {
}));
});
it('should support duplicates', () => {
let l = ['a', 'a', 'a', 'b', 'b'];
differ.check(l);
@ -324,5 +331,80 @@ export function main() {
});
});
});
describe('trackBy function', function() {
var differ;
var trackByItemId = (index: number, item: any): any => item.id;
var buildItemList =
(list: string[]) => { return list.map((val) => {return new ItemWithId(val)}) };
beforeEach(() => { differ = new DefaultIterableDiffer(trackByItemId); });
it('should not treat maps as new with track by function', () => {
let l = buildItemList(['a', 'b', 'c']);
differ.check(l);
expect(differ.toString())
.toEqual(iterableChangesAsString({
collection: [`{id: a}[null->0]`, `{id: b}[null->1]`, `{id: c}[null->2]`],
additions: [`{id: a}[null->0]`, `{id: b}[null->1]`, `{id: c}[null->2]`]
}));
l = buildItemList(['a', 'b', 'c']);
differ.check(l);
expect(differ.toString())
.toEqual(iterableChangesAsString({
collection: [`{id: a}`, `{id: b}`, `{id: c}`],
previous: [`{id: a}`, `{id: b}`, `{id: c}`]
}));
});
it('should track moves normally with track by function', () => {
let l = buildItemList(['a', 'b', 'c']);
differ.check(l);
l = buildItemList(['b', 'a', 'c']);
differ.check(l);
expect(differ.toString())
.toEqual(iterableChangesAsString({
collection: ['{id: b}[1->0]', '{id: a}[0->1]', '{id: c}'],
previous: ['{id: a}[0->1]', '{id: b}[1->0]', '{id: c}'],
moves: ['{id: b}[1->0]', '{id: a}[0->1]']
}));
});
it('should track duplicate reinsertion normally with track by function', () => {
let l = buildItemList(['a', 'a']);
differ.check(l);
l = buildItemList(['b', 'a', 'a']);
differ.check(l);
expect(differ.toString())
.toEqual(iterableChangesAsString({
collection: ['{id: b}[null->0]', '{id: a}[0->1]', '{id: a}[1->2]'],
previous: ['{id: a}[0->1]', '{id: a}[1->2]'],
moves: ['{id: a}[0->1]', '{id: a}[1->2]'],
additions: ['{id: b}[null->0]']
}));
});
it('should track removals normally with track by function', () => {
let l = buildItemList(['a', 'b', 'c']);
differ.check(l);
ListWrapper.removeAt(l, 2);
differ.check(l);
expect(differ.toString())
.toEqual(iterableChangesAsString({
collection: ['{id: a}', '{id: b}'],
previous: ['{id: a}', '{id: b}', '{id: c}[2->null]'],
removals: ['{id: c}[2->null]']
}));
});
});
});
}