Angular Signals надають реактивний та ефективний спосіб керування станом і обчисленими значеннями у вашому додатку. У цьому блозі ми продемонструємо, як Signals можуть оптимізувати продуктивність при роботі з Angular Reactive Forms, запобігаючи постійним переоцінкам, викликаним традиційними геттерами.
Проблема з традиційними геттерами в Angular формах
При роботі зі складними формами, особливо з полями для адреси, використання геттера для обчислення комбінованих значень адреси може призвести до частих перерахунків, навіть якщо основні дані не змінилися. Це може спричинити непотрібне навантаження на продуктивність.
Приклад: Форма адреси з Signals
Ми створимо форму з наступними полями:
- Ім'я
- BIC
- Код клірингу
- Рахунок
- Назва будівлі
- Номер будівлі
- Адреса вулиці
- Поштовий ящик
- Місто
- Штат
- Країна
- Поштовий індекс
1.
Визначення форми
import { Component } from '@angular/core';
import { Signal, computed, inject, Injector } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { toSignal } from '@angular/core/rxjs-interop';
import { of } from 'rxjs';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
form!: FormGroup;
private injector = inject(Injector);
// Signals для полів адреси
buildingName!: Signal | undefined;
buildingNumber!: Signal | undefined;
streetAddress!: Signal | undefined;
poBox!: Signal | undefined;
city!: Signal | undefined;
state!: Signal | undefined;
country!: Signal | undefined;
postalCode!: Signal | undefined;
exampleData = {
name: 'John Doe',
bic: 'ABCDEF12',
clearingCode: '12345',
account: '9876543210',
buildingName: 'Sunrise Tower',
buildingNumber: '45A',
streetAddress: 'Elm Street',
poBox: 'PO1234',
city: 'Metropolis',
state: 'New York',
country: 'USA',
postalCode: '10001',
};
getterCallCount = 0;
signalCallCount = 0;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
name: '',
bic: '',
clearingCode: '',
account: '',
buildingName: '',
buildingNumber: '',
streetAddress: '',
poBox: '',
city: '',
state: '',
country: '',
postalCode: '',
});
// Ініціалізація Signals після створення форми з ?? та Injector
this.buildingName = toSignal(
this.form.controls['buildingName'].valueChanges ?? of(undefined),
{ injector: this.injector }
);
this.buildingNumber = toSignal(
this.form.controls['buildingNumber'].valueChanges ?? of(undefined),
{ injector: this.injector }
);
this.streetAddress = toSignal(
this.form.controls['streetAddress'].valueChanges ?? of(undefined),
{ injector: this.injector }
);
this.poBox = toSignal(
this.form.controls['poBox'].valueChanges ?? of(undefined),
{ injector: this.injector }
);
this.city = toSignal(
this.form.controls['city'].valueChanges ?? of(undefined),
{ injector: this.injector }
);
this.state = toSignal(
this.form.controls['state'].valueChanges ?? of(undefined),
{ injector: this.injector }
);
this.country = toSignal(
this.form.controls['country'].valueChanges ?? of(undefined),
{ injector: this.injector }
);
this.postalCode = toSignal(
this.form.controls['postalCode'].valueChanges ?? of(undefined),
{ injector: this.injector }
);
this.form.patchValue(this.exampleData);
}
// Підхід за допомогою Getter
get fullAddressGetter(): string {
this.getterCallCount++;
return [
this.form.value.buildingName,
this.form.value.buildingNumber,
this.form.value.streetAddress,
this.form.value.poBox,
this.form.value.city,
this.form.value.state,
this.form.value.country,
this.form.value.postalCode,
]
.filter(Boolean)
.join(', ');
}
// Підхід за допомогою обчислювального сигналу (computed Signal)
fullAddressSignal = computed(() => {
this.signalCallCount++;
return [
this.buildingName?.(),
this.buildingNumber?.(),
this.streetAddress?.(),
this.poBox?.(),
this.city?.(),
this.state?.(),
this.country?.(),
this.postalCode?.(),
]
.filter(Boolean)
.join(', ');
});
}
Приклад даних
{{ exampleData | json }}
Адреса (Getter): {{ fullAddressGetter }}
Адреса (Обчислювальний сигнал): {{ fullAddressSignal() }}
Кількість викликів Getter: {{ getterCallCount }}
Кількість викликів Обчислювального сигналу: {{ signalCallCount }}
``
fullAddressGetter
## **Вихід**

## Порівняння продуктивності
1.
**Використання Getter:** Геттервикликається кожного разу, коли Angular запускає перевірку змін (change detection), навіть якщо жодне з полів адреси не було змінено. Подивіться на кількість викликів геттера.
fullAddressSignal
2. **Використання Обчислювального сигналу:**буде перераховуватися лише тоді, коли зміниться будь-який із залежних сигналів (
buildingName,
buildingNumber` тощо). Це дозволяє уникнути зайвих перерахунків і покращує продуктивність у великих додатках.
Висновок
Використання сигналів (Signals) та обчислювальних сигналів (Computed Signals) в Angular Forms надає більш ефективний спосіб керування похідним станом. Зменшуючи кількість непотрібних перерахунків і тісно пов'язуючи реактивність зі змінами конкретних значень, сигнали пропонують потужне оновлення порівняно з традиційними геттерами в Angular додатках.
Розпочніть інтеграцію сигналів у ваші Angular форми вже сьогодні для більш продуктивної та масштабованої архітектури!
Перекладено з: Optimizing Angular Forms with Signals for Better Performance