Сигнали в Angular
Що таке Сигнали?
Сигнали (введені в Angular 16) — це новий реактивний примітив для управління станом. Вони забезпечують тонку реактивність — Angular точно знає, що змінилося, і оновлює лише ті частини інтерфейсу, які постраждали.
Основне Використання
typescript
import { signal, computed, effect } from '@angular/core';
@Component({
template: \`
<p>Кількість: {{ count() }}</p>
<p>Подвійна кількість: {{ doubleCount() }}</p>
<button (click)="increment()">+</button>
<button (click)="reset()">Скинути</button>
\`
})
export class CounterComponent {
// Записуваний сигнал
count = signal(0);
// Обчислений сигнал (похідний, тільки для читання)
doubleCount = computed(() => this.count() * 2);
increment() {
// Встановити нове значення
this.count.set(this.count() + 1);
// Або оновити на основі попереднього значення
this.count.update(c => c + 1);
}
reset() {
this.count.set(0);
}
}signal() — Записуваний Сигнал
typescript
const name = signal('Аліса');
// Читання
console.log(name()); // "Аліса"
// Запис
name.set('Боб');
// Оновлення на основі поточного значення
name.update(current => current.toUpperCase());
// Мутація (для об'єктів/масивів — мутація на місці)
const users = signal([{ name: 'Аліса' }]);
users.mutate(arr => arr.push({ name: 'Боб' }));computed() — Похідний Сигнал
typescript
const firstName = signal('Джон');
const lastName = signal('Доу');
// Автоматично перераховується, коли змінюються залежності
const fullName = computed(() => \`\${firstName()} \${lastName()}\`);
console.log(fullName()); // "Джон Доу"
firstName.set('Джейн');
console.log(fullName()); // "Джейн Доу"
// обчислені сигнали є ТІЛЬКИ ДЛЯ ЧИТАННЯ
// fullName.set('X'); // ❌ Помилка!effect() — Побічні Ефекти
typescript
const count = signal(0);
// Виконується щоразу, коли count змінюється
effect(() => {
console.log('Кількість змінилася на:', count());
// Лог, збереження в localStorage, виклик API тощо.
});
count.set(5); // Лог: "Кількість змінилася на: 5"Сигнали проти Спостережуваних
| Особливість | Сигнали | Спостережувані (RxJS) |
|---|---|---|
| Синхронні | ✅ Завжди має значення | ❌ Може бути асинхронним |
| Поточне значення | ✅ signal() | ⚠️ Потрібен BehaviorSubject |
| Автоочищення | ✅ Без підписок | ❌ Потрібно скасовувати підписки |
| Використання в шаблонах | {{ signal() }} | {{ obs$ | async }} |
| Оператори | ❌ Немає | ✅ Багаті оператори |
| Потоки/Події | ❌ Не призначено для | ✅ Створено для потоків |
| Виявлення змін | Тонке | На основі зони |
Вхідні Сигнали (Angular 17+)
typescript
@Component({ /* ... */ })
export class UserCard {
// Вхідні дані на основі сигналів
name = input.required<string>();
age = input(0); // З значенням за замовчуванням
role = input<'admin' | 'user'>('user');
// Обчислене з вхідних сигналів
isAdult = computed(() => this.age() >= 18);
}html
<app-user-card [name]="'Аліса'" [age]="25" />Практичний Приклад: Кошик для Покупок
typescript
@Component({ /* ... */ })
export class CartComponent {
items = signal<CartItem[]>([]);
totalPrice = computed(() =>
this.items().reduce((sum, item) => sum + item.price * item.quantity, 0)
);
itemCount = computed(() =>
this.items().reduce((sum, item) => sum + item.quantity, 0)
);
addItem(item: CartItem) {
this.items.update(items => [...items, item]);
}
removeItem(id: string) {
this.items.update(items => items.filter(i => i.id !== id));
}
}Важливо:
Сигнали є сучасною реактивною системою Angular — простішою, ніж RxJS для синхронного стану. Використовуйте signal() для стану, computed() для похідних значень і effect() для побічних ефектів. Вони забезпечують тонке виявлення змін (не потрібен Zone.js). Продовжуйте використовувати RxJS для асинхронних потоків (HTTP, WebSocket, події). Сигнали та Спостережувані працюють разом.
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.