typescript - Angular global trackBy-egenskap direktiv med strikt typkontroll
Jag vill skapa en unik trackBy-direktiv genom att skicka ett egenskapsnamn för att spåra ngFor-elementet, detta är koden:
import { NgForOf } from '@angular/common';
import { Directive, Host, Input } from '@angular/core';
@Directive({
selector: '[ngForTrackByProp]',
})
export class NgForTrackByPropDirective<T> {
private propertyName: keyof T;
constructor(@Host() public ngForOf: NgForOf<T>) {
this.ngForOf.ngForTrackBy = this.trackBy.bind(this);
}
trackBy(index: number, item: T) {
if (!this.propertyName) {
throw new Error(`Property name not defined`);
}
if (typeof item[this.propertyName] === 'undefined') {
throw new Error(`Property "${this.propertyName}" is undefined`);
}
const value = item[this.propertyName];
console.log(
`Item "${index}" trackByProp "${this.propertyName}" with value "${value}"`
);
return value;
}
@Input()
set ngForTrackByProp(value: keyof T) {
this.propertyName = value;
}
static ngTemplateContextGuard<T>(
dir: NgForTrackByPropDirective<T>,
ctx: any
): ctx is NgForTrackByPropDirective<T> {
return true;
}
}
Användning :
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `
<ul *ngFor="let item of list; trackByProp: 'id'">
<li>{{ item.id }} {{ item.name }}</li>
</ul>`,
})
export class AppComponent {
list = [
{ id: 0, name: 'foo' },
{ id: 1, name: 'bar' },
{ id: 2, name: 'baz' },
];
}
detta är ONLINE-DEMON
Koden fungerar, men jag vill försäkra mig om att egenskapen som skickas är en nyckel till objektet i samlingen och om den inte är det, låt felet bli ett kompilationsfel, t.ex.:
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `
<ul *ngFor="let item of list; trackByProp: 'XXXXXXXXXXXXX'">
<li>{{ item.id }} {{ item.name }}</li>
</ul>`,
})
export class AppComponent {
list = [
{ id: 0, name: 'foo' },
{ id: 1, name: 'bar' },
{ id: 2, name: 'baz' },
];
}
sidonotering, min tsconfig.json:
"angularCompilerOptions": {
"fullTemplateTypeCheck": true,
"strictTemplates": true,
"strictInjectionParameters": true
}
Svar #1
Du kan begränsa typen av ett objekt i en passerande array genom att definiera ngForOf @Input-egenskap:
import { NgIterable, ... } from '@angular/core';
...
export class NgForTrackByPropDirective<T> {
@Input() ngForOf: NgIterable<T>;
constructor(@Host() public ngForOfDir: NgForOf<T>) {
this.ngForOfDir.ngForTrackBy = this.trackBy.bind(this);
}
...
Gafflad Stackblitz
