Alle Beiträge

Angular Signals in der Praxis: RxJS gezielt ablösen

Angular Signals in Angular-Anwendungen
Angular Signals in Angular-Anwendungen

Die Signals-API von Angular ist inzwischen so ausgereift, dass sie viele reaktive Muster wirklich vereinfacht, für die bisher RxJS notwendig war. Es geht dabei nicht darum, RxJS vollständig zu ersetzen — Observables sind nach wie vor die richtige Wahl für asynchrone Event-Streams, HTTP-Kommunikation und komplexe Datenflüsse aus mehreren Quellen. Für lokalen Komponentenzustand und abgeleitete Werte sind Signals jedoch oft übersichtlicher und leichter zu verstehen.

Das Grundprinzip

Ein Signal ist ein reaktives Grundelement, das einen Wert hält und alle Konsumenten benachrichtigt, sobald sich dieser Wert ändert. Anders als ein Observable besitzt es immer einen aktuellen Wert, der synchron gelesen werden kann. Das beseitigt eine ganze Klasse von Lade- und Undefined-Boilerplate.

import { signal, computed, effect } from '@angular/core';

const count = signal(0);
const doubled = computed(() => count() * 2);

effect(() => {
    console.log(`count ist ${count()}, doubled ist ${doubled()}`);
});

count.set(5); // löst den Effect automatisch aus

Zum Vergleich die äquivalente Implementierung mit BehaviorSubject:

import { BehaviorSubject, combineLatest, map } from 'rxjs';

const count$ = new BehaviorSubject(0);
const doubled$ = count$.pipe(map(n => n * 2));

combineLatest([count$, doubled$]).subscribe(([c, d]) => {
    console.log(`count ist ${c}, doubled ist ${d}`);
});

count$.next(5);

Beide Varianten funktionieren, aber die Signals-Version ist für diesen Anwendungsfall weniger aufwändig.

Zustandsverwaltung in Komponenten

Der größte Gewinn zeigt sich in Komponenten, die bisher mehrere BehaviorSubjects zur Verfolgung zusammengehöriger Zustände verwendet haben:

// Vorher: viele BehaviorSubjects
@Component({ ... })
export class FilterComponent {
    private query$ = new BehaviorSubject('');
    private category$ = new BehaviorSubject<string | null>(null);
    readonly results$ = combineLatest([this.query$, this.category$]).pipe(
        debounceTime(200),
        switchMap(([query, category]) => this.api.search(query, category))
    );
}

// Nachher: Signals für den Zustand, RxJS nur für den HTTP-Aufruf
@Component({ ... })
export class FilterComponent {
    readonly query = signal('');
    readonly category = signal<string | null>(null);
    readonly results = toSignal(
        toObservable(computed(() => ({ query: this.query(), category: this.category() }))).pipe(
            debounceTime(200),
            switchMap(({ query, category }) => this.api.search(query, category))
        )
    );
}

Die Bridge-Utilities toSignal und toObservable ermöglichen eine unkomplizierte Kombination beider Ansätze.

Wann RxJS die bessere Wahl bleibt

Signals sind kein vollständiger Ersatz. RxJS bleibt die richtige Wahl für:

  • HTTP-AnfragenHttpClient liefert Observables; das Fehlerbehandlungsmodell ist etabliert
  • WebSocket- / SSE-Streams — kontinuierliche Event-Quellen passen natürlich zu Observables
  • Komplexe OperatorenswitchMap, mergeMap, debounceTime, retry haben kein Signal-Äquivalent
  • Router-EventsRouter.events ist ein Observable-Stream

Eine gute Faustregel: Signals für Zustand innerhalb einer Komponente oder eines Service, RxJS an den Grenzen zwischen der Anwendung und der Außenwelt.

OnPush und Signals

Ein wichtiger Vorteil, der besonders hervorzuheben ist: Komponenten mit Signals funktionieren mit ChangeDetectionStrategy.OnPush ohne jeden zusätzlichen Aufwand. Angulars Change Detection versteht Signals nativ und rendert nur dann neu, wenn sich ein im Template gelesenes Signal tatsächlich ändert.

Das war bisher ein häufiger Schmerzpunkt mit OnPush — man musste sorgfältig steuern, welche Observables über die async-Pipe abonniert werden, und vergessene markForCheck-Aufrufe führten zu schwer nachvollziehbaren Bugs. Mit Signals übernimmt Angular dieses Tracking automatisch.

Pragmatischer Migrationspfad

Wer eine bestehende Angular-Anwendung betreut, muss nichts auf einmal umschreiben. Der pragmatische Ansatz:

  1. Neue Komponenten: von Beginn an mit Signals schreiben
  2. Bestehende Komponenten: migrieren, wenn die Datei ohnehin angefasst wird
  3. Services: RxJS beibehalten für alles, was HTTP oder komplexe Stream-Koordination erfordert
  4. Geteilter Zustand über Komponenten hinweg: Signal-basierte Store-Patterns evaluieren

Das Angular-Team hat die Migration bewusst schrittweise gestaltet — es gibt keine erzwungene Entscheidung zwischen beiden Systemen.

Teilen

Gedanken dazu? Einfach direkt schreiben.

Artikel diskutieren
Logo

IT-Beratung, Softwareentwicklung und digitale Transformation für Unternehmen in Regensburg, Oberpfalz, Bayern und DACH-weit.

Kontakt

Möchten Sie ein Projekt, einen Workflow oder eine Modernisierungsinitiative besprechen?

verixon kontaktieren

Alle Rechte vorbehalten. © 2026 verixon