Spec-Zone.ru › Angular

Зависимое состояние с linkedSignal

Вы можете использовать функцию signal, чтобы хранить некоторое состояние в коде Angular. Иногда это состояние зависит от какого-либо другого состояния. Например, представьте компонент, который позволяет пользователю выбрать способ доставки заказа:

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

В этом примере selectedOption по умолчанию соответствует первому варианту, но меняется, если пользователь выбирает другой вариант. Однако shippingOptions — это сигнал, его значение может измениться! Если shippingOptions изменится, selectedOption может содержать значение, которое больше не является допустимым вариантом.

Функция linkedSignal позволяет создать сигнал для хранения состояния, которое неразрывно связано с другим состоянием. Вернёмся к примеру выше: linkedSignal может заменить 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 работает подобно signal, но с одним важным отличием: вместо передачи значения по умолчанию вы передаёте функцию вычисления, как и в случае с computed. Когда результат вычисления меняется, значение linkedSignal становится равным результату вычисления. Это помогает гарантировать, что linkedSignal всегда содержит допустимое значение.

В следующем примере показано, как значение linkedSignal может меняться в зависимости от связанного с ним состояния:

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'

Учёт предыдущего состояния

В некоторых случаях при вычислении значения linkedSignal необходимо учитывать предыдущее значение linkedSignal.

В примере выше selectedOption всегда возвращается к первому варианту, когда меняется shippingOptions. Однако может потребоваться сохранить выбор пользователя, если выбранный им вариант всё ещё есть в списке. Для этого можно создать linkedSignal с отдельными параметрами источника и вычисления:

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` is set to the `computation` result whenever this `source` changes.
    source: this.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.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'},
    ]);
  }
}

При создании linkedSignal можно передать объект с отдельными свойствами source и computation вместо того, чтобы указывать только вычисление.

В качестве source может выступать любой сигнал, например computed или компонентный input. Значение linkedSignal обновляется при изменении source или любого сигнала, на который ссылается computation; новое значение определяется результатом переданного computation.

computation — это функция, которая получает новое значение source и объект previous. У объекта previous есть два свойства: previous.source — предыдущее значение source, а previous.value — предыдущее значение linkedSignal. Эти предыдущие значения можно использовать, чтобы определить новый результат вычисления.

ПОЛЕЗНО: При использовании параметра previous необходимо явно указывать аргументы обобщённого типа для linkedSignal. Первый аргумент обобщённого типа соответствует типу source, а второй определяет выходной тип computation.

Пользовательское сравнение на равенство

Для linkedSignal, как и для любого другого сигнала, можно настроить пользовательскую функцию сравнения на равенство. Зависимые от него сигналы используют эту функцию, чтобы определить, изменилось ли значение linkedSignal (результат вычисления):

const activeUser = signal({id: 123, name: 'Morgan', isAdmin: true});

const activeUserEditCopy = linkedSignal(() => activeUser(), {
  // 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 activeUserEditCopy = linkedSignal({
  source: activeUser,
  computation: (user) => user,
  equal: (a, b) => a.id === b.id,
});

Super-powered by Google ©2010–2025.
Code licensed under an MIT-style License. Documentation licensed under CC BY 4.0.
https://angular.dev/guide/signals/linked-signal

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API