I think the simplest and most common example is selecting an item from a list when that list can change.
COMPUTED
Here computed is enough, since the value only needs to be updated when the list changes.
myAnimals = signal(['Cat', 'Dog', 'Pig']);
myOldestAnimal = linkedSignal(() => this.myAnimals()[0]);
addNewAnimalToList() {
this.myAnimals.update(myAnimals => [...myAnimals, ANIMALS[myAnimals.length]]);
}
removeAnimalFromList(animal: string) {
this.myAnimals.update(myAnimals => myAnimals.filter(a => !== animal);
}
WRITABLE SIGNAL
There is enough signal here, since the list does not change
myImmortalAnimals = signal(['Cat', 'Dog', 'Pig']);
myFavoriteAnimal = signal('Cat');
selectNextAnimal() {
this.myFavoriteAnimal.update(selected => {
const index = this.myImmortalAnimals().indexOf(selected);
return this.myImmortalAnimals()[(index + 1) % this.myImmortalAnimals().length];
});
}
selectPrevAnimal {
this.myFavoriteAnimal.update(selected => {
const index = this.myImmortalAnimals().indexOf(selected);
return this.myImmortalAnimals()[(index - 1 + this.myImmortalAnimals().length) % this.myImmortalAnimals().length];
});
}
LINKED SIGNAL
LinkedSignal is a hybrid of WritableSignal and Computed. Let's imagine that our list can change (an animal is missing) or we can switch to another element in the list (the mood has changed, and the favorite animal is now different). It may seem at first that it is enough to use WritableSignal.
But if our favorite animal was a cat, and it missed from the list, then the cat will continue to be stored in the signal. This looks like a bug.
And thanks to LinkedSignal, we can set logic that will automatically switch to the first animal when the list changes (always, or when the currently selected animal is no longer in the list).
animals = signal(['Cat', 'Dog', 'Pig']);
selectedAnimal = linkedSignal(() => this.animals()[0]); //simple example
addAnimalToList() {
//logic to change the list
}
removeAnimalFromList() {
//logic to change the list
}
selectNextAnimalInList() {
//logic to change the selected element
}
selectPrevAnimalInList() {
//logic to change the selected element
}
Every time the list changes, the selected item will automatically become the first one. This will help avoid the problem that when you delete a list item, the selected item will remain non-existent.
But of course, you can use more complex logic to reset an element only when it no longer exists in the list.
We can take the previous element, for example. If it exists. And if it doesn't, then we reset it to the first one.
selectedAnimal = linkedSignal<string[], string>({
source: this.animals,
computation: (list, prev) => {
if (!prev) return list[0];
if (list.includes(prev.value)) return prev.value;
return list[0];
}
});