Skip to content

Commit dc64928

Browse files
committed
feat(core): added directive NgForTrackByKey
Example: *ngFor="let item of items trackByKey 'key'"
1 parent 03f54e1 commit dc64928

3 files changed

Lines changed: 71 additions & 0 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { NgForOf } from '@angular/common';
2+
import { Directive, Host, Input, Optional } from '@angular/core';
3+
4+
@Directive({
5+
selector: '[ngForTrackByKey]'
6+
})
7+
export class NgForTrackByKeyDirective<T> {
8+
9+
@Input()
10+
set ngForTrackByKey(key: keyof T) {
11+
if (key) {
12+
this.ngFor.ngForTrackBy = (index: number, item: T): T[keyof T] => item[key];
13+
} else {
14+
this.ngFor.ngForTrackBy = undefined;
15+
}
16+
17+
this.ngFor['_differ'] = null;
18+
this.ngFor['_ngForOfDirty'] = true;
19+
this.ngFor.ngDoCheck();
20+
}
21+
22+
constructor(@Host() @Optional() private ngFor: NgForOf<T>) {
23+
if (!ngFor) {
24+
throw new Error('TrackByKey should use with *ngFor!');
25+
}
26+
}
27+
28+
}

projects/platform/src/lib/platform.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { ReturnDirective } from './directives/return.directive';
1212
import { RouteDirective } from './directives/route.directive';
1313
import { SetPropsDirective } from './directives/set-props.directive';
1414
import { TimeoutDirective } from './directives/timeout.directive';
15+
import { NgForTrackByKeyDirective } from './directives/track-by-key.directive';
1516
import { UseEffectDirective } from './directives/use-effect.directive';
1617
import { UseReducerDirective } from './directives/use-reducer.directive';
1718
import { UseStateDirective } from './directives/use-state.directive';
@@ -32,6 +33,7 @@ const DIRECTIVES = [
3233
RouteDirective,
3334
SetPropsDirective,
3435
TimeoutDirective,
36+
NgForTrackByKeyDirective,
3537
UseReducerDirective,
3638
UseStateDirective,
3739
UseEffectDirective,
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { CommonModule } from '@angular/common';
2+
import { createHostComponentFactory, SpectatorWithHost } from '@netbasal/spectator';
3+
import { NgForTrackByKeyDirective } from '../../lib/directives/track-by-key.directive';
4+
5+
describe('NgForTrackByKeyDirective', () => {
6+
let host: SpectatorWithHost<NgForTrackByKeyDirective<any>>;
7+
const create = createHostComponentFactory({
8+
component: NgForTrackByKeyDirective,
9+
declarations: [ NgForTrackByKeyDirective ],
10+
imports: [ CommonModule ]
11+
});
12+
13+
it('should add generate trackBy fn by key', () => {
14+
host = create(`
15+
<ng-container *ngFor="
16+
let item of [
17+
{ animal: '🦊' },
18+
{ animal: '🦄' }
19+
] trackByKey 'animal'">
20+
{{ item.animal }}
21+
</ng-container>
22+
`);
23+
24+
expect(host.element).toHaveText('🦊 🦄');
25+
});
26+
27+
it('should throw exception', () => {
28+
expect(() => {
29+
host = create(`
30+
<ng-container *ngFor="
31+
let item of [
32+
{ animal: '🦊' },
33+
null
34+
] trackByKey 'animal'">
35+
{{ item?.animal }}
36+
</ng-container>
37+
`);
38+
}).toThrow(`Cannot read property 'animal' of null`);
39+
});
40+
41+
});

0 commit comments

Comments
 (0)