angular
November 24

Signal forms в Angular

Angular продолжает движение в сторону чистой реактивности без RxJS, и сигналы стали первым большим шагом. Теперь к ним добавились сигнальные формы — экспериментальный API, появившийся в Angular 21.

Это логичное продолжение идеи: если уже есть реактивное состояние на сигналах, то и формы тоже должны жить в этой модели — без подписок, без форм-групп, без FormControl, без грязных зон.

Первое, что бросается в глаза — состояние формы мы объявляем через обычный сигнал без Observable и без подписок:

formData = signal({
  username: 'btomer',
  firstName: 'Tomer',
  lastName: 'Bill',
  age: 35,
});

Далее создаём саму форму:

form = form(this.formData, (user) => {
  required(user.username);
  required(user.lastName);
  pattern(user.firstName, /^[A-Za-z]+$/, { message: 'Проверьте ввод, допустимы только символы' });
  max(user.age, 40);
});

По сравнению со старым подходом все стало сильно проще: никаких FormControl, FormBuilder и подписок на изменения.

Связывание с шаблоном так же упростилось:

<input [control]="form.username" />
<input [control]="form.firstName" />
<input type="number" [control]="form.age" />

<app-custom [control]="form.lastName" />

Состояние формы так же выводится через сигналы:

<pre>{{ form().valid() }}</pre>

@let errors = form().errorSummary();
@for (error of errors; track $index) {
  <p>{{ error | json }}</p>
}

<pre>{{ form().value() | json }}</pre>

И самое главное, Angular уходит от использования CVA (Control Value Accessor)

Чтобы создать новый контрол, нужно имплементировать интерфейс FormValueControl. На первый взгляд он сильно проще, но надо еще попробовать на сложном контроле. В базе выглядит как-то так:

@Component({
  selector: 'app-custom',
  imports: [Control],
  template: `
    <div>
      <label>Last name: </label>
      <input #i [value]="value()" (input)="value.set(i.value)" />
    </div>
  `
})
export class Custom implements FormValueControl<string> {
  value = model<string>('');
}

Всё, что нужно — сигнал model(), представляющий значение.
Ни registerOnChange, ни writeValue, ни setDisabledState — просто реактивная модель.

По ощущениям это действительно новая эпоха форм в Angular, похожая на то, как signals изменили работу с состоянием.

Попробовать сигнальные формы можно самостоятельно, например на StackBlitz