Dieses Blatt ist zum Danebenlegen gedacht: die Formen und Entscheidungen, die man beim Bauen einer Angular-22-Anwendung ständig braucht und regelmäßig vergisst. Es bringt keine davon bei — dafür sind die Module des Seminars da. Maßgeblich bleibt die offizielle Angular-Dokumentation; der Mehrwert hier liegt in der Auswahl. Was danach kommt — Tests, Barrierefreiheit, Rendering, Sicherheit und Modernisierung — steht auf dem zweiten Blatt Angular 22 — Qualität und Betrieb.
Versionen, Node und TypeScript
| Angular | Node.js | TypeScript |
|---|---|---|
| 22.0.x | 22.22.3, 24.15.0, 26.0.0 | ab 6.0, unter 6.1 |
| 21.x | 20.19.0, 22.12.0, 24.0.0 | ab 5.9, unter 6.0 |
| 20.2.x | 20.19.0, 22.12.0, 24.0.0 | ab 5.8, unter 6.0 |
Version 22 ist seit dem 03.06.2026 aktiv und bis 06/2027 im Support; 21 und 20 sind im Langzeitsupport, alles darunter bekommt keine Sicherheitspatches mehr. Der Langzeitsupport endet 12 Monate nach der aktiven Phase — ein Projekt bleibt damit höchstens 24 Monate ohne Update im unterstützten Bereich, und ein Sprung über zwei Hauptversionen sind zwei Updates, nicht eines.
Modul: Angular 22 einordnen
Strenge, die gesetzt sein muss
{
"compilerOptions": { "strict": true, "noUnusedLocals": true },
"angularCompilerOptions": {
"strictTemplates": true,
"typeCheckHostBindings": true
}
}
strictTemplates findet, was TypeScript allein nicht sieht: eine Eingabe mit
dem falschen Typ, ein Feld im Template, das es nicht mehr gibt, einen
möglicherweise undefinierten Wert ohne Prüfung, ein Ereignis an einer Methode
mit anderer Signatur.
Modul: Workspace, Build und Entwicklungsumgebung
Komponentenverträge: input, output, model
readonly auftrag = input.required<Auftrag>();
readonly kompakt = input(false, { transform: booleanAttribute });
readonly nummer = input('', { alias: 'auftragsnummer' });
readonly geoeffnet = output<string>();
readonly markiert = model(false);
| Dekorator früher | Signal-Eingabe heute |
|---|---|
@Input() value = 0 | value = input(0) |
| Pflicht über Option | input.required<T>() |
Lesen über this.value | Lesen über this.value() |
Änderung über ngOnChanges | Änderung über computed |
model(false) erzeugt zusätzlich die Ausgabe markiertChange; im Template
bindet [(markiert)] beide Richtungen. booleanAttribute und
numberAttribute liefert Angular mit.
Modul: Komponenten und ihre Verträge
Signals: gespeichert oder abgeleitet
| Gespeichert | Abgeleitet |
|---|---|
signal, linkedSignal | computed |
| wird gesetzt | wird berechnet |
| kann veralten | kann nicht veralten |
| gehört in den Store | gehört neben den Store |
private readonly _auftraege = signal<Auftrag[]>([]);
readonly auftraege = this._auftraege.asReadonly();
readonly treffer = computed(() =>
this.auftraege().filter((a) => a.status === this.statusFilter()));
readonly auswahl = linkedSignal<Auftrag[], Auftrag>({
source: this.treffer,
computation: (neu, vorher) =>
neu.find((a) => a.id === vorher?.value.id) ?? neu[0],
});
linkedSignal ist der Fall „Auswahl gehört zu einer Liste, die sich ändern
kann”: Ein reines Signal zeigte nach dem Filtern ins Leere, ein computed
ließe die Auswahl durch den Nutzer nicht zu.
Bei effect() gilt: Signale vor dem ersten await lesen — danach zählt
kein Zugriff mehr als Abhängigkeit. untracked() liest, ohne abhängig zu
werden.
Modul: Signals: Zustand modellieren
Signal oder Stream
| Aufgabe | Mittel |
|---|---|
| Aktueller Wert für die Ansicht | signal, computed |
| Eingabe entprellen | Observable mit debounceTime |
| Mehrere Quellen zusammenführen | Observable mit combineLatest |
| Laufende Anfrage abbrechen | Observable oder resource |
| Abgeleitete Kennzahl | computed |
Die Leitfrage: Wird der Verlauf gebraucht oder nur der aktuelle Wert?
Zeitliche Abläufe als Stream modellieren und am Rand in ein Signal überführen —
toSignal braucht dabei einen initialValue, sonst liefert es zunächst
undefined.
Modul: Zoneless Change Detection und RxJS-Interoperabilität
Kontrollfluss im Template
Variable in @for | Bedeutung |
|---|---|
$index | Position der aktuellen Zeile |
$count | Anzahl der Einträge |
$first, $last | erste beziehungsweise letzte Zeile |
$even, $odd | gerader beziehungsweise ungerader Index |
track verbindet Datensatz und DOM-Knoten. Ohne stabilen Schlüssel baut
Angular Zeilen unnötig neu auf — der Index taugt nur, wenn sich die Reihenfolge
nie ändert. @switch vergleicht streng.
Modul: Templates, Kontrollfluss und Wiederverwendung
Wo ein Provider steht
| Ort | Lebensdauer | Typischer Fall |
|---|---|---|
bootstrapApplication | Anwendung | HttpClient, Konfiguration |
| Route | solange das Feature aktiv ist | Feature-Store |
| Komponente | je Einbaustelle | Zustand eines Bausteins |
Anwendungsweit heißt geteilt, je Komponente heißt getrennt. Ein mehrfach bereitgestellter Dienst erzeugt unbemerkt mehrere Instanzen.
Modul: Dependency Injection
Fehler sind nicht gleich Fehler
| Art | Beispiel | Behandlung |
|---|---|---|
| Fachfehler | Auftrag bereits abgeschlossen | im Modell, sichtbar am Feld |
| Technische Störung | API nicht erreichbar | zentral, mit Wiederholung |
| Programmfehler | Zugriff auf leere Auswahl | Test statt Meldung |
Modul: Feature-Architektur und Zuständigkeiten
Routing: nachladen und absichern
{ path: 'auswertung',
loadComponent: () => import('./auswertung/auswertung').then((m) => m.Auswertung) }
export const nurLeitung: CanMatchFn = () => {
const nutzer = inject(NutzerService);
return nutzer.istStandortleitung() ? true : inject(Router).createUrlTree(['/']);
};
CanMatchFn verhindert zusätzlich das Nachladen des Bündels, CanActivateFn
greift erst danach. Mit withComponentInputBinding wird ein Routenparameter
direkt als Eingabe der Komponente gebunden. Ein Guard ist kein
Zugriffsschutz — die API prüft noch einmal.
Modul: Routing und Navigation
HTTP: Reaktion nach Fehlerart
| Lage | Beispiel | Reaktion |
|---|---|---|
| Netz nicht erreichbar | Zeitüberschreitung | erneut versuchen anbieten |
| Server meldet 500 | interner Fehler | Meldung, Vorgang abbrechen |
| Server meldet 409 | Auftrag gesperrt | fachlich am Feld anzeigen |
| Server meldet 401 | Sitzung abgelaufen | neu anmelden führen |
Schreibende Aufrufe nie automatisch wiederholen — sonst wird zweimal angelegt.
Modul: HTTP und typisierter Datenzugriff
resource() und die Werte von status()
readonly detail = resource({
params: () => ({ id: this.auftragId() }),
loader: ({ params, abortSignal }) =>
fetch(`/api/auftraege/${params.id}`, { signal: abortSignal }).then((r) => r.json()),
});
| Status | Bedeutung |
|---|---|
idle | keine gültige Anfrage |
loading | Parameter haben sich geändert |
reloading | reload wurde aufgerufen |
resolved | erfolgreich abgeschlossen |
error | Fehler aufgetreten |
local | Wert wurde lokal gesetzt |
Ändert sich ein in params gelesenes Signal, bricht Angular die laufende
Anfrage ab und startet den loader neu. Liest params kein Signal, lädt die
Ressource nie nach.
Modul: Reaktiver Datenabruf mit resource() und httpResource()
Signal Forms
readonly modell = signal({ kennzeichen: '', standort: '', notiz: '' });
readonly formular = form(this.modell, (pfad) => {
required(pfad.kennzeichen);
required(pfad.standort);
});
validate(pfad.notiz, ({ value, valueOf }) =>
valueOf(pfad.dringend) && !value()
? { kind: 'notizPflicht', message: 'Grund angeben.' }
: null);
<input [field]="formular.kennzeichen" />
@if (formular.kennzeichen().touched() && formular.kennzeichen().invalid()) {
<p role="alert">Kennzeichen fehlt.</p>
}
Serverseitige Befunde über validateHttp zurück ans Feld geben, nicht in eine
allgemeine Meldungsbox. Reactive Forms bleiben im Bestand gültig — nur nicht
gemischt innerhalb eines Formulars.
Modul: Formulare: Signal Forms und Reactive Forms
Typische Fallen
- Signal im Template ohne Klammern gelesen — dann steht dort die Funktion, nicht der Wert.
- Objekt im Signal verändert statt ersetzt — die Gleichheitsprüfung greift,
nichts aktualisiert sich. Gleiches gilt für
pushauf einem Array. - Trefferzahl als zweites Signal geführt statt abgeleitet — sie veraltet still.
- Effekt liest und schreibt dasselbe Signal und läuft im Kreis; oder er
ersetzt ein
computedund muss die Reihenfolge selbst sicherstellen. - Signal nach dem
awaitgelesen — der Effekt läuft nie wieder. - Zone.js aus der Konfiguration entfernt, aber als Abhängigkeit installiert geblieben — es kommt über ein Paket zurück.
- Store steht anwendungsweit, obwohl er zu einem Feature gehört; oder Zustand liegt an der Route und geht beim Verlassen verloren.
- Nachgeladene Route importiert ihr Ziel zusätzlich statisch — die Grenze wirkt nicht.
- Antwortformat der API wird direkt als eigenes Modell verwendet und wandert unverändert durch die ganze Anwendung.
- Fehlermeldung erscheint vor der ersten Berührung, weil
touched()nicht geprüft wird. anyals Notausgang für einen einzelnen Befund der Template-Prüfung — er breitet sich aus.
Dieses Thema als Schulung für Ihr Team
Dieser Beitrag erklärt das Thema. Damit Ihr Team es danach auch anwendet, gibt es Angular für erfahrene Entwickler als Schulung — an Ihrem eigenen Code, mit den Fragen, die ein Text nicht beantwortet. Sie wählen die Module, wir bauen daraus ein Programm.
6 Tage·ab 900 EUR netto pro Tag (bis 3 Teilnehmende) ·Termin nach Vereinbarung
Als Team-Schulung anfragenZum Seminar Angular für erfahrene Entwickler →