AngularAtlasAusg. 22.2

Kap. 14Fortgeschrittene Reaktivität63 / 75

NgRx SignalStore

State Management im großen Stil auf Signals: withState, withComputed, withMethods, rxMethod, Entities und Hooks — und wann ein einfacher Store reicht.

Architektur ~0 Min. Lesezeit

NgRx SignalStore (das Paket @ngrx/signals) ist ein deklarativer Weg, Stores auf Basis von Signals zu bauen. Du setzt Features zusammen (withState, withComputed, withMethods, withHooks, withEntities…), und das Ergebnis ist eine injizierbare Klasse. Ihr Zustand wird als Signals bereitgestellt und lässt sich nur über die Methoden ändern, die du definierst.

Es ist der nächste Schritt nach dem selbstgebauten Store auf der Seite State Management: dieselben Ideen, aber mit einer Standardstruktur, fertigen Entities, RxJS-Integration für asynchrone Abläufe und wiederverwendbaren Bausteinen über mehrere Stores hinweg. Hier wird @ngrx/signals 22 verwendet, kompatibel mit Angular 22.

Warum das wichtig ist
Wenn die Anwendung wächst, sieht jeder selbstgeschriebene Store irgendwann anders aus: Manche stellen beschreibbare Signals bereit, andere nicht; manche halten Loading/Error, andere nicht. SignalStore gibt dir für alle eine einheitliche Form. Und die wiederkehrenden Teile (Request-Zustand, Paginierung, Entity-Collections) schreibst du nur einmal, als Features.

Aufbau eines Stores

Ein signalStore(...) ist eine Liste von Features, die der Reihe nach angewendet werden. Jedes Feature sieht, was die vorherigen hinzugefügt haben. withState definiert den Zustand: Jede Property wird zu einem Signal (store.todos()), und verschachtelte Objekte werden zu Deep Signals. withComputed fügt abgeleiteten Zustand hinzu, withMethods die Operationen. Nur Methoden dürfen patchState aufrufen. Der Zustand ist standardmäßig geschützt, du kannst ihn also nicht direkt von außerhalb des Stores ändern.

import { computed } from '@angular/core';
import { patchState, signalStore, withComputed, withMethods, withState } from '@ngrx/signals';

type Filter = 'all' | 'open' | 'done';
interface Todo { id: number; title: string; done: boolean }

export const TodoStore = signalStore(
  { providedIn: 'root' }, // ohne die Option: dort in `providers` eintragen, wo du ihn brauchst
  withState({ todos: [] as Todo[], filter: 'all' as Filter }),
  withComputed(({ todos, filter }) => ({
    visible: computed(() =>
      filter() === 'all' ? todos() : todos().filter((t) => t.done === (filter() === 'done')),
    ),
    openCount: computed(() => todos().filter((t) => !t.done).length),
  })),
  withMethods((store) => ({
    add(title: string): void {
      patchState(store, (s) => ({ todos: [...s.todos, { id: Date.now(), title, done: false }] }));
    },
    toggle(id: number): void {
      patchState(store, (s) => ({ todos: s.todos.map((t) => (t.id === id ? { ...t, done: !t.done } : t)) }));
    },
    setFilter(filter: Filter): void {
      patchState(store, { filter });
    },
  })),
);

patchState erhält entweder ein partielles Objekt ({ filter }) oder eine Funktion (state) => Partial<State>. Es akzeptiert außerdem beliebig viele Updater, die gemeinsam angewendet werden. Der Zustand ist immutable: Du gibst immer neue Objekte und Arrays zurück, sonst bemerken die Signals die Änderung nicht (dieselbe Regel wie bei signal()).

Wo der Store lebt

Wie bei jedem Service wird die Lebensdauer eines Stores durch den Injector bestimmt, in dem du ihn bereitstellst. Mit { providedIn: 'root' } bekommst du eine einzige globale Instanz, gut für die Benutzersitzung oder einen Warenkorb. Ohne die Option trägst du ihn in die providers einer Komponente oder einer Route ein und bekommst eine neue Instanz, die zusammen mit ihr zerstört wird. Das ist die richtige Wahl für den Zustand einer Seite.

// 1. Global: eine einzige Instanz für die ganze Anwendung
export const SessionStore = signalStore({ providedIn: 'root' }, withState({ user: null as User | null }));

// 2. Pro Seite / pro Komponente: neue Instanz, wird zusammen mit der Komponente zerstört
export const ProductsStore = signalStore(withEntities<Product>() /* ... */);

@Component({
  providers: [ProductsStore], // jedes <app-catalog> hat seinen eigenen Store
  template: `...`,
})
export class Catalog {
  readonly store = inject(ProductsStore);
}

// 3. Pro Route (Feature): lebt so lange wie die Route
{ path: 'catalog', providers: [ProductsStore], loadComponent: () => import('./catalog') }
LiveLokaler Store vs. Root-Store
Instanz 1
local: 0
root: 0
Instanz 2
local: 0
root: 0
„local“ stirbt mit der Komponente; „root“ überlebt.
Klicke in jeder Karte auf „local +1“: Jede hat ihre eigene Instanz. „root +1“ erhöht in beiden denselben Zähler. Zerstöre die Karten und erstelle sie neu: Der lokale Zähler beginnt bei null, der Root-Zähler behält seinen Wert.

Entities

withEntities<Product>() fügt eine normalisierte Collection hinzu: Im Zustand hast du ids und entityMap, und als Signal hast du entities(), ein abgeleitetes Array. Die Updater aus @ngrx/signals/entities (setAllEntities, addEntity, updateEntity, upsertEntity, removeEntity…) werden direkt an patchState übergeben. Für mehrere Collections im selben Store gibt es benannte Collections (withEntities({ entity: type<Product>(), collection: 'product' }) → productEntities()).

Asynchrone Abläufe mit rxMethod

rxMethod (aus @ngrx/signals/rxjs-interop) macht aus einer RxJS-Pipe eine Methode. Du kannst sie mit einem Wert, mit einem Observable oder mit einem Signal aufrufen. Bei einem Signal läuft die Methode bei jeder Änderung davon. Der Aufruf store.search(store.query) in onInit koppelt die Suche nur einmal an die Query. Der Rest ist gewöhnliches RxJS: debounceTime, switchMap, um den veralteten Request abzubrechen, und catchError innerhalb des switchMap, damit ein Fehler den Stream nicht beendet (siehe Fortgeschrittenes RxJS). Die Subscription wird von selbst geschlossen, wenn der Store zerstört wird.

import { HttpClient } from '@angular/common/http';
import { inject } from '@angular/core';
import { patchState, signalStore, withHooks, withMethods, withState } from '@ngrx/signals';
import { setAllEntities, updateEntity, withEntities } from '@ngrx/signals/entities';
import { rxMethod } from '@ngrx/signals/rxjs-interop';
import { catchError, debounceTime, distinctUntilChanged, EMPTY, pipe, switchMap, tap } from 'rxjs';

export const ProductsStore = signalStore(
  withEntities<Product>(),                 // Zustand: ids + entityMap; Signal: entities()
  withState({ query: '' }),
  withRequestStatus(),                     // eigenes Feature (unten)
  withMethods((store, http = inject(HttpClient)) => ({
    setQuery: (query: string) => patchState(store, { query }),
    buy: (id: number) =>
      patchState(store, updateEntity({ id, changes: (p) => ({ stock: p.stock - 1 }) })),

    search: rxMethod<string>(
      pipe(
        debounceTime(300),
        distinctUntilChanged(),
        tap(() => patchState(store, setPending())),
        switchMap((q) =>
          http.get<Page<Product>>(`/api/products?q=${encodeURIComponent(q)}`).pipe(
            tap((page) => patchState(store, setAllEntities(page.items), setFulfilled())),
            catchError(() => {
              patchState(store, setError('Der Server antwortet nicht.'));
              return EMPTY; // der äußere Stream bleibt am Leben
            }),
          ),
        ),
      ),
    ),
  })),
  withHooks({
    onInit(store) {
      store.search(store.query); // rxMethod erhält ein SIGNAL: Sie läuft bei jeder Änderung
    },
  }),
);
tapResponse
In der NgRx-Dokumentation siehst du oft tapResponse statt tap + catchError. Es tut dasselbe, nur kompakter, kommt aber aus dem separaten Paket @ngrx/operators. Das Beispiel hier verwendet nur RxJS.

Wiederverwendbare Features

signalStoreFeature(...) fasst mehrere Features zu einer Funktion zusammen, die du jedem Store hinzufügen kannst, zum Beispiel withRequestStatus(): der Request-Zustand plus isPending und error. Die Updater sind einfache Funktionen, die ein Stück Zustand zurückgeben, daher kombinierst du sie mit anderen im selben patchState. So entfällt der kopierte Loading-/Error-Code in jedem Store.

import { computed } from '@angular/core';
import { signalStoreFeature, withComputed, withState } from '@ngrx/signals';

export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string };

// Ein wiederverwendbarer Store-Baustein: Zustand + Computeds, in jeden signalStore einsetzbar
export function withRequestStatus() {
  return signalStoreFeature(
    withState<{ requestStatus: RequestStatus }>({ requestStatus: 'idle' }),
    withComputed(({ requestStatus }) => ({
      isPending: computed(() => requestStatus() === 'pending'),
      error: computed(() => {
        const s = requestStatus();
        return typeof s === 'object' ? s.error : null;
      }),
    })),
  );
}

// Updater: einfache Funktionen, die ein Stück Zustand zurückgeben → patchState(store, setPending())
export const setPending = () => ({ requestStatus: 'pending' as RequestStatus });
export const setFulfilled = () => ({ requestStatus: 'fulfilled' as RequestStatus });
export const setError = (error: string) => ({ requestStatus: { error } as RequestStatus });
LiveProdukt-Store: Entities + rxMethod + eigenes Feature
  • Keine Produkte.
getState(store) — live
{
  "query": "",
  "inStockOnly": false,
  "cart": 0,
  "requestStatus": "idle",
  "ids": []
}
Im Warenkorb: 0 · Lagerwert: 0 RON
Tippe in die Suche: Der Request geht erst nach 300 ms Pause raus. „Kaufen“ ändert mit updateEntity eine einzelne Entity. Rechts siehst du getState(store) live aktualisiert.

Der Rest des Werkzeugkastens

signalState ist die Variante „ohne Store“: ein Objekt aus Signals für den lokalen Zustand einer Komponente, ebenfalls mit patchState. withProps fügt Properties hinzu, die kein Zustand sind (Services, Resources, Observables). withLinkedState leistet für einen Store, was linkedSignal für eine Komponente leistet. getState liefert dir einen Snapshot des Zustands, und watchState benachrichtigt dich synchron bei jeder Änderung. Für Tests kannst du mit unprotected(store) aus @ngrx/signals/testing den Store in einen bestimmten Zustand versetzen. Das Paket hat außerdem ein Events-Plugin (@ngrx/signals/events) für den Redux-Stil mit Events und Reducern.

import { patchState, signalState } from '@ngrx/signals';

// Für den lokalen Zustand EINER Komponente: kein Store, nur ein Objekt aus Signals
@Component({ /* ... */ })
export class Filters {
  readonly state = signalState({ query: '', page: 1, sort: 'name' as 'name' | 'price' });

  next(): void {
    patchState(this.state, (s) => ({ page: s.page + 1 }));
  }
  // im Template: state.query(), state.page()
}

Wann ja, wann nein

SignalStore verwenden
  • Feature-Zustand, der von mehreren Komponenten genutzt wird, mit asynchronen Operationen (Suche, Speichern, Paginierung).
  • Entity-Collections, die oft aktualisiert werden (editierbare Listen, Warenkörbe, Tabellen).
  • Mehrere Teams, die für alle Stores dieselbe Form und wiederverwendbare Features wollen.
  • Du hast den klassischen NgRx Store und willst für neue Features etwas Leichteres. Beide können nebeneinander existieren.
Etwas Einfacheres genügt
  • Der Zustand einer einzelnen Komponente: signal() oder signalState.
  • Daten, die vom Server gelesen und nur angezeigt werden: httpResource (siehe resource).
  • Ein kleiner Service mit zwei Signals und drei Methoden: Ein selbstgebauter Store ist für alle klarer.
  • Anwendungen, in denen niemand im Team NgRx kennt und die Deadline morgen ist. Die Abhängigkeit hat Lernaufwand.
Selbstgebauter StoreSignalStoreNgRx Store (Redux)
Boilerplateminimalgeringhoch (Actions, Reducers, Effects, Selectors)
Standardstrukturwas das Team entscheidetjaja, sehr strikt
Entitiesselbst schreibenwithEntities@ngrx/entity
Asyncresource / RxJSrxMethod, resourceEffects
Gut fürkleine Featuresdie meisten Anwendungenkomplexe Abläufe, Event-Auditing

Häufige Stolperfallen

  • Den Zustand mutieren (s.todos.push(x) in einem Updater). Das Signal erhält dieselbe Referenz und nichts wird aktualisiert. Gib immer neue Arrays und Objekte zurück.
  • providedIn: 'root' für den Zustand einer Seite. Der Zustand „klebt“ von einem Besuch zum nächsten und vermischt sich zwischen Komponenteninstanzen. Trage den Store in die providers der Seite ein.
  • catchError nach dem switchMap in einer rxMethod. Der erste Fehler beendet den Stream und die Suche funktioniert nicht mehr. Setze es in den inneren Stream.
  • Business-Logik in Komponenten, wobei der Store nur als „Datensack“ dient. Die Methoden des Stores sind der richtige Ort dafür.
  • Ein riesiger Store für die ganze Anwendung. Baue kleine Stores pro Feature und setze sie mit Features zusammen. Das ist leichter zu testen und per Lazy Loading zu laden.
  • effect() in Komponenten, um Stores miteinander zu synchronisieren. Bevorzuge withComputed oder withLinkedState (siehe effect()).

Spickzettel

signalStore(config?, ...features)        config: { providedIn: 'root' } oder nichts (→ providers: [Store])
withState({ ... })                       Zustand → Signals: store.x()
withComputed(store => ({ y: computed(...) }))
withMethods((store, dep = inject(X)) => ({ ... }))   der einzige Ort, an dem du den Zustand änderst
withHooks({ onInit(store) {}, onDestroy(store) {} })
withProps / withLinkedState              Nicht-Zustand-Properties / gekoppelter Zustand (wie linkedSignal)
signalStoreFeature(...features)          wiederverwendbare Bausteine: withRequestStatus(), withPagination()
patchState(store, partial | updater, ...) mehrere Updater in einem Aufruf
getState(store) / watchState(store, fn)   Snapshot / synchroner Beobachter des Zustands
rxMethod<T>(pipe(...))                   aus @ngrx/signals/rxjs-interop — akzeptiert einen Wert, ein Signal oder ein Observable
withEntities<T>() + setAllEntities, addEntity, updateEntity, removeEntity, upsertEntity  (@ngrx/signals/entities)
signalState({ ... })                     lokaler Zustand in einer Komponente, ohne Store
unprotected(store)                       aus @ngrx/signals/testing — Zustand in Tests direkt setzen

Kap. 14 · 0 von 4 abgeschlossen

Lektion abgeschlossen?

Markiere sie, dann erscheint sie auf deiner Karte: im Inhaltsverzeichnis und auf der Startseite.

Wird automatisch markiert, sobald du die ganze Lektion gelesen hast.

  1. Signals unter der Haube
  2. Fortgeschrittenes RxJS: Operatoren höherer Ordnung
  3. NgRx SignalStore
  4. Zustandsautomaten mit Signals
Deine ganze Karte ansehen

Alle 75 Seiten · tippen zum Filtern

  • 01Was ist neu in Angular 21 & 22Alle wichtigen Änderungen in v21 und v22 in Kürze, mit Link zur Seite, die sie jeweils erklärt.Start
  • 01Deine erste App, Schritt für SchrittEine Leseliste, von einer leeren Komponente bis zur fertigen App — Signals, @for, Inputs und Outputs, ein Service und ein Formular, Schritt für Schritt.Start
  • 01Schnelle Entscheidungen„Wohin damit?“, „Signal oder Observable?“, „Shared oder Feature?“ — kurze Antworten auf Alltagsfragen.Start
  • 01Angular-Interviewfragen60 echte Interviewfragen für Junior, Mid und Senior, mit der passenden Antwort, der Nachfrage und den Warnsignalen. Dazu ein Probeinterview.Start
  • 02Standalone & BootstrapWie eine App ohne NgModule startet: bootstrapApplication, app.config.ts, imports in der Komponente.Moderne Grundlagen
  • 02Data Binding & EventsInterpolation, Property- und Attribut-Bindings, Events mit $event und Tastenfiltern und wie [( )] aufgebaut ist — was in das DOM geht und was zurückkommt.Moderne Grundlagen
  • 02Control Flow: @if, @for, @switchDie Block-Syntax, die *ngIf/*ngFor/ngSwitch ersetzt, mit track, @empty und Exhaustive Checks.Moderne Grundlagen
  • 02@let und die neuen Template-AusdrückeLokale Variablen mit @let, Arrow Functions, Spread, Regex, instanceof, Kommentare in Tags.Moderne Grundlagen
  • 02@defer — Lazy Loading in TemplatesSchwere Komponenten nur laden, wenn sie gebraucht werden: Trigger, Prefetch, Placeholder, Loading, Error.Moderne Grundlagen
  • 02Class- & Style-BindingsWarum [class.x] und [style.x] ngClass und ngStyle abgelöst haben (mit automatischer Migration ab v21).Moderne Grundlagen
  • 02Komponenten-Styles & Kapselung:host, die drei Kapselungsmodi, Theming mit CSS-Variablen und warum ::ng-deep der letzte Ausweg ist.Moderne Grundlagen
  • 02Pipes: wann und wiePure vs. Impure Pipes, eigene Pipes und wie du zwischen Pipe, computed() und Methode wählst.Moderne Grundlagen
  • 03signal() und computed()Die reaktiven Grundbausteine: beschreibbarer Zustand, abgeleiteter Zustand, Gleichheit, untracked und Regeln zur Immutability.Signals & Reaktivität
  • 03effect() — wann ja, wann neinMit Signals synchronisierte Seiteneffekte: localStorage, Logging, imperative APIs. Und ihre Stolperfallen.Signals & Reaktivität
  • 03linkedSignal() — editierbarer abgeleiteter ZustandEin Signal, das sich zurücksetzt, wenn sich seine Quelle ändert, das du aber auch manuell setzen kannst.Signals & Reaktivität
  • 03resource, rxResource, httpResourceAsynchrone Daten als Signals: status, value, error, reload, automatischer Abbruch. Stabil seit v22.Signals & Reaktivität
  • 03debounced() — verzögerte SignalsDie „Debounce“-Variante eines Signals, ohne RxJS. Perfekt für die Suche.Signals & Reaktivität
  • 03Listen: suchen, filtern, sortieren, blätternEine Kette von computed-Signals, die nur neu berechnet, was sich geändert hat — mit den Filtern in der URL, damit ein Link dieselbe Ansicht öffnet.Signals & Reaktivität
  • 03Observables von null anWas ein Observable ist, subscribe und unsubscribe, die Operatoren, die du jede Woche brauchst, cold vs. hot — und wann ein Signal reicht.Signals & Reaktivität
  • 03Signals ↔ RxJStoSignal, toObservable, takeUntilDestroyed, outputFromObservable — und wann RxJS weiterhin die richtige Wahl bleibt.Signals & Reaktivität
  • 04input() und output()Inputs als Signals (required, transform, alias) und typisierte Outputs. Daten nach unten, Events nach oben.Komponenten: Kommunikation
  • 04model() — Two-Way-BindingBanana-in-a-Box [( )] mit einer einzigen API. Wann du es nutzt und wann input + output besser ist.Komponenten: Kommunikation
  • 04viewChild & contentChildQueries als Signals: viewChild, viewChildren, contentChild, contentChildren, die Option read.Komponenten: Kommunikation
  • 04Lifecycle & DestroyRefWelche Hooks in der Signals-Ära noch zählen, afterNextRender, DestroyRef und typisiertes ngOnChanges.Komponenten: Kommunikation
  • 04Content Projectionng-content mit select, mehrere Slots, Standardinhalt und die Falle der bedingten Projektion.Komponenten: Kommunikation
  • 04ng-template & ngTemplateOutletTemplates als Werte: Render Props in Angular, typisierter Kontext und Anpassen von Komponenten.Komponenten: Kommunikation
  • 05Smart vs. DumbContainer vs. Presentational: wer den Zustand hält, wer nur anzeigt und wie du die beiden sauber trennst.Komponenten-Design
  • 05Generische Komponenten <T>Eine korrekt typisierte DataTable<T> oder Select<T>, mit Templates für Zellen.Komponenten-Design
  • 05Dynamische KomponentenNgComponentOutlet, createComponent mit inputBinding/outputBinding und ein Dashboard, das aus einer Konfiguration gebaut wird.Komponenten-Design
  • 05Direktiven & Host-KompositionAttribut-Direktiven, die host: {}-Metadaten und die Directive Composition API (hostDirectives).Komponenten-Design
  • 05Eigene Formular-ControlsFormValueControl (Signal Forms) vs. ControlValueAccessor: eine Sternebewertung, in beiden Varianten.Komponenten-Design
  • 05Change DetectionOnPush ist ab v22 der Standard, zone.js entfällt ab v21. Was das Rendering auslöst und wie du dir nicht die Finger verbrennst.Komponenten-Design
  • 06inject() & @Service()Der neue @Service()-Decorator ab v22, inject() vs. Konstruktor und der Injection-Kontext.Dependency Injection
  • 06Provider & die Injector-HierarchieRoot vs. Route vs. Komponente, useClass/useValue/useFactory/useExisting und die Modifier optional/self/skipSelf.Dependency Injection
  • 06InjectionToken & provideX()Typisierte Konfiguration, provideFeature()-Funktionen, makeEnvironmentProviders und Initializer.Dependency Injection
  • 06injectAsync() — Lazy ServicesLade einen schweren Service erst, wenn du ihn brauchst, mit Prefetch im Leerlauf.Dependency Injection
  • 07Ordnerstrukturcore / shared / features, was in welchen Ordner gehört und wie ein Feature von innen aussieht.Architektur der Anwendung
  • 07Shared vs. FeatureWann du eine Komponente nach Shared verschiebst und (vor allem) wann NICHT. Eine konkrete Checkliste.Architektur der Anwendung
  • 07Interfaces & ModelleInline-Interface oder eigene Datei? DTO vs. Domänenmodell, type vs. interface, wohin damit.Architektur der Anwendung
  • 07State Management mit SignalsEinfache Signal-Stores, Fassaden, lokaler vs. globaler Zustand und wann du NgRx brauchst.Architektur der Anwendung
  • 07Styleguide & NamensgebungDie aktuellen offiziellen Konventionen: Dateinamen ohne Suffixe, protected, readonly, Reihenfolge der Member.Architektur der Anwendung
  • 07Barrels, Aliase & Grenzenindex.ts, Pfad-Aliase, die öffentliche API eines Features und Abhängigkeitsregeln, die sich automatisch prüfen lassen.Architektur der Anwendung
  • 07Fehler: @boundary & ErrorHandlerDer neue @boundary-Block ab v22.2, der globale ErrorHandler, HTTP-Fehler und Fehler aus resource.Architektur der Anwendung
  • 08Modernes Routing & Lazy LoadingloadComponent, Child Routes, Parameter als Inputs und Parameter-Vererbung (neuer Standard in v22).Routing
  • 08Guards & ResolvercanActivate, canMatch, canDeactivate und resolve als Funktionen, mit Redirects über UrlTree / RedirectCommand.Routing
  • 09Signal Forms — die GrundlagenFormulare auf Basis von Signals: form(), [formField], schemabasierte Validierung. Stabil seit v22.Formulare
  • 09Signal Forms — fortgeschrittenEigene und feldübergreifende Validierung, async/HTTP mit Debounce, dynamische Listen, disabled/hidden, wiederverwendbare Schemas.Formulare
  • 09Typisierte Reactive FormsTypisierte FormGroup, nonNullable, FormArray — und wie sie neben Signal Forms bestehen.Formulare
  • 10HttpClient, Interceptors & die API-SchichtHttpClient ohne Setup (v21), Fetch als Standard (v22), funktionale Interceptors und eine API-Schicht mit DTO-Mapping.HTTP & Daten
  • 11Performance — eine ChecklisteWas wirklich zählt: OnPush + Signals, track, @defer, Lazy Routes, Bilder, Bundles und Hydration.Qualität & Performance
  • 11Testen mit VitestVitest ist ab v21 der Standard-Runner. TestBed, Komponenten mit Signals, Services, HTTP und Harnesses.Qualität & Performance
  • 11DevTools: ein Blick in deine AppChrome DevTools und Angular DevTools Schritt für Schritt: was jedes Panel zeigt, mit Übungen, die auf dieser Website funktionieren.Qualität & Performance
  • 11Angular AriaHeadless, barrierefreie Komponenten, die du nach Belieben stylst: Tabs, Accordion, Menü, Listbox...Qualität & Performance
  • 12SSR, Prerendering & Render ModesWann auf dem Server gerendert wird, wann beim Build und wann nur im Browser: RenderMode pro Route, app.routes.server.ts und die echten Kosten.SSR & Hydration
  • 12Hydration: Event Replay & inkrementellWie der Browser das Server-HTML übernimmt, ohne es neu zu rendern: vollständige Hydration, Event Replay, @defer (hydrate on …) und Mismatch-Fehler.SSR & Hydration
  • 12Servertauglicher Codewindow gibt es auf dem Server nicht: PLATFORM_ID, afterNextRender, DOCUMENT, TransferState, der HTTP-Cache und PendingTasks.SSR & Hydration
  • 13Micro FrontendsWann es sich lohnt, eine App in mehrere Deployments aufzuteilen und wann es nur Komplexität ist: Native Federation, Module Federation, iframes, Web Components.Architektur im großen Maßstab
  • 13Web Components mit Angular ElementscreateCustomElement: eine Angular-Komponente, die überall als HTML-Tag nutzbar ist, plus Kommunikation zwischen Apps über Events und BroadcastChannel.Architektur im großen Maßstab
  • 13Monorepo, Bibliotheken & GrenzenEin Workspace mit mehreren Apps und Bibliotheken: ng generate library, Nx, Secondary Entry Points, Publishing und Abhängigkeitsregeln.Architektur im großen Maßstab
  • 13Hexagonale Architektur (Ports & Adapters)Eine Domäne ohne Angular, Ports als abstrakte Klassen und austauschbare Adapter: HTTP, localStorage oder In-Memory, ohne die UI anzufassen.Architektur im großen Maßstab
  • 14Signals unter der HaubeDer reaktive Graph: Producer und Consumer, Push-Pull, warum computed lazy und glitch-frei ist, das Diamond-Problem und wann Effects laufen.Fortgeschrittene Reaktivität
  • 14Fortgeschrittenes RxJS: Operatoren höherer OrdnungswitchMap, mergeMap, concatMap und exhaustMap an einem Live-Diagramm, dazu Retry mit Backoff, shareReplay und wie du Leaks vermeidest.Fortgeschrittene Reaktivität
  • 14NgRx SignalStoreState Management im großen Stil auf Signals: withState, withComputed, withMethods, rxMethod, Entities und Hooks — und wann ein einfacher Store reicht.Fortgeschrittene Reaktivität
  • 14Zustandsautomaten mit SignalsKeine unmöglichen Zustände mehr: ein typisierter Automat auf Signals, mit Übergängen, Guards und einem Live-Zustandsdiagramm.Fortgeschrittene Reaktivität
  • 15WebSockets: Echtzeit, die das Netz überstehtEin typisierter Socket als Signals: Reconnect mit Backoff und Jitter, Heartbeat, ein Postausgang im Offline-Modus und SSR-Sicherheit.Echtzeit & offline
  • 15Optimistische Updates & RollbackEine sofortige UI, die ehrlich bleibt: Snapshot und Rollback, Antworten in falscher Reihenfolge, Idempotenzschlüssel.Echtzeit & offline
  • 15Offline-first: Service Worker & CachingDer Angular Service Worker: Caching-Strategien, Update-Hinweise und ein Postausgang für Änderungen, die offline entstehen.Echtzeit & offline
  • 16Web WorkersSchwere Berechnungen, ohne die UI zu blockieren: ein echter Worker in der Seite, Message Passing und wann es sich nicht lohnt.Fortgeschrittene UI & Performance
  • 16Riesige Listen: Virtual Scroll100.000 Zeilen ohne Ruckeln: CDK Virtual Scroll, itemSize, trackBy und die Alternativen (Paginierung, Infinite Scroll).Fortgeschrittene UI & Performance
  • 16Moderne Animationen & View Transitionsanimate.enter und animate.leave statt des veralteten @angular/animations, CSS-Transitions und View Transitions bei der Navigation.Fortgeschrittene UI & Performance
  • 16Memory Leaks finden und behebenSubscriptions, Timer und Listener, die ihre Komponente überleben – wie du sie in den DevTools findest und schließt.Fortgeschrittene UI & Performance
  • 17Sicherheit: XSS, Sanitizing & CSPWas Angular automatisch gegen XSS tut, wann bypassSecurityTrust gefährlich wird, dazu CSP mit Nonce, Trusted Types und XSRF.Sicherheit & Globalisierung
  • 17Authentifizierung: Tokens & RefreshEin Interceptor, der das Token anhängt, bei 401 einmal erneuert und die wartenden Requests wiederholt — dazu, wo du das Token aufbewahrst.Sicherheit & Globalisierung
  • 17OAuth 2.1 & OpenID Connect mit PKCEDer Authorization-Code-Flow mit PKCE Schritt für Schritt: Verifier und Challenge, State und Nonce, das ID-Token und warum ein BFF sicherer ist.Sicherheit & Globalisierung
  • 17Internationalisierung (i18n)$localize, das i18n-Attribut, ICU plural/select, ein Build pro Sprache vs. Übersetzungen zur Laufzeit und Formatierung nach Locale.Sicherheit & Globalisierung