April 15, 2025
Angular Signals Teil 6 – Was sind Linked Signals?
Click here for the english version of this blog post.
Erweitere die Funktionalität Deiner Signals mit Linked Signals
Mit Angular 19 wurde nun ein neues Feature eingeführt: Linked Signals. Wenn Du Dich fragst: Was sind Linked Signals? Und was bringt es mir sie zu nutzen? Bist Du hier genau richtig. Wir erklären Dir wie Linked Signals funktionieren und wie Du mit ihnen die Funktionalität Deiner Signals erweitern kannst. Falls Du einen kurzen Refresh zu Angular Signals willst, haben wir noch einen Artikel der genau richtig ist. In unserem Angular Guide geben wir Dir einen Überblick über alle Artikel in unserer Angular Signals Reihe, angefangen mit: Was sind eigentlich Signals und welche Vorteile bringen Sie mit?
Was sind also Linked Signals: Sie ähneln grundsätzlich den mit computed()
erstellten readonly Signals, mit einem entscheidenden Unterschied – Linked Signals sind writeable Signals, das heißt sie können manuell geändert werden, wie signal()
. Das bedeutet, dass ihr Wert nicht ausschließlich von dem Signal in der Callback-Funktion abhängt, sondern auch gezielt angepasst werden kann. Im Folgenden schauen wir uns an wie man Linked Signals erstellt und welches Problem sie beseitigen.
Das Problem mit readonly Signals
Bevor wir uns mit Linked Signals befassen, möchten wir zunächst das zugrunde liegende Problem betrachten. Dazu erstellen wir ein kleines Beispiel und wenden Signals so an, wie wir sie bereits kennengelernt haben.
In unserem Szenario geht es um Restaurantinformationen mit zwei zentralen Funktionen:
-
Dem Nutzer werden automatisch die Details des bestbewerteten Restaurant angezeigt.
-
Der Nutzer kann ein Restaurant aus einer Liste manuell auswählen.
Was wir bisher über Signals wissen
Kümmern wir uns zunächst einmal um die erste Anforderung. Wir erstellen ein signal()
namens restaurants
, das eine Liste von Restaurant-Objekten enthält. Dann wird ein computed()
-Signal namens selectedRestaurant
erstellt, welches das Restaurant mit der höchsten Bewertung aus der restaurants
-Liste zurückgibt. Zur Erinnerung: Wenn sich das restaurants
-Signal durch set()
oder update()
ändert, wird die Callback-Funktion des abhängigen Signals (selectedRestaurant
) automatisch ausgeführt und das bestbewertete Restaurant wird ermittelt.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import { Component, computed, Signal, signal } from '@angular/core'; @Component({ ... template: ` <h3>All restaurants:</h3> @for (restaurant of this.restaurants(); track restaurant) { <li> Name: {{restaurant.name}} Rating: {{restaurant.rating}} </li> } <br> <h3>Restaurant details:</h3> <div>Name: {{selectedRestaurant().name}} Rating: {{selectedRestaurant().rating}}</div> <div>Address: {{selectedRestaurant().address}}</div> `, }) export class RestaurantSignalsComponent { // Create Restaurant Signal restaurants: Signal<Restaurant[]> = signal([ { name: 'Pizza Paradiso', rating: 2.0, address: '123 Pizza Street, Rome' }, { name: 'Sushi World', rating: 4.9, address: '456 Sushi Lane, Tokyo' }, { name: 'Pasta Palace', rating: 3.0, address: '789 Pasta Avenue, Florence' }, ]); // Create Computed Signal selectedRestaurant = computed(() => { return this.restaurants().sort((a, b) => b.rating - a.rating)[0]; }); } |
Wir möchten nun die zweite Anforderung umsetzen, damit der Nutzer ein Restaurant beispielsweise per Klick-Event manuell auswählen kann, anstatt nur automatisch das bestbewertete Restaurant angezeigt zu bekommen. Eine naheliegende Lösung wäre, selectedRestaurant
mit set()
oder update()
zu verändern. Das ist jedoch nicht möglich, da ein computed()
-Signal, wie anfangs erwähnt, readonly ist und sich ausschließlich dann aktualisiert, wenn sich seine abhängigen Signale ändern. Daher würde der folgende Code nicht wie gewünscht funktionieren:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
... @Component({...}) export class RestaurantSignalsComponent { ... // Create Computed Signal selectedRestaurant = computed(() => { return this.restaurants().sort((a, b) => b.rating - a.rating)[0]; }); // Computed Signal: Will not work selectRestaurantOnList(restaurant: any){ this.selectedRestaurant.set(restaurant) // X ERROR } } |
Um computed()
mit einer manuellen Auswahl zu kombinieren, benötigen wir ein zusätzliches writeable Signal, das den vom Nutzer gewählten Wert speichert. Da computed()
nur readonly ist, lässt sich der Wert nicht direkt setzen. Stattdessen kann ein weiteres signal()
oder ein effect()
verwendet werden, das auf Änderungen im restaurants
-Signal reagiert und sicherstellt, dass das ausgewählte Restaurant nur dann automatisch gesetzt wird, wenn der Nutzer noch keine eigene Auswahl getroffen hat. Dieser imperativer Ansatz ist umständlich, da wir explizit steuern müssen, wann und wie das ausgewählte Restaurant aktualisiert wird, anstatt dies rein deklarativ zu lösen. Genau hier bieten Linked Signals eine viel elegantere Alternative.
Die Lösung: Linked Signals erstellen
Wir möchten unser Beispiel nun mit Linked Signals umsetzen und zeigen, wie viel einfacher sich die Anforderungen damit realisieren lassen. Ein linkedSignal()
kombiniert computed()
und signal()
, indem es berechnete Werte automatisch aktualisiert, während sie gleichzeitig manuell überschrieben werden können. Das bedeutet, dass sie writeable sind, sodass der Benutzer den Wert von selectedRestaurant
über set()
oder update()
nach Belieben anpassen kann.
Ein Linked Signal kann ganz einfach mit der Funktion linkedSignal()
erzeugt werden.
Ausgewählte Restaurants werden über die set()
-Funktion gesetzt.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
_import { linkedSignal, signal } from '@angular/core'; @Component({...}) export class RestaurantLinkedSignalsComponent { restaurants = signal([ { name: 'Pizza Paradiso', rating: 2.0, address: '123 Pizza Street, Rome'}, { name: 'Sushi World', rating: 4.9, address: '456 Sushi Lane, Tokyo'}, { name: 'Pasta Palace',rating: 3.0, address: '789 Pasta Avenue, Florence'}, ]); // Create Linked Signal selectedRestaurant = linkedSignal(() => { return this.restaurants().sort((a, b) => b.rating - a.rating)[0]; }) // Manual Selection selectRestaurantOnList(restaurant: Restaurant) { this.selectedRestaurant.set(restaurant); } } |
Alternative Erstellung
Zur Erzeugung kann auch ein objektbasierten Ansatz verwendet werden. Dabei werden die Quelle (Source
) und die Berechnung (Computation
) explizit angegeben. Diese Syntax ist flexibler und eignet sich für komplexe Anwendungsfälle.
1 2 3 4 5 6 7 |
... selectedRestaurant = linkedSignal({ source: this.restaurants, computation: () => { return this.restaurants().sort((a, b) => b.rating - a.rating)[0]; } }); |
Fazit
Linked Signals schließen eine Lücke zwischen readonly computed()
-Signals und normalen writeable signal()
-Signals. Sie ermöglichen es, berechnete Werte automatisch zu aktualisieren, während sie gleichzeitig manuell überschrieben werden können.
signal()
: Ein einfaches Signal, das einen Wert speichert und sowohl gelesen als auch verändert werden kann (writetable).computed()
: Ein lesbares Signal, das automatisch neu berechnet wird, wenn sich abhängige Signals ändern, aber nicht direkt verändert werden kann (readonly).linkedSignal()
: Ein Signal, das automatisch neu berechnet wird, wenn sich abhängige Signals ändern, und welches auch manuell geändert werden kann (writeable).
Erfahrung ist der beste Lehrer: Hands-On mit Linked Signals
Bist Du bereit, die Theorie in die Praxis umzusetzen? In diesem interaktiven StackBlitz-Beispiel befindet sich die vorgestellte Beispielanwendung, die die Grundkonzepte von Linked Signals aufgreift und die vorgestellten Beispiele und Funktionen beinhaltet.
Was kommt als nächstes?
Neben den Linked Signals wurde mit Angular 19 auch die Resource experimentell eingeführt, um asynchrone Daten nahtlos in signalbasierte Anwendungen zu integrieren. Dieses Thema werden wir im nächsten Artikel näher betrachten.