a blog for those who code

Thursday 28 March 2019

Understanding Reactive and Template-driven Forms in Angular


Getting user input with forms are essential part for any application where you get user inputs to login, entering data etc. There are mainly two types of forms in Angular : Reactive and Template Driven.

The Difference between Reactive Forms and Template Driven


Template Driven is mainly used for simple scenarios and are very easy to use. Using Template Driven forms you can have Event Binding, Property Binding as well as Two Way Binding and thus you will have minimal component code.

Reactive Forms on the other hand is much more flexible than template-driven and it is used to handle complex scenarios. The data binding is not done and have less HTML markup code and more component code.

Do note that if you are writing unit tests then you should use Reactive Forms as its easier to write unit tests for Reactive forms.

Both forms use a form model to track value changes between Angular forms and form input elements, but in Reactive Forms the form model provides value and status of the form element whereas for the Template Driven forms, the value and status of the form element is provided by the template itself.

Reactive Forms in Angular


As discussed earlier, Reactive Forms are model-driven forms where we will use the underlying Angular APIs to create our own instances of FormControl, FormGroup inside our component class. Lets go through some of the building blocks of Reactive Forms and they are FormControl, FormGroup and FormArray.

Note: To use Reactive Forms you need to import ReactiveFormModule class in app.module.

FormControl : It is the basic building block like a input in HTML form. To register a single form control we need to create a new instance of the form control to save it as a class property. It tracks the value and validity status of an angular form control.

//New Instance of Form Control which is required
this.name = new FormControl('', Validators.required);
//Registering the control in the template
<input type="text" [formControl]="name" >
// Getting the FormControl Value
{{ name.value }} // using string interpolation
// Setting FormControl Value
this.name.setValue('Initial Value');


FormGroup : FormControl gives you control over a single input field, a form group keeps tracks of group of form control. Each form control in a group is tracked by name.

//New Instance of Form Group
this.myForm = new FormGroup({
  'name': new FormControl('', Validators.required),
  'age': new FormControl('')
});
//Registering the form group in the template
<form [formGroup]="myForm">
  Name: <input type="text" formControlName="name">
  Age: <input type="text" formControlName="age">
</form>
// Getting the Individual FormControl Value inside the FormGroup
this.myForm.get('name').value
// Setting FormControl Value
myForm.setValue({name: 'Just Name', age: '10'});

FormArray : It is just a variation of FormGroup. In FormArray the data gets serialized as an array whereas in FormGroup it is serialized as object. FormArray is useful when you don't know before hand that how many controls you need.

//New Instance of Form Array
this.myForm = new FormGroup({
  'name': new FormControl('', Validators.required),
  'age': new FormControl(''),
  'skills': new FormArray([])
});
//Registering the form array in the template
<div formArrayName="skills">
  <div *ngFor="let mySkill of myForm.get('skills').controls; let i = index">
    <input type="text" [formControlName]="i">
  </div>
</div>
// Getting the FormArray control
const mySkills = this.myForm.get('skills') as FormArray
// Adding a new Item to FormArray
(<FormArray>this.myForm.get('skills')).push(new FormControl(''));

Thus looking at the FormControl, FormGroup and FormArray we have eventually covered the Reactive Forms, thus you can see how simple is to create a Reactive Forms in Angular. One question you might ask is what if we want to update only one control in a FormGroup and not all the FormControl. This is where PatchValue comes into picture.

Understanding Observable - A key Concept in Angular


Difference Between SetValue and PatchValue


SetValue and PatchValue are methods in Angular FormGroup, they both set the value of a control in a formgroup but the difference lies in that in setValue you need to set the values of all the controls whereas using the patchValue you can set the values of the controls you need.

So when we have used setValue in the above code we have set the values of both the controls :

myForm.setValue({name: 'Just Name', age: '10'});

So if we want to update only the name we will use patchValue as shown below

myForm.patchValue({name: 'Just Name'});

Validation in Reactive Forms


In reactive forms we add validation in component class and directly to the form control. The validators are of two types sync and async validators.

Sync Validators: It immediately returns a set of validation error or null.
Async Validators: It returns a Promise or Observable that returns a set of validation error or null.

Adding Validators to Form Control is relatively easy and you can use Built-in validators when creating a new instance of FormControl as shown below :

this.name = new FormControl('', [Validators.required, Validators.minLength(10));

Custom Validators


Let's say in your app only users who are above 15 can Login, so we can write a simple validator which checks the age. We will write a function which takes the parameter as a FormControl.

If the validation fails, it returns an object which contains a key-value pair where key is the name of the error and value is true. If the validation does not fail it will return null.

ageValidator(control: FormControl): {[errorName: string]: boolean} | null {
  if(control.value < 15) {
    return{'ageLessThan15': true};
  }
  return null;
}

And you can ass your custom validator as shown below

this.myForm = new FormGroup({
  'name': new FormControl('', Validators.required),
  'age': new FormControl('', [ageValidator])
 });

Template Driven Forms in Angular


We have gone through the Reactive Forms and its time to learn how Template-driven forms works. In Template Driven forms the forms is created with a component and template where we will use ngModel to create two-way bindings for reading and writing input-control values.

Note : To use Template-driven forms you need to import FormsModule class in app.module.

Custom Bindings using Input and Output Properties in Angular 7


Lets have a simple Login Form written in HTML5 where you have username and password inputs and a Submit button to Login. Both the inputs have HTML5 required attribute. We have also used ngModel for two-way data binding. We have also added a variable loginForm which is a reference to the NgForm directive and it is been added by Angular automatically.

<form #loginForm="ngForm" id="LoginForm">
  <div class="input-group">
    <div class="form-line">            
      <input [(ngModel)]="model.userName"
             class="form-control"
             type="text"
             name="userName"
             required />            
    </div>
    <div class="form-line">                 
      <input [(ngModel)]="model.password"
             class="form-control"
             type="text"
             name="password"
             required />            
    </div>
    <div class="col-xs-4">              
      <button id="LoginButton"
              type="submit">Login</button>            
    </div>      
  </div>
</form>

What happens internally is that whenever Angular sees a <form> element it attaches NgForm directive to the <form> tag. Also Angular creates FormControl instances and registers them with an NgForm directive created by Angular. Each FormControl is registered under the name which we have assigned to the name attribute of the input element.

One more important think about Forms is that you can check the state and validity of your control using Angular CSS classes that reflect the state and validity.

ng-touched Class gets added if the control has been visited.
ng-dirty Class gets added if the control's value has been changed.
ng-invalid Class gets added of the control's value is invalid.

Validation in Template-driven Forms


To add validation to a template-driven form, you will be adding the same validation attributes just like native HTML form validation like required, minlength, maxlength,  pattern etc. When you add the validation, check for ng-valid class to see if the form is valid.

Note: Do check ng-touched or ng-dirty class before checking for invalid attributes because you do not want to display errors if the user has not edited the form.

With this we now have basic understanding of both the forms and how to use them in our next application.

Career Category (English)728x90

No comments:

Post a Comment