Post Top Ad

miércoles, 10 de julio de 2019

Validación De Forma Angular

One of the most common features in any web application is providing a form to users to input some data. You use forms daily to log in, register, place orders, etc.

Processing user inputs before validating can have serious consequences. You may end up storing invalid data like an incorrect date, email, age, etc. It could also be a security issue due to attacks like Cross-Site Scripting (XSS).

The traditional way to validate HTML forms is by using JavaScript or JQuery. Unfortunately, this approach warrants a bunch of code.

Angular, being a full-fledged framework, has provided excellent support for validating user inputs and displaying validation messages. It has lots of commonly used built-in validators that you can take advantage of, or you can even write your custom validators.

Forms in Angular

An Angular form is a regular HTML form with few additional features. For each field (input, radio, select, etc.) in the form, we need an object of the FormControl class. The FormControl object gives information about that field. Its value, if the value is valid, and if it is not valid what are the validation errors, etc.

It also provides the state of the field such as touched, untouched, dirty, pristine, etc.

Similarly, a FormGroup is the collection of the FormControl objects. Every Angular form has at least one FormGroup. You may decide to have multiple FormGroups in use-cases like separating the handling of personal details and professional details sections of a user registration form.

All the properties of a FormGroup (valid, error, etc.) is also available to the FormControl. For instance, the valid property of a FormControl will return true if all FormControl instances are valid.

So to add validation to an Angular form we need two things:

At least one FormGroup object for the form

A FormControl object for each field in the form

There are two different ways by which these control objects can be created. We can provide some directives in the template of the form and Angular can create such controls under the hood for us. Forms created by this way are called template-driven forms.

If we have some special use cases and we want more control over the form we can explicitly create such control objects. Forms created this way are called reactive forms.

Template-Driven Forms

In template-driven forms, we apply the ngModel directive for every field in the template. Angular creates a FormControl object under the hood for each such field and associate it with the respective field:

Validation in Template-Driven Forms

Angular has provided some built-in validators to validate common use cases. In order to use built-in validators, you would need to apply validation attributes to each form field where you want some validation. These validation attributes are the same as the regular HTML5 validation attributes like required, minlength, maxlength, etc. Under the hod, Angular has provided directives to match these attributes with the validator functions defined in the Angular framework.

Whenever a FormControl's value changes, Angular generates a list of validation errors by running validation. If the list is empty it means it is a valid status, otherwise, it is an invalid status.

Let's say we want to put the following validations into it:

As the fields Name and Username have the required attribute, we want to display a validation message if this field is left empty.

The Name field should have a value whose minlegth and maxlength should be 2 and 30 characters respectively.

If the username has spaces, display an invalid username message.

For every form-control in which we want to add validation, we need to add appropriate validation attributes and export ngModel to a local template variable:

In the above example, we have used the following built-in validators - required, minlength, and maxlength.

We can use the template variable name in the template to check for validation states of the used validators:

<div *ngIf="name.invalid && (name.dirty || name.touched)"class="alert alert-danger"><div *ngIf="name.errors.required">
Name is required.
</div><div *ngIf="name.errors.minlength">
Name cannot be more than 30 characters long.
</div><div *ngIf="name.errors.minlength">
Name must be at least 2 characters long.
</div></div>

As we've used a conditional statement to render the first div, it'll only be displayed if the status of the built-in validator is invalid. We've explained at the start of the section how the status is determined as valid or invalid.

Similarly, the inner div's will be displayed only if the template variable name has a property errorsand the errors property has one of the following properties - required, minlength and maxlengthand the property value id true. We've already discussed how the template variable binds to the ngModel directive and it receives these properties every time there is any change in the form control and after Angular runs the validation for that field.

Note: It is important to check for dirty and touched states, otherwise the error message will be displayed the very first time the page is loaded, which is bad for user experience. We need the validation message to be displayed in one of the following conditions:

The user changes some value, i.e the field is dirty (formControlObject.dirty)

The user uses tab or clicks to switch focus to some other element, i.e the field was touched (formControlObject.touched)

If you want to refer a full list of Angular's built-in validators, you may follow the Validators API.

Writing a Custom Validator

Sometimes the built-in validators may not cover your exact use-case. In this case, you may need to create your custom validator function.

A validator function implements the ValidatorFn interface, which means it should have the signature:

The ValidationErrors should be an object that has one or more key-value pairs:

type ValidationErrors = {
[key: string]: any;
};

The key should be a string and is used to denote the type of validation error like invalidEmail, required, etc. The value can be anything and is used to supply more information about the validation error.

Subscribe to our Newsletter

Get occassional tutorials, guides, and reviews in your inbox. No spam ever. Unsubscribe at any time.

For the above example, we want to write a custom validation function that validates if there are no spaces in the username.

While technically we can write this function anywhere in the application, it is always good practice to put all related validator functions inside a separate class:

Here we applied the formGroup directive and associated it with the FormGroup object registrationForm that we created in the component. We also associated the formControlNamedirective with the respective FormControl objects name and username.

Note: The directives to build reactive forms are defined in ReactiveFormsModule. So if you get an error such as:

Can't bind to formGroup

...then you should check if you have imported that ReactiveFormsModule in your main module app.module.ts.

Validations in Reactive Forms

In reactive forms, we do not pass the ngModel directive and we also do not use HTML5 validation attributes. We specify validators while creating the objects of the FormControl in the component itself.

As we can see the first parameter is the initial state of the control which can be kept empty i.e ''. The second parameter is ValidatorFn.

To add the built-in validator functions for a FormControl we can pass it the appropriate ValidatorFn. For the following example we've used the following built-in validators required, minLength, and maxLength - :

Please also notice, unlike Template-driven forms we do not use the validation attributes. We use the respective ValidatorFn like Validators.required, Validators.minLength(2) etc. Your code editor may provide autocomplete for all ValidatorFn the moment you type Validators followed by a dot ..

Custom validators for Reactive forms

We need to write the custom validator function the same way as we did it for the Template-Drivenform section. We can use the same custom validator function UserRegistrationFormValidators.usernameShouldBeValid in the component for the reactive form:

Conclusion

In this tutorial, we explored the two different ways to handle user inputs - Template-Driven and Reactive forms. We learned how to put validation on both types of forms. And finally, we also wrote our custom validator function and included it with the built-in validators.

As we can see Angular has great support for forms and provides some under-the-hood useful features to validate forms. Providing every single feature with Angular forms is beyond the scope of this tutorial. You may read the Angular documentation for complete information.