javascript / expert
Snippet
Typisierte Content Projection mit Angular Template-Verträgen
Kontextobjekte struktureller Direktiven sind in Angular standardmäßig ungetypt, sodass Template-Autoren keine Kompilierzeit-Rückmeldung erhalten, wenn sie eine Kontextvariable wie `let-idx="indx"` falsch schreiben. Die statische Methode `ngTemplateContextGuard` ist ein Compiler-Hook, nach dem Angulars Template-Type-Checker gezielt sucht: Sie erlaubt einer generischen Direktive, die Form ihres Kontextobjekts zu behaupten, damit der Template-Compiler `let-`-Bindings gegen das echte Interface einschränkt und prüft. Ohne diesen Guard fällt der Ivy-Template-Checker für den Kontext auf `any` zurück und akzeptiert stillschweigend fehlerhafte Bindings, die erst zur Laufzeit tief in einer Schleife mit „undefined is not a function" scheitern.
snippet.js
javascript
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
import { Directive, TemplateRef, Input } from '@angular/core';export interface RowContext<T> {$implicit: T;index: number;isLast: boolean;}@Directive({selector: '[appRow]',standalone: true,})export class RowDirective<T> {@Input('appRow') data!: T;constructor(public template: TemplateRef<RowContext<T>>) {}static ngTemplateContextGuard<T>(dir: RowDirective<T>,ctx: unknown): ctx is RowContext<T> {return true;}}// usage in host component template:// <ng-container *ngFor="let item of items; let i = index; let last = last">// <ng-template [appRow]="item" let-row let-idx="index" let-isLast="isLast">// {{ row.name }} - {{ idx }} - {{ isLast }}// </ng-template>// </ng-container>
angular
Erklärung
1
export interface RowContext<T> { $implicit: T; index: number; isLast: boolean; }
`$implicit` ist der reservierte Schlüssel, den Angular für das unbenannte `let-row`-Binding nutzt; andere Schlüssel müssen exakt zu ihren `let-x="key"`-Namen passen.
2
static ngTemplateContextGuard<T>(dir: RowDirective<T>, ctx: unknown): ctx is RowContext<T>
Ein Type-Predicate, das der Ivy-Compiler zur Kompilierzeit aufruft — sein Rückgabetyp, nicht sein Laufzeitkörper, schränkt den Template-Kontexttyp ein.
3
constructor(public template: TemplateRef<RowContext<T>>) {}
Die Bindung des Generics an `TemplateRef` koppelt den Typparameter der Direktive an die tatsächliche eingebettete View, sodass `T` von `[appRow]="item"` in den Kontext fließt.