# Angular Signals in der Praxis: RxJS gezielt ablösen

![Angular Signals in Angular-Anwendungen](/blog/2026-03-26/2026-03-26-hero_de.svg)

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.

```typescript
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:

```typescript
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:

```typescript
// 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-Anfragen** — `HttpClient` liefert Observables; das Fehlerbehandlungsmodell ist etabliert
- **WebSocket- / SSE-Streams** — kontinuierliche Event-Quellen passen natürlich zu Observables
- **Komplexe Operatoren** — `switchMap`, `mergeMap`, `debounceTime`, `retry` haben kein Signal-Äquivalent
- **Router-Events** — `Router.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.
