Start / Cheat Sheets

Cheat Sheet

Angular — Moderne Patterns Cheat Sheet

Stand: · Angular Grundlagen & Moderne Patterns

AngularSignalsNgRxMicro-Frontends

Dieses Blatt ist zum Danebenlegen gedacht: die Entscheidungen und Regeln, die man im Angular-Alltag 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 samt Style Guide; der Mehrwert hier liegt in der Auswahl. Warum getrennte Projekte im Monorepo beim Ausliefern trotzdem aneinanderhängen, erklärt der Beitrag Das Monorepo entkoppelt eure Teams — beim Ausliefern nicht.

Strict-Schalter, die gesetzt sein müssen

SchalterWas er verhindert
strictVariablen vor Definition, unbehandeltes null, falsche Argumenttypen
strictInjectionParametersLaufzeitfehler, wenn ein Service oder Token nicht auflösbar ist
strictInputAccessModifiersInputs, die von außen gar nicht gesetzt werden können
strictTemplatesungültige Property-Bindings und fehlende Null-Prüfungen im Template

Zwei Blöcke, nicht einer: strict bei den compilerOptions, die übrigen drei unter angularCompilerOptions. Im falschen Block ist ein Schalter gültiges JSON und wirkungslos.

{
  "compilerOptions": { "strict": true },
  "angularCompilerOptions": {
    "strictInjectionParameters": true,
    "strictInputAccessModifiers": true,
    "strictTemplates": true
  }
}

Welcher Builder läuft, verrät eine Zeile in den Dev-Dependencies: @angular/build ist Vite, @angular-devkit/build-angular noch webpack. Umstellen mit ng update @angular/cli --name=use-application-builder.

Modul: Moderne Standards

Signals oder RxJS

SignalsRxJS
lokaler Komponentenzustandechte Datenströme und Events
Inputs und Outputskomplexe asynchrone Orchestrierung
abgeleitete Werte (computed)reaktive Transformationen über die Zeit
einmaliges Laden von DatenZusammenführen mehrerer Quellen

Prüffrage: Ist der Zustand ein Wert, der jetzt gilt, oder ein Verlauf über die Zeit? Umzustellen sind in jedem Fall die Dekoratoren: @Input()input(), @Output()output(), @ViewChildviewChild().

Modul: Moderne Standards

Store-Formen im Vergleich

Drei Ausbaustufen, in dieser Reihenfolge zu prüfen. Kanonische Kurzform:

// 1 — Service mit Signalen: Quelle privat, Ableitungen öffentlich
export class CartService {
  private items = signal<CartItem[]>([]);
  readonly cart  = this.items.asReadonly();
  readonly count = computed(() => this.items().length);
}

// 2 — Daten aus einer API
private res = httpResource<Part[]>(() => '/api/parts');
readonly parts = computed(() => this.res.value() ?? []);

// 3 — NgRx SignalStore, wenn Struktur über viele Stores nötig wird
export const CartStore = signalStore(
  withState({ items: [] as CartItem[] }),
  withComputed(({ items }) => ({ count: computed(() => items().length) })),
  withMethods(store => ({ add: (p: Part) => patchState(store, …) })),
);

effect() ist für Seiteneffekte da (Persistenz, Protokollierung), nicht für Ableitungen — dafür gibt es computed. Die Abhängigkeit entsteht allein dadurch, dass ein Signal im Callback gelesen wird.

Vor NgRx gehört eine Fassade: ein schmaler Service, der den Store intern nutzt und nach außen nur fachliche Signale zeigt. Ohne sie taucht der NgRx-Typ StateSource im abgeleiteten Typ jedes Konsumenten auf.

Modul: State Management

Architekturstufen und ihr Auslöser

StufeLöst welchen Schmerz
Ein Projekt, wenige Routen— Ausgangspunkt
Feature-Shellsträger Erststart, unklare Zuständigkeiten
Sheriffungewollte Kopplung zwischen Feature-Shells
Monorepo mit ProjektenMerge-Konflikte, divergierende Team-Prioritäten
Micro-FrontendsDeploy-Kopplung, Compliance-Isolation

Von rechts lesen: Wer den Schmerz nicht wiedererkennt, braucht die Stufe nicht. Im Monorepo bleibt ein Kopplungspunkt bestehen:

Geteilt über alle ProjekteJe Projekt eigen
eine package.json mit allen Versioneneigene tsconfig.json
ein angular.jsoneigene main.ts
ein node_moduleseigene App-Konfiguration
ein Release-/Deploy-Verbundeigenes Build-Artefakt in dist/

Feature-Shells und Monorepo ergänzen einander — das eine ersetzt das andere nicht. Sheriffs depRules sind eine Whitelist: Was nicht dasteht, ist verboten; src und src/app brauchen ein eigenes Tag.

Module: Architektur · Monorepo & Micro-Frontends

Micro-Frontends — Bausteine zur Laufzeit

BausteinRolle
federation.config.jslegt fest, was ein Remote nach außen gibt
Remote-Entry-URLsagt dem Konsumenten, was verfügbar ist
loadRemoteModulelädt einen Export zur Laufzeit nach
Host-app.routes.tshängt die Remote-Routen als Kindrouten ein
MenuItems-Exportliefert die Navigationseinträge des Remotes

Der Host kennt seine Remotes nur über URLs, nicht über Importe — deshalb ist gleichgültig, ob sie im selben Repository liegen. Einrichtung je Projekt: ng add @angular-architects/native-federation --project <name> --type host|remote --port <n>.

Modul: Monorepo & Micro-Frontends

Namen, Selektoren und Klassenmember

EbeneRegelBeispiel
DateiWörter mit Bindestrichpart-details.ts
Dateialle Dateien einer Komponente gleich benanntpart-details.html, .css
DateiTestdateien auf .spec.tspart-details.spec.ts
DateiName passt zum exportierten BezeichnerPartDetails
Selektoreigenes Präfix, nie ngnw-part-details
SelektorKomponenten in kebab-casenw-cart-summary
SelektorAttribut-Direktiven in camelCasenwHighlight
Propertykeine Kollision mit DOM-Namennicht id, style, title
Propertykein Präfix bei Inputs, kein on bei Outputsicon, saved

Klassenmember: Angular-Properties vor den Methoden, protected für alles, was nur das Template braucht, readonly für alles, was Angular initialisiert, Event-Handler nach ihrer Wirkung benennen (addToCart(), nicht onClick()). Abhängigkeiten kommen per inject() herein.

Beim Umhüllen eines Standard-Elements lohnt der Attribut-Selektor selector: 'button[nwIconButton]' — so bleiben dessen Events und Attribute erreichbar. Gegliedert wird nach Fachbereichen, nicht nach Dateityp; je Datei eine Verantwortung.

Modul: Standalone & Style Guide

Change Detection und Ladezeit

OnPush ist mit Signals und Zoneless die naheliegende Voreinstellung — eine Zeile je Komponente, eine globale Einstellung gibt es nicht:

@Component({
  selector: 'nw-part-details',
  changeDetection: ChangeDetectionStrategy.OnPush,
})

Bilder über der Falz gezielt priorisieren — alle zu priorisieren ist dasselbe wie keines. NgOptimizedImage verlangt dafür width und height:

<img [ngSrc]="part.image" width="125" height="125" [priority]="priority()" />

Im Netzwerk-Tab zuerst das Caching abschalten, sonst misst man den zweiten Besuch:

BeobachtungEinzuordnen als
die meisten Anfragen unter 100 KBunauffällig
einzelne Anfragen über 1 MBprüfen und begründen können
Anfragen im Megabyte-Bereichin jedem Fall nachgehen
viele Bilder in der Typ-AnsichtKandidaten für Optimierung
eine Datei löst sehr viele Anfragen ausAuslöser-Spalte ansehen

Absolute Grenzwerte gibt es nicht — der Vergleich mit dem eigenen Normalfall zählt. Die Angular DevTools brauchen den Entwicklungsmodus, die Chrome-Werkzeuge den Produktionsmodus.

Modul: Performance

Bundle-Budgets

TypÜberwacht
initialwas beim ersten Aufruf geladen wird
anyComponentStyledie CSS-Größe jeder einzelnen Komponente
bundleein namentlich genanntes Bundle (braucht name)
allScriptdas gesamte JavaScript der Anwendung
allalles zusammen — JS, CSS, HTML
anyjede einzelne Datei beliebigen Typs

Mit baseline wird relativ statt absolut budgetiert — praktisch, weil es auf „wie groß darf die App sein” keine allgemeine Antwort gibt:

{ "type": "initial", "baseline": "296kB",
  "maximumWarning": "5%", "maximumError": "10%" }

Modul: Performance

Typische Fallen

  • Strict-Schalter im falschen Block. Gültiges JSON, keine Fehlermeldung, keine Wirkung.
  • Modernes Angular, alter Builder. ng update erlaubt das Überspringen des Vite-Wechsels — ein Projekt auf Angular 20 kann weiterhin webpack bauen.
  • Standalone-Migration von oben. Eine Elternkomponente zieht alle Kinder mit, die wiederum ihre Eltern. Bei den Blättern anfangen.
  • async-Pipe vergessen. Im Template steht dann der Objekt-Platzhalter statt der Daten.
  • Manuell abonnierte Observables ohne Zone.js. Kein markForCheck, kein Re-Render — und keine Fehlermeldung.
  • Schreibbares Signal öffentlich gemacht. Jede Komponente kann den Zustand überschreiben; die Kapselung ist dahin.
  • Signale im Signal mutieren. Ein verändertes Array bleibt dasselbe Array — die Änderung wird nicht bemerkt. Dasselbe gilt für OnPush-Inputs.
  • effect statt computed. Ein Effekt, der sein eigenes Signal setzt, läuft endlos.
  • Sheriff nur lokal. Eine Prüfung, die nicht in der CI läuft, ist eine Erinnerung. Und bei jeder Meldung die Regel aufzuweichen führt zur Whitelist, die alles erlaubt.
  • src-Ebene ohne Sheriff-Tag. Dann meldet schon main.ts einen Verstoß.
  • Federation: Exportschlüssel ≠ Ladestring. Zeichenketten, nicht typgeprüft — der Fehler kommt erst beim Klick.
  • Getrennte dist-Ordner als Release-Unabhängigkeit lesen. Im Monorepo bleibt die Deploy-Kopplung bestehen: Eine fremde Änderung wird erst sichtbar, wenn das nutzende Projekt neu baut.
  • Budget angehoben statt Ursache gesucht. Nach einem Jahr steht die Zahl doppelt so hoch, und niemand weiß, wann.
  • Nur initial überwacht. Lazy geladene Bereiche wachsen dann unbeobachtet.
  • Im falschen Modus gemessen. Angular DevTools im Produktionsbuild zeigen einen leeren Tab, Chrome-Profile im Dev-Build unbrauchbare Zahlen.

Alle Module des Seminars

Zum Seminar Angular Grundlagen & Moderne Patterns