Skip to main content
Version: 22

Form

Forms are a fundamental part of most web applications, enabling users to input data, submit information, and interact with your application. Angular provides multiple approaches to handling forms, each with robust solutions for validation, data binding, and state management, designed to address different use cases and levels of complexity.

Signal forms is the latest option based on Angular's signals, aiming to replace existing solutions.

Template-Driven form is the simplest approach that relies on directives in the template, suitable for simple forms with minimal complexity.

Reactive form is a more structured and flexible solution, but verbose, ideal for complex forms with programmatic control.

General guidelines

Avoid mixing reactive and template-driven form for the same control.

  • <input [formControl]="title" [disabled]="true">
  • <input [formControl]="title"> and title.disable() in typescript.
  • <input [formControl]="title" [value]="defaultTitle">
  • <input [formControl]="title"> and title.setValue(defaultTitle) in typescript.

Do bind form submission to the ngSubmit form event instead of button click.

❌ click event
<form>
...
<button (click)="save()">Submit</button>
</form>
✅ ngSubmit event
<form (ngSubmit)="save()">
...
<button type="submit">Submit</button>
</form>

Signal forms

Consider using signal forms.

  • ✅ Signal forms
  • ❌ Reactive forms
  • ❌ Template-driven forms
Why?

Signal forms is the new form solution leveraging Angular's signal-based reactivity system. It provides improved performance, developer experience and new features compared to other solutions. It was introduced to replace both reactive and template-driven forms.

For compatibility reasons, using reactive and template-driven forms in existing code is fine, but prefer signal forms for new code.

warning

Signal Forms are still maturing and currently have a few known limitations. Since best practices are still emerging, this section is intentionally brief. For the most up-to-date recommendations, see the official documentation.

Template-driven forms

Consider using template-driven forms for simple interactive components.

  • ❌ Multi-field form with validation
  • ✅ Toggle button to open/close a menu
  • ✅ Simple search bar
  • ✅ Single-field form

Reactive forms

Consider using reactive forms for complex forms.

  • ❌ Interactive component that is not part of a form
  • ✅ Multi-field form
  • ✅ Form with disabled fields
  • ✅ Form with validation

Do type reactive forms (FormGroup, FormControl and FormArray).

  • title: FormControl
  • title: FormControl<string>
  • form: FormGroup
  • form: FormGroup<{ title: FormControl<string> }>

Avoid using strings to access form group controls.

  • in template:
    • formControlName="title"
    • [formControl]="form.controls.title"
  • in typescript:
    • form.get('title')
    • form.controls.title
Why?

Using strings prevents type checking and can cause runtime errors if the control is missing, whereas direct property access guarantees its existence and makes code refactoring safer.

Do use getRawValue() to get all control values including disabled controls.

Custom fields

Do use ControlValueAccessor to create custom form fields.

  • ✅ PIN code input
  • ✅ Input file
  • ...