Вступ
Angular продовжує еволюціонувати за допомогою API на основі сигналів, що вводять чистіший, декларативний та реактивний підхід до побудови компонентів. У Angular 17+ сигнали замінюють традиційні декоратори, такі як @Input, @Output, @ViewChild та @ViewChildren, одночасно надаючи нові можливості, такі як обов'язкові запити та динамічні трансформації.
Цей пост занурюється глибше в ці нововведення, демонструючи практичні приклади того, що відкривають сигнали, чого не можуть досягти традиційні декоратори, а також як вони спрощують комунікацію компонентів, запити до DOM і гарантії під час виконання.
1. Сигнали в Inputs і Outputs
Сигнали з @Input
Функція input() покращує комунікацію компонентів, роблячи введення реактивним, підтримуючи трансформації та забезпечуючи необхідні значення. Це усуває потребу в ручному виявленні змін (ngOnChanges) або додатковій логіці.
Приклад: Реактивне введення з трансформацією
import { Component, input } from '@angular/core';
@Component({
selector: 'app-transformed-input',
template: `
Formatted Input: {{ formattedValue() }}
` })
export class TransformedInputComponent {
formattedValue = input('', { transform: (value: string) => value.trim().toUpperCase() });
}
Що дають сигнали:
• Реактивний доступ: Використовуйте formattedValue() для отримання поточного значення.
• Трансформації: Обробка вхідних даних (наприклад, обрізання, форматування) до того, як вони будуть використані в компоненті.
Використання обов'язкових параметрів з Inputs
Переконайтеся, що важливі входи завжди надаються за допомогою параметра required:
import { Component, input } from '@angular/core';
@Component({
selector: 'app-required-input',
template: `
Required Input: {{ requiredValue() }}
`
})
export class RequiredInputComponent {
requiredValue = input('', { required: true });
}
Що дають сигнали:
- Angular викине помилку під час виконання, якщо батьківський компонент не надасть вхідне значення:
Error: Required input 'requiredValue' is missing for component RequiredInputComponent.
Сигнали з @Output
Функція output() спрощує емітери подій, безпосередньо інтегруючи сигнали в систему виведення.
Приклад: Вихід на основі сигналу
import { Component, output } from '@angular/core';
@Component({
selector: 'app-custom-button',
template: `Click Me`
})
export class CustomButtonComponent {
action = output();
notify() {
this.action.emit('Button clicked!');
}
}
Що дають сигнали:
- Безпека типів: Забезпечує, щоб емітовані події відповідали очікуваному типу.
- Спрощений синтаксис: Більше не потрібно використовувати EventEmitter.
Сигнали в запитах до компонентів
Заміняючи @ViewChild та @ViewChildren
Запити на основі сигналів, як-от viewChild() і viewChildren(), роблять доступ до DOM або дочірніх компонентів реактивним, усуваючи потребу в ручних перевірках на null або синхронізації з життєвим циклом.
Приклад: Заміняючи @ViewChild
import { Component, viewChild, ElementRef } from '@angular/core';
@Component({
selector: 'app-input-focus',
template: ``
})
export class InputFocusComponent {
focusInput = viewChild('focusInput');
ngAfterViewInit() {
this.focusInput().focus(); // Реактивний доступ до елементу DOM
}
}
Приклад: Заміняючи @ViewChildren
import { Component, viewChildren } from '@angular/core';
import { ItemComponent } from './item.component';
@Component({
selector: 'app-items',
template: ``
})
export class ItemsComponent {
items = ['Item 1', 'Item 2', 'Item 3'];
itemComponents = viewChildren(ItemComponent);
ngAfterViewInit() {
console.log(this.itemComponents().length); // Логує кількість екземплярів ItemComponent
}
}
Обов'язкові запити
Обов'язкові запити забезпечують, що певні дочірні компоненти або елементи DOM повинні існувати в шаблоні, додаючи гарантії під час виконання і усуваючи логіку перевірки на null.
Приклад: Обов'язкові запити до компонентів
import { Component, viewChild, contentChild } from '@angular/core';
@Component({
selector: 'app-custom-card',
template: `
`
})
export class CustomCard {
header = viewChild.required(CustomCardHeader); // Обов'язковий запит
body = contentChild(CustomCardBody); // Необов'язковий запит
}
3. Що дають сигнали, чого не можуть традиційні декоратори
4. Приклад: Ланцюжкові обчислювані сигнали
import { Component, signal, computed } from '@angular/core';
@Component({
selector: 'app-price-calculator',
template: `
Total Price: {{ totalPrice() }}
`
})
export class PriceCalculatorComponent {
quantity = signal(2);
pricePerUnit = signal(50);
totalPrice = computed(() => this.quantity() * this.pricePerUnit());
}
Що дають сигнали:
• Реактивні, динамічні похідні значення, які автоматично оновлюються при зміні залежностей.
Чому традиційні декоратори не вистачає:
Потрібні методи або хуки життєвого циклу для похідного стану:
@Input() quantity = 2;
@Input() pricePerUnit = 50;
totalPrice!: number;
ngOnChanges() {
this.totalPrice = this.quantity * this.pricePerUnit;
}
Висновок
API на основі сигналів в Angular відкриває ряд можливостей, які роблять компоненти більш декларативними, реактивними та безпечними за типами. Від забезпечення обов'язкових введень і запитів до дозволу на динамічні виводи та обчислений стан, ці нововведення покращують зрозумілість коду, безпеку під час виконання та продуктивність.
Заклик до дії:
"Яка функція, що працює лише з сигналами, вам найбільше подобається? Поділіться своїми думками та досвідом у коментарях!"
Перекладено з: Modernizing Angular Components: Signals, Inputs, Outputs, and Required Queries in Angular 17+