0 Tage Serie
javascript / expert
Snippet

Eigene strukturelle Direktiven mit typisierten Template-Guards

Eine eigene strukturelle Direktive ist nur so sicher wie die Typisierung ihres Template-Kontexts. `ngTemplateContextGuard` ist eine statische Type-Guard-Methode, die der Angular-Compiler zur Kompilierzeit (über den Ivy-Template-Type-Checker) aufruft, um zu prüfen, ob mit `let-foo` deklarierte Variablen im Template tatsächlich zur Kontext-Form passen, die `createEmbeddedView` ausgibt. Ohne sie kompiliert `<ng-template let-x>` selbst dann, wenn `x` auf dem Kontextobjekt gar nicht existiert — die Abweichung fällt erst zur Laufzeit auf. Zusammen mit `ngTemplateGuard_ifRole: 'binding'` sagt man dem Compiler zusätzlich, den Typ des gebundenen Ausdrucks selbst zu verengen, sodass der Template-Körper `role` als `string` sieht, nicht als `string | null`.

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
32
33
34
35
36
37
38
39
40
41
42
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
interface IfRoleContext {
$implicit: boolean;
ifRole: string;
}
@Directive({
selector: '[ifRole]',
standalone: true,
})
export class IfRoleDirective {
private hasView = false;
constructor(
private templateRef: TemplateRef<IfRoleContext>,
private viewContainer: ViewContainerRef,
) {}
@Input() set ifRole(role: string) {
const allowed = role === 'admin';
if (allowed && !this.hasView) {
this.viewContainer.createEmbeddedView(this.templateRef, {
$implicit: true,
ifRole: role,
});
this.hasView = true;
} else if (!allowed && this.hasView) {
this.viewContainer.clear();
this.hasView = false;
}
}
static ngTemplateGuard_ifRole: 'binding';
static ngTemplateContextGuard(
dir: IfRoleDirective,
ctx: unknown,
): ctx is IfRoleContext {
return true;
}
}
angular
Erklärung
1
interface IfRoleContext { $implicit: boolean; ifRole: string; }
Definiert die exakte Form, die den `let-`-Variablen im Template zur Verfügung steht — `$implicit` ist das, an das `let-x` ohne Namen bindet.
2
static ngTemplateGuard_ifRole: 'binding';
Weist den Compiler an, den gebundenen `ifRole`-Ausdruck selbst zu typprüfen und zu verengen, nicht nur den Template-Kontext.
3
static ngTemplateContextGuard(dir: IfRoleDirective, ctx: unknown): ctx is IfRoleContext
Ein reines Kompilierzeit-Type-Predicate; der Compiler ruft diese Signatur während der Typprüfung auf, sie läuft nie im Browser.
4
this.viewContainer.createEmbeddedView(this.templateRef, { $implicit: true, ifRole: role })
Das tatsächliche Laufzeit-Kontextobjekt — seine Form muss exakt zu `IfRoleContext` passen, sonst wird der Guard zu einer Lüge, der der Compiler vertraut.