Guías Detalladas
Signals

Estado dependiente con linkedSignal

Puedes usar la función signal para gestionar el estado en tu código de Angular. A veces, este estado depende de algún otro estado. Por ejemplo, imagina un componente que permite al usuario seleccionar un método de envío para una orden:

@Component({/* ... */})export class ShippingMethodPicker {  shippingOptions: Signal<ShippingMethod[]> = getShippingOptions();  // Por defecto, selecciona la primera opción de envío.  selectedOption = signal(this.shippingOptions()[0]);  changeShipping(newOptionIndex: number) {    this.selectedOption.set(this.shippingOptions()[newOptionIndex]);  }}

En este ejemplo, selectedOption se establece por defecto en la primera opción, pero cambia si el usuario selecciona otra opción. Pero shippingOptions es una signal: ¡su valor puede cambiar! Si shippingOptions cambia, selectedOption puede contener un valor que ya no es una opción válida.

La función linkedSignal te permite crear una signal para gestionar un estado que está intrínsecamente vinculado a otro estado. Retomando el ejemplo anterior, linkedSignal puede reemplazar a signal:

@Component({/* ... */})export class ShippingMethodPicker {  shippingOptions: Signal<ShippingMethod[]> = getShippingOptions();  // Inicializar selectedOption a la primera opción de envío.  selectedOption = linkedSignal(() => this.shippingOptions()[0]);  changeShipping(index: number) {    this.selectedOption.set(this.shippingOptions()[index]);  }}

linkedSignal funciona de manera similar a signal con una diferencia clave: en lugar de pasar un valor por defecto, pasas una función de cómputo, igual que computed. Cuando el valor del cómputo cambia, el valor del linkedSignal cambia al resultado del cómputo. Esto ayuda a asegurar que el linkedSignal siempre tenga un valor válido.

El siguiente ejemplo muestra cómo el valor de un linkedSignal puede cambiar basado en su estado vinculado:

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'

Considerando el estado anterior

En algunos casos, el cómputo para un linkedSignal necesita considerar el valor anterior del linkedSignal.

En el ejemplo anterior, selectedOption siempre se actualiza de vuelta a la primera opción cuando shippingOptions cambia. Puedes, sin embargo, querer preservar la selección del usuario si su opción seleccionada todavía está en algún lugar de la lista. Para lograr esto, puedes crear un linkedSignal con una fuente y cómputo separados:

interface ShippingMethod {  id: number;  name: string;}@Component({/* ... */})export class ShippingMethodPicker {  constructor() {    this.changeShipping(2);    this.changeShippingOptions();    console.log(this.selectedOption()); // {"id":2,"name":"Postal Service"}  }  shippingOptions = signal<ShippingMethod[]>([    { id: 0, name: 'Ground' },    { id: 1, name: 'Air' },    { id: 2, name: 'Sea' },  ]);  selectedOption = linkedSignal<ShippingMethod[], ShippingMethod>({    // `selectedOption` se establece al resultado del `cómputo` siempre que esta `source` cambie.    source: this.shippingOptions,    computation: (newOptions, previous) => {      // Si newOptions contiene la opción previamente seleccionada, preservar esa selección.      // De lo contrario, usar por defecto la primera opción.      return (        newOptions.find((opt) => opt.id === previous?.value.id) ?? newOptions[0]      );    },  });  changeShipping(index: number) {    this.selectedOption.set(this.shippingOptions()[index]);  }  changeShippingOptions() {    this.shippingOptions.set([      { id: 0, name: 'Email' },      { id: 1, name: 'Sea' },      { id: 2, name: 'Postal Service' },    ]);  }}

Cuando creas un linkedSignal, puedes pasar un objeto con propiedades separadas source y computation en lugar de proporcionar solo un cómputo.

La source puede ser cualquier signal, como un computed o input de componente. Cuando el valor de source cambia, linkedSignal actualiza su valor al resultado del cómputo proporcionado.

El computation es una función que recibe el nuevo valor de source y un objeto previous. El objeto previous tiene dos propiedades — previous.source es el valor anterior de source, y previous.value es el resultado anterior del cómputo. Puedes usar estos valores anteriores para decidir el nuevo resultado del cómputo.

ÚTIL: Cuando uses el parámetro previous, es necesario proporcionar explícitamente los argumentos de tipo genérico de linkedSignal. El primer tipo genérico corresponde con el tipo de source y el segundo tipo genérico determina el tipo de salida del cómputo.

Comparación de igualdad personalizada

linkedSignal, como cualquier otro signal, puede ser configurado con una función de igualdad personalizada. Esta función es usada por las dependencias aguas abajo para determinar si ese valor del linkedSignal (resultado de un cómputo) cambió:

const activeUser = signal({id: 123, name: 'Morgan', isAdmin: true});const activeUserEditCopy = linkedSignal(() => activeUser(), {  // Considerar al usuario como el mismo si es el mismo `id`.  equal: (a, b) => a.id === b.id,});// O, si separando `source` y `computation`const activeUserEditCopy = linkedSignal({  source: activeUser,  computation: user => user,  equal: (a, b) => a.id === b.id,});