Введение в Angular Signals
По сути, сигнал представляет собой комбинацию переменной и уведомления об изменении. В отличие от observables, сигналы синхронны и всегда имеют значение.
Работа с сигналами
Существует три различных метода, которые можно использовать для управления значениями сигналов в Angular.
Метод set()
позволяет заменить текущее значение сигнала новым.
numb = signal(1);
numb.set(2);
Метод update()
можно использовать для изменения значения сигнала на основе его текущего значения.
name = signal('Dino');
name.update((value) => value + '1337') // Dino1337
Метод mutate()
используется для изменения содержимого значения сигнала без замены самого значения сигнала. При работе с массивами вы можете использовать этот метод для изменения отдельных элементов массива. Точно так же при работе с объектами этот метод можно использовать для изменения определенных свойств объекта.
arr = signal([]);
arr.mutate((v) => v.push(i));
obj = signal({ name });
obj.mutate((v) => v.name = 'Dino');
Вычисленные сигналы
Я считаю, что на данный момент это самый сильный вариант использования основных рабочих сигналов, и вот почему:
@Component({
template: `
{{ fullnameA }} // will not update
{{ fullnameB() }} // will update
<button (click)="changeSurname()">Change surname</button>
`
})
export class App {
nameA = 'John';
surnameA = 'Doe';
fullnameA = this.nameA + this.surnameA;
nameB = signal('Jane');
surnameB = signal('Doe');
fullnameB = computed(() => this.nameB() + this.surnameB());
changeSurname() {
this.surnameA = 'Haaland';
this.surnameB.set('Ibrahimovic');
}
}
На первый взгляд, многие могут предположить, что изменение переменной surnameA автоматически обновит переменную fullnameA, как если бы мы использовали выражение Angular, например {{ nameA + surnameA }}
. Однако, это не так.
Этого можно добиться с помощью сигналов (вычисленных сигналов), которые автоматически пересчитываются при изменении любого из зависимых от них сигналов.
Сигналы — это функции, и здесь мы привязываемся к вызову функции, хотя все мы знаем золотое правило что так лучше не делать.
Мы могли бы также сделать что-то вроде этого:
{{ fullnameA() }}
fullnameA = () => this.nameA + this.surnameA;
И это сработает... но!
Это демонстрирует, что fullnameA() будет вызываться всякий раз, когда срабатывает обнаружение изменений, тогда как signal fullnameB() будет вызываться только при обновлении зависимого от него сигнала. Эта функция примечательна тем, что использование сигналов позволяет более точно управлять обнаружением изменений, что может привести к повышению производительности.
Более того, вычисляемые сигналы пересчитываются при двух условиях: когда один или несколько зависимых от него сигналов изменяются и когда считывается значение вычисляемого сигнала.
Использование эффекта
Эффект сигнала очень полезен для получения уведомлений об обновлении (изменении) вашего сигнала, поэтому вы можете регистрировать его или выполнять вызовы API (как вы могли бы сделать с useEffect React.. своего рода).
effect(() => console.log(this.selected()));
effect(() => apiCall(this.param());