Monday, March 23, 2020

Angular Reactive Forms


Angular reactive forms, also known as model-driven forms, offers an easy way to use reactive patterns and validations. They follow the reactive programming style that supports an explicit data management flow between non-UI data models (frequently retrieved from a server) and a UI-oriented form model that keeps the states and values of HTML controls on the app screen.
When coding reactive forms, we will avoid directives like required, ngModel, NgForm and such. The idea is that we actually use the underlying APIs to do it for us. In a sense, instead binding Object models to directives like it happens in template-driven forms, we create our own instances inside a component class and build our very own JavaScript models. This approach has a lot more power and is extremely productive to work with since it allows us to write expressive code (a very testable one that keeps all the logic in the same place) instead of dividing it over different form templates.
With reactive forms, you will be able to create and manipulate form control objects directly in the Component. Since the component class has access to the form control structure and the data model, you can push data model values into the form controls as well as pull values that have been changed by the user. The component is able to observe changes in the form control state and react to them. This is specially useful for showing a validation message.
One of the advantages that working directly with form control objects brings you is that value and validity updates are always synchronous and under your control. You won’t find the timing issues that sometimes affect a template-driven form. Also, reactive forms tend to be easier to unit test.
Reactive Form Model Setup
Step-1: Create a Reactive Form Component: Command to create a Reactive Form component is:

          ng g c reactive-form --spec false

Step-2: import ReactiveFormsModule in app.module.ts file: As we know that Reactive forms make use of the "ReactiveFormsModule". So we include this in app.module.ts file as below:

import { ReactiveFormsModule } from '@angular/forms';

Step-3: Now write code for reactive-form.component.ts, But first, add FormControl and FormGroup module in ReactiveFormComponent.ts file as these are basic building blocks of Reactive Forms.

import { FormGroupFormControl } from '@angular/forms';

FormControl: it tracks the value and validity status of an angular form control. It matches to an HTML form control like an input.
FormGroup: it tracks the value and validity state of each input control. It aggregates the values of each child FormControl into one object, using the name of each form control as the key. 

Step-4: Code for reactive-form.component.ts

import { ComponentOnInit } from '@angular/core';
import { FormGroupFormControl } from '@angular/forms';

@Component({
  selector: 'app-reactive-form',
  templateUrl: './reactive-form.component.html',
  styleUrls: ['./reactive-form.component.css']
})
export class ReactiveFormComponent implements OnInit {

  courseFormFormGroup;  // create a property of type formgroup
  constructor() {}

  ngOnInit() {
    this.initForm();
  }
  onSubmit() {
      console.log(this.courseForm);
  }
  private initForm() {
    this.courseForm = new FormGroup({
      courseName: new FormControl(),
      courseDesc: new FormControl(),
      courseAmount: new FormControl()
    });
  }
}


Step-5: Now write following code in reactive-form.component.html

<div class="container">
  <h3 class="text-center text-danger">Reactive Form</h3>
  <br />
  <div class="row">
    <div class="col-xs-12">
      <form [formGroup]="courseForm" (ngSubmit)="onSubmit()">
        <div class="row">
          <div class="col-sm-4 form-group">
            <label for="courseName">Course Name</label>
            <input
              type="text"
              id="courseName"
              class="form-control"
              formControlName="courseName"
            />
          </div>
          <div class="col-sm-4 form-group">
            <label for="courseDesc">Course Description</label>
            <input
              type="text"
              id="courseDesc"
              class="form-control"
              formControlName="courseDesc"
            />
          </div>
          <div class="col-sm-4 form-group">
            <label for="courseAmount">Course Amount</label>
            <input
              type="number"
              id="courseAmount"
              class="form-control"
              formControlName="courseAmount"
            />
          </div>
        </div>
        <div class="row">
          <div class="col-xs-12">
            <button class="btn btn-success m-3" type="submit">Add</button>
          </div>
        </div>
      </form>
    </div>
  </div>
</div>
<br /><br />

<!-- Reactive Form Validation -->

<table border="1">
  <tr>
    <th style="padding: 10px">FormGroup</th>
    <th style="padding: 10px">FormControl(courseName)</th>
  </tr>
  <tr>
<!-- Form Validation -->
    <td style="padding: 10px">
      touched : {{ courseForm.touched }} <br />
      dirty : {{ courseForm.dirty }} <br />
      valid : {{ courseForm.valid }} <br />
      Form Values : {{ courseForm.value | json }}
    </td>
<!—Input Controls Validation -->
    <td style="padding: 10px">
      touched : {{ courseForm.get('courseName').touched }} <br />
      dirty : {{ courseForm.get('courseName').dirty }} <br />
      valid : {{ courseForm.get('courseName').valid }} <br />
      courseName Value : {{ courseForm.get('courseName').value }}
    </td>
  </tr>
</table>


Code Explanation:

      courseFormFormGroup;  // create a property of type formgroup

1. In .ts file First we create a variable of reactive form courseForm form FormGroup;
2. Then we create a private method private initForm() and call this in the ngOnInit() life cycle hook, so that it will be called as the component initialize.
3.   In private initForm() create an instance FormGorup as courseForm and use these in Template to create Form. Similarly we also create instances of FormControl as courseName, courseDesc and courseAmount respectively to create input for taking values from the user.
4.   In OnSubmit() function we are getting form values and send it to the console.
5.   In order to display form value in View it-self, we use local variable to display the values as json object using the command courseForm.value | json.

6.   In template we use formGroup property for form and formControlName as an attribute for create instance of input controls for collecting form values.

Code Output:



Reactive Form Validation

In reactive forms, instead of adding validators through attributes in the template (like it happens with template-driven forms), you add validator functions directly to the form control model in the angular component class. Angular will call these functions whenever the value of the control changes.
You can choose between writing your own validator functions and using some of the Angular built-in validators by importing Validaors module .
import { FormGroupFormControlValidators } from '@angular/forms';

Now update your code in reactive-form.component.ts file with following code:
private initForm() {
    this.courseForm = new FormGroup({
      courseName: new FormControl(''Validators.required),
      courseDesc: new FormControl(''Validators.required),
      courseAmount: new FormControl()
    });
  }
 Angular is constantly updating the state of both the form and the input fields.
To validate form use the code:

touched : {{ courseForm.touched }}     // Form-Name.touched
To validate form control use the code:

touched : {{ courseForm.get('courseName').touched }}
Note: get('courseName') is used to find the input control name.