In-depth Guides
Signals

Dependent state with linkedSignal

IMPORTANT: linkedSignal is developer preview. It's ready for you to try, but it might change before it is stable.

You can use the signal function to hold some state in your Angular code. Sometimes, this state depends on some other state. For example, imagine a component that lets the user select a shipping method for an order:

      
@Component({/* ... */})export class ShippingMethodPicker {  shippingOptions: Signal<ShippingMethod[]> = getShippingOptions();  // Select the first shipping option by default.  selectedOption = signal(this.shippingOptions()[0]);  changeShipping(newOptionIndex: number) {    this.selectedOption.set(this.shippingOptions()[newOptionIndex]);  }}

In this example, the selectedOption defaults to the first option, but changes if the user selects another option. But shippingOptions is a signal— its value may change! If shippingOptions changes, selectedOption may contain a value that is no longer a valid option.

The linkedSignal function lets you create a signal to hold some state that is intrinsically linked to some other state. Revisiting the example above, linkedSignal can replace signal:

      
@Component({/* ... */})export class ShippingMethodPicker {  shippingOptions: Signal<ShippingMethod[]> = getShippingOptions();  // Initialize selectedOption to the first shipping option.  selectedOption = linkedSignal(() => this.shippingOptions()[0]);  changeShipping(index: number) {    this.selectedOption.set(this.shippingOptions()[index]);  }}

linkedSignal works similarly to signal with one key difference— instead of passing a default value, you pass a computation function, just like computed. When the value of the computation changes, the value of the linkedSignal changes to the computation result. This helps ensure that the linkedSignal always has a valid value.

The following example shows how the value of a linkedSignal can change based on its linked state:

      
const shippingOptions = signal(['Ground', 'Air', 'Sea']);const selectedOption = linkedSignal(() => shippingOptions()[0]);console.log(selectedOption()); // 'Ground'selectedOption.set(shippingOptions()[2]);console.log(selectedOption()); // 'Sea'shippingOptions.set(['Email', 'Will Call', 'Postal service']);console.log(selectedOption()); // 'Email'

Accounting for previous state

In some cases, the computation for a linkedSignal needs to account for the previous value of the linkedSignal.

In the example above, selectedOption always updates back to the first option when shippingOptions changes. You may, however, want to preserve the user's selection if their selected option is still somewhere in the list. To accomplish this, you can create a linkedSignal with a separate source and computation:

      
@Component({/* ... */})export class ShippingMethodPicker {  shippingOptions: Signal<ShippingMethod[]> = getShippingOptions();    selectedOption = linkedSignal({    // `selectedOption` is set to the `computation` result whenever this `source` changes.    source: shippingOptions,    computation: (newOptions, previous) => {      // If the newOptions contain the previously selected option, preserve that selection.      // Otherwise, default to the first option.      return newOptions.find(opt => opt.id === previous?.value) ?? newOptions[0];    }   });  changeShipping(newOptionIndex: number) {    this.selectedOption.set(this.shippingOptions()[newOptionIndex]);  }}

When you create a linkedSignal, you can pass an object with separate source and computation properties instead of providing just a computation.

The source can be any signal, such as a computed or component input. When the value of source changes, linkedSignal updates its value to the result of the provided computation.

The computation is a function that receives the new value of source and a previous object. The previous object has two properties— previous.source is the previous value of source, and previous.value is the previous result of the computation. You can use these previous values to decide the new result of the computation.

Custom equality comparison

linkedSignal updates to the result of the computation every time its linked state changes. By default, Angular uses referential equality to determine if the linked state has changed. You can alternatively provide a custom equality function.

      
const activeUser = signal({id: 123, name: 'Morgan'});const email = linkedSignal(() => `${activeUser().name}@example.com`, {  // Consider the user as the same if it's the same `id`.  equal: (a, b) => a.id === b.id,});// Or, if separating `source` and `computation`const alternateEmail = linkedSignal({  source: activeUser,  computation: user => `${user.name}@example.com`,  equal: (a, b) => a.id === b.id,});// This update to `activeUser` does not cause `email` or `alternateEmail`// to update because the `id` is the same.activeUser.set({id: 123, name: 'Morgan', isAdmin: false});