Skip to content

material(ngrx): State Management mit NgRx – Serie (Teil 1–3) + Beispiel-Apps#105

Draft
JohannesHoppe wants to merge 24 commits into
mainfrom
redux-kapitel
Draft

material(ngrx): State Management mit NgRx – Serie (Teil 1–3) + Beispiel-Apps#105
JohannesHoppe wants to merge 24 commits into
mainfrom
redux-kapitel

Conversation

@JohannesHoppe

@JohannesHoppe JohannesHoppe commented Jun 30, 2026

Copy link
Copy Markdown
Member

Dreiteilige Artikelserie State Management mit NgRx plus zwei lauffähige Beispiel-Apps, portiert auf das aktuelle Buch (BookManager).

  • Teil 1 Einführung · Teil 2 Global Store · Teil 3 SignalStore
  • Demos: Angular 22 + NgRx 21, echter BookStore-HttpClient gegen api1.angular-buch.com, Favoriten als Client-State

Noch nicht notwendig zu reviewen, Teil 3 ist 100% AI. Da gehe ich nochmal rüber

…Stub

- Teil 2 aus dem rohen Tool-Dump zu sauberem Markdown aufbereitet:
  - alle LaTeX-Artefakte entfernt (\lstinputlisting, \footnote, \mbox,
    \file, \begin{...}, \autoref, \index, Layout-Makros usw.)
  - alle Code-Listings aus den Buch-Snippets eingesetzt (vorher fehlten sie
    komplett, es standen nur die \lstinputlisting-Direktiven da)
  - versehentlich mitkopierte Fremdkapitel (Prettier, EAP, Raetsel,
    Changelog, Vorwort) entfernt
  - Frontmatter, Serien-Header (3-teilig) und [[toc]] ergaenzt
  - DevTools-Screenshots eingebunden, 4 Diagramme als TODO markiert
  - Shortlinks aufgeloest, gerade Anfuehrungszeichen, z. B. statt z.\,B.
- Teil 3 (SignalStore) als Stub mit Frontmatter und Platzhalter angelegt
- redux-flow-simple, selectors-gap, ngrx-flow-full, facade
- aus den Buch-PDFs per 'pdftocairo -svg' nach SVG konvertiert (Vektor,
  kein Rasterbild), kein yEd noetig
- TODO-Platzhalter durch echte Bild-Einbindungen mit beschreibendem
  Alt-Text ersetzt
- selectors-gap, ngrx-flow-full und facade: Label 'select()' bzw.
  'store.select(...)' durch 'selectSignal()' ersetzt
- alte (als Vektor-Pfad vorliegende) Beschriftung in Hintergrundfarbe
  ueberdeckt und durch echten, editierbaren <text> ersetzt
- Bild-Alt-Texte/Titel entsprechend angepasst

Hinweis: Der Fliesstext von Teil 2 nennt weiterhin store.select()
(buchtreuer Port) - die Angleichung an selectSignal() erfolgt in der
Modernisierung (Phase 2).
… TypeScript-Querverweis

- A1: Hinweis-Box ergaenzt (Historie der Nachrichten wird in der Praxis
  nicht gespeichert) - war im Buch eine Fussnote, ging beim Port verloren
- C: Verweis auf Spread-/Rest-Syntax als Link auf /material/typescript
  gesetzt; uebrige rein buchinterne Querverweise bleiben bewusst weg
- lastModified aktualisiert
Gegen die offizielle NgRx-Doku (ngrx.io, v21/v22) und das Schematic im
Repo verifiziert:
- Standalone statt NgModule: provideStore/provideState/provideEffects/
  provideStoreDevtools in app.config.ts bzw. Route-providers; NgModule-Weg
  komplett entfernt
- Store-Lesen mit store.selectSignal() (Signal) statt store.select()
  (Observable); Templates mit @if/@for statt *ngIf/*ngFor, keine AsyncPipe
- Effects mit inject()-Feldern; funktionale Effects als gleichwertige
  Variante ergaenzt (entspricht dem aktuellen Effect-Blueprint, PR #5155)
- useDefineForClassFields-Abschnitt entfernt (obsolet, Issue #3654)
- concatLatestFrom aus @ngrx/operators (seit v18 umgezogen)
- createFeature als moderne Reducer/Selektor-Buendelung ergaenzt
- @ngrx/component als Maintenance-Modus eingeordnet
- Versionsnote auf Angular 22; durchgaengig wir-Form statt Sie-Form
- '--defaults' aus 'ng add @ngrx/store' und 'ng g feature' entfernt
  (ist kein NgRx-Schematic-Flag)
- concatLatestFrom-Historie praezisiert (in @ngrx/operators seit v18,
  in @ngrx/effects seit v17 deprecated)
…fiziert

Komplett neuer Artikel (war nicht im Buch). Struktur: architektonischer
Unterschied zum Global Store / Vorstellung / Vergleich / Fazit.

- gesamte @ngrx/signals-Doku (17 Seiten) per Headless-Chrome in den Context
  geholt, jede API gegen ngrx.io verifiziert (kein Raten)
- signalStore/withState/withComputed/withMethods/withHooks, patchState,
  rxMethod, withEntities, signalStoreFeature, lokal vs. providedIn:'root'
- durchgehendes BookMonkey-Beispiel analog zu Teil 2 (Buchliste + Ladeindikator)
- Vergleichstabelle Global Store vs. SignalStore, Entscheidungshilfe im Fazit
- ehrlich eingeordnet: keine offizielle Redux-DevTools-Anbindung,
  Events-Plugin (seit 19.2) fuer Flux-Stil, ersetzt @ngrx/component-store
- adversarialer Review-Agent: keine erfundene API, keine falschen Signaturen;
  protectedState-Aussage praezisiert
Form-neutrale Korrekturen aus dem kritischen Lektorat:
- T1: Em-Dash in Zitat-Attribution -> Halbgeviertstrich; 'weiter gehen'
  -> 'weiter treiben'; toter Vorwaerts-Verweis -> Link auf Teil 2;
  shareReplay-Formulierung praezisiert
- T2: 'Lassen Sie uns' -> 'Fassen wir' (letzte Sie-Form); fehlende
  Leerzeile vor Ueberschrift; Effect-Fehlerfall klarer; root- vs.
  feature-provideEffects erklaert
- T3: 'haengt im einen' -> korrekt; Kongruenz 'waren'; Kasus 'einem
  Lade-Status'; Wiederverwendungs-Aussage entschaerft; withEntities als
  Variante gekennzeichnet; component-store-Redundanz entfernt; Wortwdh.
Anrede-Umstellung (du) noch offen (Rueckfrage).
…n-Konsistenz)

- 'Bitte beachten Sie' -> 'Wir muessen beachten'
- 'Welches der Frameworks Sie ... Ihren Favoriten' -> wir/unseren,
  'Ihnen empfehlen' -> 'empfehlen'
- Pronomen 'sie' (Methode/Funktion/Frameworks) bleiben korrekt
- lastModified T1+T2 auf 2026-06-13
Alle drei Teile jetzt durchgaengig wir-Form (konsistent mit den uebrigen
Material-Artikeln).
…n C)

Beide Teile zeigen jetzt ein vollstaendiges, baufaehiges Feature
(Laden + Anlegen/Aendern/Loeschen + Fehler-State + UI-Anzeige), gegen
ngrx.io verifiziert (adversarialer Review: keine Fehler).

Teil 3 (SignalStore), neu strukturiert zu durchgehendem Beispiel:
- error-Feld im State; patchState-Updater-Funktion fuer immutables
  Array-Update (add/update/delete) gezeigt
- Schreib-Methoden via rxMethod + concatMap + tapResponse + Fehler-State
- EIN konsolidierter Komplett-Store (Laden + CRUD + computed + withHooks)
- Komponente mit Aktionen (Loeschen-Button) + Fehleranzeige im Template
- Route-providers fuer lazy Feature ergaenzt
- withEntities-Variante zeigt addEntity/updateEntity/removeEntity als
  Automatisierung der manuellen Updates (Phantom-APIs behoben)
- Test mit gemocktem BookStoreService

Teil 2 (Global Store), neuer Abschnitt 'Buecher anlegen, aendern, loeschen':
- error-Feld im State + loadBooksFailure setzt error
- Create/Update/Delete-Actions-Trios, Effects mit concatMap (statt
  switchMap) fuer Schreibvorgaenge, immutable Reducer-Updates
- Mehrfach-on() fuer die Fehlerfaelle, selectBooksError + selectSignal
- Komponente mit dispatch + Fehleranzeige
- macht den Boilerplate-Kontrast zu Teil 3 explizit
… & 3

Zwei echte Angular-21-Apps (Vitest, zoneless) in den jeweiligen
Artikel-Verzeichnissen unter demo/ — beweisen, dass der Artikel-Code
laeuft. NgRx 21 (latest; NgRx 22 ist noch nicht auf npm).

material/ngrx-global-store/demo  (@ngrx/store + @ngrx/effects):
- Book-Modell, In-Memory BookStoreService, Actions/Reducer/Selektoren/
  Effects mit Laden + CRUD + Fehler-State (Code wie im Artikel)
- 16 Vitest-Tests gruen: Reducer (alle CRUD-Transitions), Selektoren,
  Effects (provideMockActions, switchMap/concatMap), App (provideMockStore)
- ng build erfolgreich

material/ngrx-signal-store/demo  (@ngrx/signals + @ngrx/operators):
- signalStore mit withState/withComputed/withMethods/rxMethod/withHooks,
  Laden + CRUD + Fehler-State (Code wie im Artikel)
- 7 Vitest-Tests gruen: Store (Laden, add/update/delete, Fehler,
  clearError) + App-Komponente
- ng build erfolgreich

Beim Bauen entdeckt & gefixt: Teil 3 nutzte store.clearError() im
Template, der konsolidierte Store definierte es nicht -> ergaenzt
(in App und Artikel).
…egacy-peer-deps)

- ng update @angular/{cli,core}@22 in beiden Demos (Signal Store + Global Store)
- NgRx bleibt 21.1.1: laeuft unter Angular 22; .npmrc mit legacy-peer-deps=true
  sorgt fuer reibungsloses "npm install" (NgRx-21-Peer-Range ist noch ^21.0.0)
- ChangeDetectionStrategy.Eager-Migration zurueckgenommen, damit die Demo
  deckungsgleich zum Artikel bleibt; tsconfig-Diagnostics der Migration uebernommen
- Beweis auf Angular 22 (frischer npm install): Signal Store 7/7, Global Store 16/16
  Vitest gruen, beide bauen (124 kB / 146 kB)
- Demo-READMEs auf "Angular 22" aktualisiert inkl. Hinweis zur Peer-Range
Faktische Fehler:
- Buchauflage korrigiert: "(4. Auflage)" -> "(1. Auflage)" in allen drei Teilen
- NgRx richtig aufgeloest: "Reactive State for Angular" statt "Reactive Extensions ..."
- Entity-Reducer-Bug: createBookSuccess + action.book (war createBook/action.data)

Korrektheit & Konsistenz (Artikel an bewiesene Demos angeglichen):
- Ehrliche Fehler-Typisierung: unknown + toMessage()-Helfer statt {message:string}
- Schreibvorgaenge setzen alte Fehlermeldung zurueck (Reducer/SignalStore + Artikel)
- Teil-2-State-Illustration und Test-Stubs um error-Feld ergaenzt
- Phantom-Import ./test-helper raus -> inline-Helfer (b()/book()), String-ISBNs
- "Propertys" -> "Properties"; Teil 1 "Kapitel" -> "Artikel"; scan()-Aussage entschaerft

Demos: vollstaendiges CRUD jetzt sichtbar
- Anlege-Formular + "Bewertung +1" (update) + Loeschen verdrahtet; rating wird genutzt
- OnPush ergaenzt; leere app.css entfernt; .error/.add-form-Styles; app.config aufgeraeumt
- Global-Demo: ungenutztes @ngrx/operators entfernt
- In-Memory-Service wirft bei doppelter ISBN -> Fehleranzeige ohne Server ausloesbar

Tests deutlich erweitert (Beweis auf Angular 22 / NgRx 21):
- Global: updateBook$-Effect + alle Failure-Pfade + Komponententest (16 -> 27)
- Signal: Write-Failure, loading-Transition, error-reset, Komponententest (7 -> 12)

Hygiene & Didaktik:
- .vscode/mcp.json (npx-basiert) aus der Versionierung entfernt
- Teil 3: async/Promise-Aussage entschaerft, HttpClient-Service-Hinweis, Test ohne
  doppeltes loadBooks() (onInit), store.select()-Erklaerung in Effects (Teil 2)
- Demo-READMEs: CRUD-Beschreibung, Testumfang, Hinweis auf abgeflachte Struktur
- lastModified der Artikel auf 2026-06-14

Beweis: Global 27/27, Signal 12/12 Vitest gruen; beide bauen (148 kB / 126 kB).
…ests, Politur

Korrektheit/Ehrlichkeit:
- Versionshinweise (Teil 2 + 3) richtiggestellt: Angular 22 + NgRx 21 laufen via
  legacy-peer-deps zusammen (NgRx 22 noch nicht released) – keine "passende"/
  Major-Schema-Aussage mehr
- Minko-Gechev-Zitat (nicht mehr belegbar) aus Teil 1 entfernt

Demo-Parität & exemplarischer Code:
- clearError nun auch im Global Store (Action + Reducer + OK-Button) -> beide Demos
  und beide Artikel dismissen Fehler einheitlich
- addBook nimmt die Input-Elemente entgegen, leert die Felder selbst (statt
  Mehrfach-Statement im Template); aria-label an den Eingabefeldern
- toMessage-Kommentar präzisiert (HttpErrorResponse ist kein instanceof Error)
- ungenutzte Deps @angular/forms und @angular/router aus beiden Demos entfernt

Tests erweitert (Beweis der neuen UI-Logik):
- Global 31, Signal 16 (vorher 27/12): add-on-click, Leer-Guard, rateUp-Clamp@5,
  clearError-Klick, clearError-Reducer

Konsistenz/Politur:
- Demo-READMEs: Hinweis auf flache CLI-Namen vs. BookMonkey-Konvention
- Teil-2-Testhinweis entschärft (vitest-marbles nicht im Demo enthalten)
- Teil-3-Inkrement: Ternär-Klammern + Testdaten (Title) an Demo angeglichen
- "nach dem Dispatch" -> "nach dem Anlegen" im SignalStore-Text

Beweis: Global 31/31, Signal 16/16 Vitest gruen; beide bauen (148 kB / 126 kB).
…e entfernt

Der Angular-Style-Guide hat die Typ-Suffixe abgeschafft (Datei- UND Klassennamen):
- Komponenten in den Artikeln: BookListComponent -> BookList,
  book-list.component.{ts,html,spec.ts} -> book-list.{ts,html,spec.ts}
  (die Demos waren hier bereits flach)
- Datenservice überall: book-store.service.ts -> book-api.ts, Klasse
  BookStoreService -> BookApi (Methoden getAll/create/update/remove unverändert).
  Der kurze, suffixfreie Name vermeidet zudem die Kollision mit dem
  NgRx-SignalStore BookStore in Teil 3.
- Artikel, beide Demos und alle Specs angeglichen; Demo-READMEs aktualisiert
  (verbleibende Unterschiede: Feature-Ordner books/ vs. flach, Präfix bm- vs. app-).

Beweis: Global 31/31, Signal 16/16 Vitest gruen; beide bauen (148 kB / 126 kB).
- angular.json prefix app -> bm in beiden Demos
- Selektoren: app-root -> bm-root, app-book-list -> bm-book-list
- Templates/index.html entsprechend; Demos damit deckungsgleich zu den Artikeln
- Demo-READMEs: Präfix-Unterschied entfernt (nur noch Ordnerstruktur differiert)

Beweis: Global 31/31, Signal 16/16 Vitest gruen; beide bauen (148 kB / 126 kB).
- Alle Nennungen des Projektnamens in den drei Teilen und beiden Demo-READMEs
  von "BookMonkey" auf "BookManager" umgestellt (Buch-Umbenennung 1. Auflage).
- Der Selektor-/CLI-Präfix bm- bleibt: "bm" passt weiterhin (BookManager).
- errata-3a/4a bewusst unverändert: Sie verweisen auf die historischen
  Projekte und veröffentlichte Artefakte (GitHub book-monkey4, npm
  book-monkey5-styles) und dürfen nicht umbenannt werden.
…er Pflege)

- Feste "Angular 22 / NgRx 21"-Nennungen aus den Artikel-Hinweisen entfernt;
  übrig bleibt die dauerhaft gültige Aussage zu Standalone/Signals/inject().
- Demo-READMEs: Stack ohne feste Nummern (Versionen stehen in package.json);
  der legacy-peer-deps-Hinweis ist jetzt allgemein formuliert (gilt für jeden
  Angular-Major-Wechsel, nicht nur 21↔22) – muss bei NgRx-Releases nicht mehr
  angefasst werden.
- Historische Hinweise ("seit NgRx 18/19.2") bleiben, sie sind dauerhaft korrekt.
- Konkrete Versionen leben nur noch in package.json (per ng update pflegbar).
- Die "Hinweis zur Version"-Blöcke in Teil 2 und Teil 3 komplett gestrichen.
- Demo-READMEs: legacy-peer-deps-Hinweis entfernt, Versions-Zusatz an der
  Paketzeile entfernt. Die .npmrc bleibt liegen, npm install läuft also
  weiterhin ohne Zutun durch – nur eben ohne erklärenden Kasten.
- "Projektstruktur"-Notiz bleibt (versionsunabhängig).

Damit enthalten die Texte keinerlei versionsgebundene Meta-Hinweise mehr.
- Datenservice: In-Memory raus -> echter HttpClient `BookStore` (@service())
  gegen die oeffentliche API api1.angular-buch.com (getAll/create/remove),
  provideHttpClient in app.config; Service-Tests via HttpTestingController.
- Volles Buch-Modell (isbn/title/subtitle?/authors/description/imageUrl/
  createdAt); kein rating mehr, kein update (hat das Buch ebenfalls nicht).
- Favoriten als reiner Client-State (likeBook/clearLikedBooks) -> Kontrast
  Server-State (Effects/rxMethod) vs. lokaler State ohne Seiteneffekt.
- Komponenten im Buch-Stil: praesentationale book-card (like/remove-Output)
  + smarte books-overview (Favoriten- + Buecher-Sektion).
- Naming: Datenservice BookStore (buchidentisch); global = injizierter Store,
  signal = BookSignalStore. Prefix bm- -> app- (wie das Buch).

Beweis: Global 37/37, Signal 21/21 Vitest gruen; beide bauen (174/152 kB).
…Code)

Alle Code-Schnipsel jetzt deckungsgleich mit den bewiesenen Demo-Apps:
- Datenservice BookStore (@service(), HttpClient gegen api1.angular-buch.com),
  kein In-Memory; volles Buch-Modell ohne rating; provideHttpClient.
- update/PUT entfernt (hat das Buch nicht): CRUD-Abschnitt = "anlegen und loeschen".
- NEU: Favoriten als reiner Client-State (likeBook/clearLikedBooks ohne Effect
  bzw. ohne rxMethod) - didaktischer Kontrast Server-State vs. lokaler State.
- "store"-Mehrdeutigkeit aufgeloest: Datenservice = BookStore (buchidentisch),
  global = injizierter Store, signal = BookSignalStore.
- Komponenten im Buch-Stil (book-card/books-overview), Prefix app-.
- Fehler ueber HttpErrorResponse/toMessage; Tests mit HttpTestingController.
- Teil 1: BookApi-Nennung -> BookStore.

Verifiziert: 0 Alt-Residuen, Fences balanciert, Code = Demo (Agent-Gegenpruefung),
wir-Form, keine Versions-Pins.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant