Angular Forms


What are forms in Angular 2?

• Forms in Angular provides data-binding and validation of input controls.
• Forms are critical to any modern front-end application, and they're a feature that we use every day.
• Some forms have less number of input fields, whereas other forms have an array of fields that stretch to a pages or tabs.
• Forms are always present in any website or application. It can be used to perform countless data-entry tasks like- authentication, order submission or a profile creation.

 The following four status are commonly used by forms
a) valid – state of the validity of all form controls, true if all controls are valid.
b) invalid – inverse of valid; true if some control is invalid.
c) pristine – gives a status about the “cleanness” of the form; true if no control was modified.
d) dirty – inverse of pristine; true if some control was modified.
 The latest version of Angular has two form-building process
1) Template-driven forms
2) Model-driven / Reactive forms
Note:- Both the forms belong to the @angular/forms library and are based on the same form control classes.
 Features of Template Driven Forms
 Easy to use like AngularJS
 Suitable for simple scenarios and fails for complex scenarios.
 Two-way data binding(using [(NgModel)]).
 Minimal component code.
 Automatic track of the form and its data.
 Unit testing is another challenge.
 Features of Model-driven/Reactive Forms
 More flexible, but needs a lot of practice.
 Handles any complex scenarios.
 No data binding is done (immutable data model preferred).
 More component code and less HTML markup.
 Reactive transformations can be made possible.
 Handling events when the components are distinct until changed.
 Adding elements dynamically.
 Easier unit testing.
 Advantages and Disadvantages of Template Driven Forms
 The up-side of this way of handling forms is its simplicity, and it’s probably more than enough to build a very large range of forms.
 On the downside, the form validation logic cannot be unit tested. The only way to test this logic is to run an end to end test with a browser, for example using a headless browser like PhantomJs.
 Difference between Template Driven Forms and Model-driven/Reactive Forms
Template Driven Forms Model-driven/Reactive Forms
Template-driven forms are asynchronous. Reactive forms are synchronous.
Most of the work is done in the template. most of the work is done in the Component instead of dividing it over different form templates.
Template driven form has form.submitted flag in the exported ngForm. model driven form doesn’t have that.
in template driven form we have exported ngModel, the syntax can be shortened to just street.valid Reading form control property in model driven form is not syntax friendly in the view. For example, the syntax of reading address validity is myForm.controls.address.controls.street.valid
Template driven form is responsible for setting up the form, the validation, control, group etc. These are suitable for simple scenarios, uses two-way data binding using the [(NgModel)]
Unit testing might be a challenge for this form. Unit testing is easy in this form.
With this form, we will be able to create and manipulate form control objects directly in the template. With this form, we will be able to create and manipulate form control objects directly in the Component.
In this we can push data model values into the form controls as well as pull values that have been changed by the user. The component can observe changes in the form control state and react to them. This is especially useful for showing a validation message.
 Template Driven Forms -
Angular 2 provides a similar mechanism also called ng-model, that allow us to build what is now called Template-Driven forms. Let's look at a form built using it-

Template-driven Form Example:

Explanation- What we have done here is to declare a simple form with two controls: first name and password, both of which are required. The form will trigger the controller method onSubmitTemplateBased on submission, but the submit button is only enabled if both required fields are filled in.  Steps for Template Driven Forms • Add FormsModule to app.module.ts. • Create a class for the User model. • Create initial components and layout for the signup form. • Use Angular form directives like ngModel, ngModelGroup, and ngForm. • Add validation using built-in validators. • Display validation errors meaningfully. • Handle form submission using ngSubmit.  Basic Form Setup Step 1: To use the template-driven form directives, we need to import the FormsModule from @angular/forms and add it to the imports array in app.module.ts.
import { FormsModule } from '@angular/forms'; @NgModule({ . . imports: [ BrowserModule, FormsModule ], . . }) export class AppModule { }
Step 2: Next, create a class that will hold all properties of the User entity. We can either use an interface and implement it in the component or use a Typescript class for the model.
User.ts export class User { id: number; email: string; password: { pwd: string; confirmPwd: string; }; gender: string; terms: boolean; constructor(values: Object = {}) { Object.assign(this, values); } }
Step 3: Now, create an instance of the class in the SignupForm component. I've also declared an additional property for the gender.
Step 4: In signup-form.component.html signup form has a select field with a list of options.
app/signup-form/signup-form.component.ts import { Component, OnInit } from '@angular/core'; import { User } from './../User'; @Component({ selector: 'app-signup-form', templateUrl: './signup-form.component.html', styleUrls: ['./signup-form.component.css'] }) export class SignupFormComponent implements OnInit { private gender: string[]; private user:User; ngOnInit() { this.gender = ['Male', 'Female', 'Others']; this.user = new User({ email:"", password: { pwd: "" , confirm_pwd: ""}, gender: this.gender[0], terms: false}); } } app/signup-form/signup-form.component.html
SignUp
Step 5: Next, we want to bind the form data to the user class object so that when we enter the signup data into the form, a new User object is created that temporarily stores that data. This way, we can keep the view in sync with the model, and this is called binding.  Reactive form- Step 1: To use reactive forms, we need to import the ReactiveFormsModule into our parent module.
src/app.module.ts import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; @NgModule({ declarations: [AppComponent], imports: [BrowserModule, ReactiveFormsModule], providers: [], bootstrap: [AppComponent] }) export class AppModule {}
Step 2 : Defining a Model. We are creating a so-called model. A model is just a class, that contains all the fields, that our data can have.
src/models/contact-request.ts export class ContactRequest { personalData: PersonalData; requestType: any = ''; text: string = ''; } export class PersonalData { email: string = ''; mobile: string = ''; country: string = ''; }
Step 4: Setting up a Contact-Component. Next, we need a component, to create our form in. Use the following command of the angular-cli – ng generate component contact This will create a basic component for us to use. It will also import that component into our AppModule automatically. Creating a Reactive Form in Angular only responsible for the form-representation. Note: Reactive Programming and Immutability In reactive forms, it is keeping the data in the form model, until the user hits the submit button. Only then the data is copied over to the original model, replacing everything. This type of overwriting everything is called "immutable objects". Form Controls To create this form-model, angular uses a class called FormGroup. To define our form-model, we create a new FormGroup. The constructor of this class then takes an object, that can contain sub-form-groups and FormControls. FormControls represent the smallest possible unit and typically represent a HTML-control (input, select, etc) of your template. Step 5: let's create a new method, that creates a new instance of the form-model for us method is createFormGroup().
src/contact/contact.component.ts createFormGroup() { return new FormGroup({ personalData: new FormGroup({ email: new FormControl(), mobile: new FormControl(), country: new FormControl() }), requestType: new FormControl(), text: new FormControl() }); }
Step 6: We want the country and the request Type to be select-able as a drop-down, we also should provide some options to choose from:
src/contact/contact.component.ts countries = ['USA', 'Germany', 'Italy', 'France']; requestTypes = ['Claim', 'Feedback', 'Help Request']; Finally, we save the result of that method to a public field, to be accessible in our template. src/contact/contact.component.ts contactForm: FormGroup; constructor() { this.contactForm = this.createFormGroup(); }
Here is how our component should look like now:
src/contact/contact.component.ts import { Component, OnInit } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; @Component({ selector: 'app-contact', templateUrl: './contact.component.html', styleUrls: ['./contact.component.css'] }) export class ContactComponent implements OnInit { contactForm: FormGroup; countries = ['USA', 'Germany', 'Italy', 'France']; requestTypes = ['Claim', 'Feedback', 'Help Request']; constructor() { this.contactForm = this.createFormGroup(); } // Step 1 createFormGroup() { return new FormGroup({ personalData: new FormGroup({ email: new FormControl(), mobile: new FormControl(), country: new FormControl() }), requestType: new FormControl(), text: new FormControl() }); } ngOnInit() {} }
Step 7: Basically, our HTML does look just like a regular form. The only difference is, that we should tell angular, which FormGroup and which FormControl to use for each control.
src/contact/contact.component.html
Step 8: At this point, your app should be in a runable state.  Using the Angular Form-Builder Angular provides a service called FormBuilder. This FormBuilder allows us to build our form-model with less code. To use the FormBuilder, we need to request it via dependency injection. We do so in the constructor of our component.
src/contact/contact.component.ts constructor(private formBuilder: FormBuilder) { this.contactForm = this.createFormGroup(); }
The FormBuilder class should be imported from @angular/forms:
src/contact/contact.component.ts import { FormControl, FormGroup, FormBuilder } from '@angular/forms';
Now we can use that FromBuilder to build our form-model. We have created a new method called "createFormGroupWithBuilder()".
src/contact/contact.component.ts createFormGroupWithBuilder(formBuilder: FormBuilder) { return formBuilder.group({ personalData: formBuilder.group({ email: 'defaul@email.com', mobile: '', country: '' }), requestType: '', text: '' }); }
Using this approach, we should define the default values of each field. You can just pass them an empty string, or fill it with some default-data. Passing a Class to the Form-Builder Instead of defining our form-model inline, the FormBuilder allows us to pass in any JavaScript object. So, we pass in a new instance of our PersonalData class.
src/contact/contact.component.ts createFormGroupWithBuilderAndModel(formBuilder: FormBuilder) { return formBuilder.group({ personalData: formBuilder.group(new PersonalData()), requestType: '', text: '' }); }
However, we should make sure, that all fields of the form are also present in this object. Therefore we have assigned the default values in our contact-request-model.  Extracting the Data from the Form Now it's time to get data out of that form into a contact-request object. For that, we are using the submit-button. We also already registered the callback-method "onSubmit”. To be able to listen to the button-press, we need to implement that callback in our component. Inside of that callback, we can access the form's values by its values property this.contactForm.value These values are still referenced by our form. If we just copy that reference and modify the data elsewhere, the form will be impacted, as well. This can cause weird side-effects. Therefore, we need to create a copy of the data. In this example, we are using Object.assign for that. const result: ContactRequest = Object.assign({}, this.contactForm.value); However, Object.assign creates something called a shallow copy. That means that all primary-fields of the object are copied properly, but the references to sub-objects or lists are still the same. Instead what we want to do is create a deep copy. We can either achieve that by doing it copying everything manually or use a utility like lodash's cloneDeep function. We will use the manual method here.
result.personalData = Object.assign({}, result.personalData);
Overall, the onSubmit method should now look something like this:
src/contact/contact.component.ts onSubmit() { // Make sure to create a deep copy of the form-model const result: ContactRequest = Object.assign({}, this.contactForm.value); result.personalData = Object.assign({}, result.personalData); // Do useful stuff with the gathered data console.log(result); }
 Resetting the Form Resetting the form is one of the easier parts with reactive forms. Again, we are going to use the reset-button. All we need to do now, is to implement the revert-callback into our component. To reset the form, we can either use the form's reset method without any parameter, // Resets to blank object this.contactForm.reset(); which results in an empty object, or pass a set of default parameters along:
// Resets to provided model this.contactForm.reset({ personalData: new PersonalData(), requestType: '', text: '' });
All in all, the revert method simply looks like this:
src/contact/contact.component.ts revert() { // Resets to blank object this.contactForm.reset(); // Resets to provided model this.contactForm.reset({ personalData: new PersonalData(), requestType: '', text: '' }); }
 Angular forms building blocks: 1) FormControl 2) FormGroup 3) FormArray  Use formGroup to bind the form to an instance of FormGroup on our component.  Use formGroupName to map to a child FormGroup of myform.  Use formControlName to bind to an instance of a FormControl directly under myform['name']..  Now each form control in the template is mapped to form controls in our model and so as we type into the input elements myform.value updates and our debug section at the bottom prints out the current value of the form. Note: In this example app, we are going to work using Reactive Forms. FormControl: It tracks the value and validity status of an angular form control. It matches to a HTML form control like an input. The following is an example which shows a FormControl for the ‘name’ property which should not be empty.
this.username = new FormControl('agustin', Validators.required);
FormGroup: It tracks the value and validity state of a FormBuilder instance group. It aggregates the values of each child FormControl into one object, using the name of each form control as the key. It calculates its status by reducing the status of its children. If one of the controls inside a group is invalid, the entire group becomes invalid.
this.user_data = new FormGroup({ username: new FormControl('agustin', Validators.required), city: new FormControl('Montevideo', Validators.required) });
FormArray: It is a variation of FormGroup. The main difference is that its data gets serialized as an array, as opposed to being serialized as an object in case of FormGroup. Useful when you don’t know how many controls will be present within the group, like in dynamic forms.
this.user_data = new FormArray({ new FormControl('agustin', Validators.required), new FormControl('Montevideo', Validators.required) });
FormBuilder: It is a helper class that creates FormGroup, FormControl and FormArray instances for us. It reduces the repetition and clutter by handling details of form control creation for you.
this.validations_form = this.formBuilder.group({ username: new FormControl('', Validators.required), email: new FormControl('', Validators.compose([ Validators.required, Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$') ])) });
Note: All of them should be imported from the @angular/forms module.
import { Validators, FormBuilder, FormGroup, FormControl } from '@angular/forms';
 Angular is packed with its own validator built in Angular input validations-  minLength: Validator that requires controls to have a value of a minimum length.  maxLength: Validator that requires controls to have a value of a maximum length.  pattern: Validator that requires a control to match a regex to its value. You can find more information about regex patterns in the PatternValidator reference.  email: Validator that performs email validation.  compose: is used when more than one validation is needed for the same form field.  required: Validator that requires controls to have a non-empty value. It also validates that the value matches the input type. For example, if the input is of “email” type, then the input will be valid if it’s not empty and if the value is of email type.  ngForm and ngModel ngForm and ngModel are Angular directives that are essential to create template-driven forms. The NgForm directive supplements the form element with additional features. It holds the controls you created for the elements with an ngModel directive and name attribute, and monitors their properties, including their validity. It also has its own valid property which is true only if every contained control is valid.  Form model We represent the form as a model composed of instances of FormGroups and FormControls. Let’s create the model for our form on our component,
import { FormGroup, FormControl } from '@angular/forms'; class ModelFormComponent implements OnInit { myform: FormGroup; (1) ngOnInit() { myform = new FormGroup({ name: new FormGroup({ (2) firstName: new FormControl(), (3) lastName: new FormControl(), }), email: new FormControl(), password: new FormControl(), language: new FormControl() }); } }
Explanation- • myform is an instance of FormGroup and represents the form itself. • FormGroups can nest inside other FormGroups. • We create a FormControl for each template form control. • The myform property is an instance of a FormGroup class and this represents our form itself. • Each form control in the template is represented by an instance of FormControl. It checks it’s validity - valid or invalid and even its current value. • These instances of FormControls nest inside our top level myform: FormGroup, we can nest FormGroups inside other FormGroups. • In our model, we’ve grouped the firstName and lastName controls under a FormGroup called name which itself is nested under our top level myform: FormGroup. • Like the FormControl instance, FormGroup instances encapsulates the state of all its inner controls, for example an instance of a FormGroup is valid only if all of its inner controls are also valid.  Linking the form model to the form template We have the HTML template for our form and the form model on our component, next up we need to link the two together. We do this using several directives which are found in the ReactiveFormsModule, so let’s import that and add it to the imports on our NgModule. import { ReactiveFormsModule } from '@angular/forms'; formGroup Firstly, we bind the
element to our top level myform property using the formGroup directive, like so: HTML
...
Now we’ve linked the myform model to the form template we have access to our myform model in our template. The value property of the myform model returns the values of all the controls as an object. We can use that with the json pipe to output some useful debug information about our form, like so:
HTML
{{myform.value | json}}
Running our application now prints out the below in the debug pre-tag: JSON { "name": { "firstName": null, "lastName": null }, "email": null, "password": null, "language": null }
Initially this seems quite exciting but as we enter values into each of the input fields in our form we would see that the model isn’t getting updated, the values remain null. That’s because although we’ve linked the form element to the myform model this doesn’t automatically link each form control in the model with each form control in the template, we need to do this explicitly with the formControlName and formGroupName directives. formGroupName & formControlName We use the formControlName directive to map each form control in the template with a named form control in the model, like so: HTML
This looks for a model form control called email in the top level of our myform model and links the element to that. We can also associate a group of template form controls to an instance of a form group on our model using formGroupName directive. Note: In our template the controls we want to group must be surrounded by another element, we’ve surrounded our controls with a fieldset element but it doesn’t need to be called fieldset, could for example be a div. We then associate the fieldset element with the form group called name in our model like so: HTML
...
Then inside our fieldset element we again use the formControlName directive to map individual form controls in the template to form controls under the form group name in our model. In the end the template should look like this: HTML
(1)
(2)
{{myform.value | json}}
 Reactive form explanation with real example- 1) Reactive Forms Validation App Component The app component defines the form fields and validators for our registration form using an Angular FormBuilder to create an instance of a FormGroup that is stored in the registerForm property. The registerForm is then bound to the form in the template below using the [formGroup] directive. I also added a getter 'f' as a convenience property to make it easier to access form controls from the template. So, for example you can access the email field in the template using f.email instead of registerForm.controls.email.
import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; @Component({ selector: 'app', templateUrl: 'app.component.html' }) export class AppComponent implements OnInit { registerForm: FormGroup; submitted = false; constructor(private formBuilder: FormBuilder) { } ngOnInit() { this.registerForm = this.formBuilder.group({ firstName: ['', Validators.required], lastName: ['', Validators.required], email: ['', [Validators.required, Validators.email]], password: ['', [Validators.required, Validators.minLength(6)]] }); } // convenience getter for easy access to form fields get f() { return this.registerForm.controls; } onSubmit() { this.submitted = true; // stop here if form is invalid if (this.registerForm.invalid) { return; } alert('SUCCESS!! :-)') } }
2) Reactive Forms Validation App Template The app component template contains all the html markup for displaying the example registration form in your browser. The form element uses the [formGroup] directive to bind to the registerForm FormGroup in the app component above. The form binds the form submit event to the onSubmit() handler in the app component using the Angular event binding (ngSubmit)="onSubmit()". Validation messages are displayed only after the user submit the form for the first time, this is controlled with the submitted property of the app component.

Angular 6 Reactive Form Validation

First Name is required
Last Name is required
Email is required
Email must be a valid email address
Password is required
Password must be at least 6 characters
3) Reactive Forms Validation App Module The main thing we need to remember for using reactive forms in Angular is to import the ReactiveFormsModule from '@angular/forms' and include it in the imports array of the @NgModule decorator.
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { ReactiveFormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; @NgModule({ imports: [ BrowserModule, ReactiveFormsModule ], declarations: [ AppComponent ], bootstrap: [AppComponent] }) export class AppModule { }
 Template-Driven form explanation with real example- 1) Template-Driven Forms Validation App Component The app component doesn't need to do much since the form fields and validators are defined in the template when using Angular template-driven forms. The component defines a model object which is bound to the form fields in the template to give us access to the data entered into the form from the app component.
import { Component } from '@angular/core'; @Component({ selector: 'app', templateUrl: 'app.component.html' }) export class AppComponent { model: any = {}; onSubmit() { alert('SUCCESS!! :-)\n\n' + JSON.stringify(this.model)) } }
2) Template-Driven Forms Validation App Template The app component template contains all the html markup for displaying the example registration form in your browser. The form input fields use the [(ngModel)] directive to bind to properties of the model object in the app component. Validation is implemented using the attributes required, minlength and email, the Angular framework contains directives that match these attributes with built-in validator functions. The form binds the submit event to the onSubmit() event handler in the app component using the Angular event binding (ngSubmit)="onSubmit()". Validation messages are displayed only after the user attempts to submit the form for the first time, this is controlled with the f.submitted property of the #f="ngForm" Angular template variable.

Angular 7 Template-Driven Form Validation

First Name is required
Last Name is required
Email is required
Email must be a valid email address
Password is required
Password must be at least 6 characters
Confirm Password is required
Passwords must match
3) Template-Driven Forms Custom "Must Match" Validator The custom MustMatch validator is used in this example to validate that both password fields - password and confirmPassword - are matching. However, it can be used to validate that any pair of fields is matching (e.g. email and confirm email fields). It works slightly differently than a typical custom validator because I'm setting the error on the second field instead of returning it to be set on the formGroup the mustMatch validation error is displayed below the confirmPassword field so I think it makes sense that the error is attached the confirmPassword form control.
import { FormGroup } from '@angular/forms'; // custom validator to check that two fields match export function MustMatch(controlName: string, matchingControlName: string) { return (formGroup: FormGroup) => { const control = formGroup.controls[controlName]; const matchingControl = formGroup.controls[matchingControlName]; // return null if controls haven't initialised yet if (!control || !matchingControl) { return null; } // return null if another validator has already found an error on the matchingControl if (matchingControl.errors && !matchingControl.errors.mustMatch) { return null; } // set error on matchingControl if validation fails if (control.value !== matchingControl.value) { matchingControl.setErrors({ mustMatch: true }); } else { matchingControl.setErrors(null); } } }
4) Template-Driven Forms Custom "Must Match" Directive The custom [mustMatch] directive wraps the custom MustMatch validator so we can attach it to the form. A custom validator directive is required when using template-driven forms because we don't have direct access to the FormGroup like in reactive forms. The directive implements the Validator interface and registers itself with the NG_VALIDATORS provider to let angular know that it's a custom validator directive. It accepts an array with the names of 2 form controls that must match for form validation to pass, e.g. [mustMatch]="['field1', 'field2']" will validate that field1 and field2 contain the same value, otherwise a validation error will be set on field2. You can see it's usage in the form tag of the app template above.
import { Directive, Input } from '@angular/core'; import { NG_VALIDATORS, Validator, ValidationErrors, FormGroup } from '@angular/forms'; import { MustMatch } from './must-match.validator'; @Directive({ selector: '[mustMatch]', providers: [{ provide: NG_VALIDATORS, useExisting: MustMatchDirective, multi: true }] }) export class MustMatchDirective implements Validator { @Input('mustMatch') mustMatch: string[] = []; validate(formGroup: FormGroup): ValidationErrors { return MustMatch(this.mustMatch[0], this.mustMatch[1])(formGroup); } }
5) Template-Driven Forms Validation App Module The main thing we need to remember for using template-driven forms in Angular is to import the FormsModule from '@angular/forms' and include it in the imports array of the @NgModule decorator. Also import the custom validation directive { MustMatchDirective } from './_helpers/must-match.directive' and include it in the declarations array of the @NgModule decorator.
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { MustMatchDirective } from './_helpers/must-match.directive' @NgModule({ imports: [ BrowserModule, FormsModule ], declarations: [ AppComponent, MustMatchDirective ], bootstrap: [AppComponent] }) export class AppModule { }