` element for a group of validation error messages.
The `*ngIf` reveals the error group if there are any errors and
the control is either `dirty` or `touched`.
- Within the error group are separate `
` elements for each possible validation error.
Here we've prepared messages for `required`, `minlength`, and `maxlength`.
The full template repeats this kind of layout for each data entry control on the form.
.l-sub-section
:marked
We shouldn't show errors for a new hero before the user has had a chance to edit the value.
The checks for `dirty` and `touched` prevent premature display of errors.
Learn about `dirty` and `touched` in the [Forms](../guide/forms.html) chapter.
:marked
The component class manages the hero model used in the data binding
as well as other code to support the view.
+makeExample('cb-form-validation/ts/app/template/hero-form-template.component.ts','class','app/template/hero-form-template.component.ts')
:marked
Use this template-driven validation technique when working with simple forms with simple validation scenarios.
Here are the pertinent files for the template-driven approach:
+makeTabs(
`cb-form-validation/ts/app/template/hero-form-template.module.ts,
cb-form-validation/ts/app/template/hero-form-template.component.html,
cb-form-validation/ts/app/template/hero-form-template.component.ts,
cb-form-validation/ts/app/shared/hero.ts,
cb-form-validation/ts/app/shared/submitted.component.ts`,
'',
`app/template/hero-form-template.module.ts,
app/template/hero-form-template.component.html,
app/template/hero-form-template.component.ts,
app/shared/hero.ts,
app/shared/submitted.component.ts`)
.l-main-section
:marked
## Reactive Forms
Reactive forms are an alternate approach to form validation in the validation rules are specified in the model as
defined in the component class. Defining the validation in the class instead of the template gives you more control.
You can adjust the validation based on the application state or user.
Your code then becomes the source of truth for your validation.
We also remove the data binding (`ngModel`) and validation messages from the template.
This means that we need to set the default value for each control, and we need to add code
that tracks the user's changes so we can hide/show validation messages as needed.
.alert.is-important
:marked
When moving the validation attributes out of the HTML, we are no longer aria ready. Work is being done to
address this.
+makeExample('cb-form-validation/ts/app/reactive/hero-form-reactive.component.ts','class','app/reactive/hero-form-reactive.component.ts')
:marked
In the component's class, we define the form and our own data structures to manage definition and display of the validation messages:
- Declare a property for the form typed as a `FormGroup`.
- Declare a property for a collection that contains the *current* validation messages to display to the user.
We'll initialize this collection with one entry for each control. We'll update this collection
with appropriate validation messages when validation rules are broken.
- Declare a property for a collection that contains the set of *possible* validation messages. We'll initialize
this collection with all of the possible validation messages for each control.
- Add a constructor for the class and use dependency injection to inject in the `FormBuilder` service. We use
that service to build the form.
- In the constructor, initialize the collections.
- The `formError` collection is initialized using the control
name as the key and the current validation message as the value. When the form is first displayed, no validation messages
should appear, so the current validation message for each control is empty.
- The `validationMessages` collection is
initialized using the control name as the key and the set of possible validation messages as the value. For this example,
we hard-code in the set of validation messages. But you can retrieve these messages from an external file or
from a database table. Alternatively, you could build a service that retrieved and managed the set of validation messsages.
- Build a method to create the form. We name this method `buildForm` in our example.
The form is created in a method so it can be
called again to reset the form with different default values when adding a new hero.
- In the `buildForm` method, group the controls defined for the form using the `FormBuilder` instance. Here we define each control's name, default value, and
validation rules.
- We call this `buildForm` method from the `ngOnInit` lifecycle hook method. But in many applications, you may need to call `buildForm`
from somewhere else. For example, if the default values are coming from an http request, call the `buildForm` method
after the data is retrieved.
.l-sub-section
:marked
Learn more about `ngOnInit` in the [LifeCycle Hooks](../guide/lifecycle-hooks.html) chapter.
:marked
There is one more important thing that we need to do. We need to watch for any changes that the user makes and adjust the
validation messages appropriately. For example, if the user enters a single character into the name field, we need to
change the validation message from `Name is required` to `Name must be at least 4 characters long`. And when the user
enters the fourth character, we need to remove the message entirely.
To watch for changes, we add one additional statement to the `buildForm` method. We subscribe to the built-in
`FormGroup`'s `valueChanges` observable. Each time any control on the form is changed by the user, we receive a
notification. In our example, we then call an `onValueChanged` method to reset the validation messages.
In the `onValueChanged` method, we:
- Loop through each entry in the `formError` collection.
- Determine whether the control has an error using the control's properties and our business rules. In our example
we define that a control has an error if the control is dirty and not valid.
- We clear any prior validation messages.
- If the control has a validation error, we loop through the errors collection and concatenate the appropriate
validation messages into one message for display to the user.
We'll use the form and `formError` collection properties in the template. Notice that when using the reactive forms approach,
the amount of code required for each control in the template is signficantly reduced.
+makeExample('cb-form-validation/ts/app/reactive/hero-form-reactive.component.html','name-with-error-msg','app/reactive/hero-form-reactive.component.html')
:marked
In the template, define a standard label and set up an input box for validation as follows:
- Set the `formControlName` directive to the name of the control as defined in the `FormBuilder`'s `group` method, `name` in this example.
In this example we also used `ngClass` to set a style on required fields. This is optional and based on the styling
you select for your application.
In the `div` element for the validation messages, we use the validation messages collection (`formError` in this example) to determine whether to display a validation message.
- We use `*ngIf` to check whether the control has a validation message in the collection.
- If so, we display it to the user using interpolation.
Repeat for each data entry control on the form.
The template then has no validation logic. If there is a validation message in the collection it displays it, if not
it doesn't. All of the logic is in the component class.
Use this technique when you want better control over the validation rules and messsages.
Here are the pertinent files for the reactive forms approach:
+makeTabs(
`cb-form-validation/ts/app/reactive/hero-form-reactive.module.ts,
cb-form-validation/ts/app/reactive/hero-form-reactive.component.html,
cb-form-validation/ts/app/reactive/hero-form-reactive.component.ts,
cb-form-validation/ts/app/shared/hero.ts,
cb-form-validation/ts/app/shared/submitted.component.ts`,
'',
`app/reactive/hero-form-reactive.module.ts,
app/reactive/hero-form-reactive.component.html,
app/reactive/hero-form-reactive.component.ts,
app/shared/hero.ts,
app/shared/submitted.component.ts`)
:marked
[Back to top](#top)