Сигналы: signal, computed, effect
Сигнал — это обёртка над значением, которая сообщает Angular, когда значение изменилось. Angular знает, какие части страницы читают сигнал, и обновляет только их. Сигналы — основной способ хранить состояние компонента в современном Angular.
Создание и чтение
Функция signal() создаёт сигнал с начальным значением. Чтобы прочитать значение, сигнал вызывают как функцию:
// src/app/todo-list/todo-list.ts
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-todo-list',
templateUrl: './todo-list.html',
})
export class TodoList {
count = signal(0);
}<!-- src/app/todo-list/todo-list.html -->
<p>Задач: {{ count() }}</p>На странице: «Задач: 0». Скобки после имени обязательны — и в шаблоне, и в классе.
Изменение: set и update
set() записывает новое значение, update() вычисляет его из старого:
// src/app/todo-list/todo-list.ts
export class TodoList {
count = signal(0);
increment() {
this.count.update((n) => n + 1);
}
reset() {
this.count.set(0);
}
}<!-- src/app/todo-list/todo-list.html -->
<p>Задач: {{ count() }}</p>
<button (click)="increment()">+1</button>
<button (click)="reset()">Сбросить</button>Каждый щелчок по «+1» увеличивает число на странице, «Сбросить» возвращает ноль.
Сигнал с массивом
Для списка дел сигнал хранит массив объектов. Опишите тип задачи в отдельном файле src/app/todo.ts:
// src/app/todo.ts
export interface Todo {
id: number;
title: string;
done: boolean;
}// src/app/todo-list/todo-list.ts
import { Component, signal } from '@angular/core';
import { Todo } from '../todo';
@Component({
selector: 'app-todo-list',
templateUrl: './todo-list.html',
})
export class TodoList {
todos = signal<Todo[]>([
{ id: 1, title: 'Купить хлеб', done: true },
{ id: 2, title: 'Позвонить Олегу', done: false },
]);
private nextId = 3;
add(title: string) {
const todo = { id: this.nextId++, title, done: false };
this.todos.update((list) => [...list, todo]);
}
}update() возвращает новый массив через spread — так Angular видит, что значение изменилось.
computed
computed() создаёт сигнал только для чтения, который вычисляется из других сигналов и пересчитывается сам:
// src/app/todo-list/todo-list.ts
import { computed } from '@angular/core';
export class TodoList {
// ...todos и add() как выше
left = computed(() => this.todos().filter((t) => !t.done).length);
}<!-- src/app/todo-list/todo-list.html -->
<p>Осталось сделать: {{ left() }}</p>Выводится «Осталось сделать: 1». После добавления задачи число увеличится без единой строки кода для обновления.
effect
effect() выполняет действие каждый раз, когда меняются прочитанные в нём сигналы. Его создают в конструкторе или при объявлении поля:
// src/app/todo-list/todo-list.ts
import { effect } from '@angular/core';
export class TodoList {
// ...todos как выше
constructor() {
effect(() => {
localStorage.setItem('todos', JSON.stringify(this.todos()));
});
}
}Список сохраняется в localStorage при каждом изменении. Эффекты нужны для связи с внешним миром; значения для показа вычисляйте через computed.
Частая ошибка
Изменять массив внутри сигнала напрямую: this.todos().push(task). Ссылка на массив осталась прежней, поэтому сигнал не узнаёт об изменении, и computed не пересчитается. Всегда создавайте новый массив через update() и spread.