25

I am trying to understand the differences between linkedSignal and computed in Angular's Signals system, and why linkedSignal would be preferred in certain situations.

For example, if I want to manage a selectedOption that depends on shippingOptions, why not use computed like this:

const shippingOptions = signal(['Ground', 'Air', 'Sea']);
const selectedOption = computed(() => shippingOptions()[0]);

Why linkedSignal is a better choice in scenarios like this?

I haven't tried implementing this yet—I'm trying to understand the conceptual differences between linkedSignal and computed. Based on what I understand, computed should work fine for dependent state. I'm wondering what makes linkedSignal a better option in Angular's Signals system.

2 Answers 2

29

A linkedSignal is a writable signal that is already derived from another reactive expression.

Indeed :

const selectedOption = linkedSignal(() => shippingOptions()[0]);

is the same as

const selectedOption = computed(() => shippingOptions()[0]);

in terms of reading the signal and reacting to its changes.

But linked signal is also a WritableSignal, so you can write to it locally.

const selectedOption = linkedSignal(() => shippingOptions()[0]);
selectionOption('myNewValue'). 

Also the computation of the linkedSignal allows to access previous values :

const selectedOption = linkedSignal({
  source: shippingOptions(),
  computation: (newValue, previousValue) => { value[0] }
});

One of the usecases for linkedSignal is a local state in a component that depends on an input() but that also be written from inside the component itself, without exposing the inner state unlike what model would do.

Sign up to request clarification or add additional context in comments.

4 Comments

Not sure if this is clear for OP, it's not only input() signals that can be used - linkedSignal is also very handy for managing form data (initial values when you want to edit something for example).
LMAO I didn't see you answered your own question. So I think OP understood. ;)
I'm confused. Is there a typo in this example? const selectedOption = linkedSignal(() => shippingOptions()[0]); selectionOption('myNewValue'). What is this selectionOption property?
Yeah, it should have been selectionOption.set('myNewValue'), it's a WriteableSignal after all; I've submitted an edit to the answer
4

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];
  }
});

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.