This document describes Angular's template-driven forms system, which provides a declarative approach to building forms using directives in templates. Template-driven forms automatically create FormControl and FormGroup instances behind the scenes based on template directives, contrasting with the explicit model creation in reactive forms.
This page focuses on the directives and mechanisms specific to template-driven forms: NgForm, NgModel, NgModelGroup, and their interaction with control value accessors and validators. For the underlying form control architecture shared by both approaches, see 8.1 Forms Architecture For reactive forms and signal-based forms, see 8.3 Reactive Forms and Signal Forms
Template-driven forms operate through a set of directives that detect form elements in templates and automatically instantiate form control objects. The system builds a model-driven structure implicitly from the template markup.
Sources: packages/forms/src/directives/ng_form.ts1-20 packages/forms/src/directives/ng_model.ts1-20 packages/forms/src/directives/ng_model_group.ts1-20
The NgForm directive is the cornerstone of template-driven forms. It automatically attaches to <form> elements and creates a FormGroup instance to manage all controls within the form.
The NgForm directive has a selector that matches both form tags and ngForm attributes packages/forms/src/directives/ng_form.ts68 When Angular encounters a <form> tag, it automatically creates an NgForm directive instance unless the form has the ngNoForm attribute or is already bound to a formGroup packages/forms/src/directives/ng_form.ts68
Key responsibilities:
FormGroup instance packages/forms/src/directives/ng_form.ts114ControlContainer to child directives packages/forms/src/directives/ng_form.ts71AbstractControl hierarchy packages/forms/src/model/abstract_model.ts95-108Sources: packages/forms/src/directives/ng_form.ts68-210 packages/forms/src/model/form_group.ts188-205
The NgModel directive creates a FormControl instance for individual form fields and establishes two-way data binding between the form control and a component property.
name attribute defines the key in the parent FormGroup packages/forms/src/directives/ng_model.ts153NgModel looks for a parent ControlContainer (like NgForm) to register its control packages/forms/src/directives/ng_model.ts241NgModel uses selectValueAccessor to find the appropriate bridge to the DOM packages/forms/src/directives/shared.ts112Sources: packages/forms/src/directives/ng_model.ts137-250 packages/forms/src/directives/shared.ts100-120
The NgModelGroup directive creates nested FormGroup instances within the parent form, allowing hierarchical organization of form controls.
NgModelGroup extends AbstractFormGroupDirective and provides a new ControlContainer scope for all its children packages/forms/src/directives/ng_model_group.ts74 This allows nested data structures in the final form value packages/forms/src/model/form_group.ts58-68
Sources: packages/forms/src/directives/ng_model_group.ts70-85 packages/forms/src/model/form_group.ts188-195
Control Value Accessors bridge the gap between form controls (model) and native DOM elements (view). Template-driven forms automatically select the appropriate value accessor.
| Element / Attribute | Accessor Class | File Path |
|---|---|---|
input, textarea | DefaultValueAccessor | packages/forms/src/directives/default_value_accessor.ts |
input[type=checkbox] | CheckboxControlValueAccessor | packages/forms/src/directives/checkbox_value_accessor.ts |
input[type=radio] | RadioControlValueAccessor | packages/forms/src/directives/radio_control_value_accessor.ts |
select | SelectControlValueAccessor | packages/forms/src/directives/select_control_value_accessor.ts |
select[multiple] | SelectMultipleControlValueAccessor | packages/forms/src/directives/select_multiple_control_value_accessor.ts |
| Direction | Trigger | Implementation |
|---|---|---|
| View → Model | input or change event | CVA.onChange(newValue) calls control.setValue() packages/forms/src/directives/shared.ts115-118 |
| Model → View | control.setValue() | CVA.writeValue(value) updates DOM property packages/forms/src/directives/control_value_accessor.ts35 |
Sources: packages/forms/src/directives/control_value_accessor.ts15-45 packages/forms/src/directives/shared.ts100-130
Validation is applied via directives that provide themselves through the NG_VALIDATORS or NG_ASYNC_VALIDATORS multi-provider tokens packages/forms/src/directives/validators.ts40-45
Common Validator Directives:
RequiredValidator: Checks for non-empty values packages/forms/src/directives/validators.ts120MinLengthValidator: Checks minimum string/array length packages/forms/src/directives/validators.ts250EmailValidator: Validates email format packages/forms/src/directives/validators.ts500Sources: packages/forms/src/directives/validators.ts100-600 packages/forms/src/validators.ts20-50
AbstractControl tracks state and emits events that template-driven forms expose to the UI.
The NgControlStatus directive automatically applies CSS classes to elements based on the underlying control's state packages/forms/src/directives/ng_control_status.ts38:
.ng-valid / .ng-invalid packages/forms/src/model/abstract_model.ts44-51.ng-pristine / .ng-dirty packages/forms/src/model/abstract_model.ts124-131.ng-touched / .ng-untouched packages/forms/src/model/abstract_model.ts140-147Controls expose observables for tracking changes:
valueChanges: Emits ValueChangeEvent when the value is updated packages/forms/src/model/abstract_model.ts109statusChanges: Emits StatusChangeEvent when validity changes packages/forms/src/model/abstract_model.ts156-163events: A unified stream of all ControlEvent types (Submit, Reset, Value, Status) packages/forms/src/model/abstract_model.ts45Sources: packages/forms/src/model/abstract_model.ts40-188 packages/forms/src/directives/ng_control_status.ts30-60
Refresh this wiki