StatusChanges In Angular Forms
In this article, We will learn about the how to use StatusChanges in Angular Forms. Angular forms raise the StatusChanges event anytime the validation status of the FormControl, FormGroup, or FormArray is calculated. You can subscribe to it because it returns an observable. The observable receives the control's most recent status. Every time a change is made to the control, Angular performs a validation check. It also produces a list of validation problems, after which the status is changed to INVALID. If there are no mistakes, the status is changed to VALID.
How to use StatusChanges
Three building blocks make up the Angular Forms. FormControl, FormGroup, and FormArray are three types of forms. The AbstractControl base class is extended by all of these controllers. The StatusChanges event is implemented by the AbstractControl base class.
We can subscribe to StatusChanges by obtaining the control's reference and doing so as shown below.
this.reactiveForm.get("firstname").statusChanges.subscribe(newStaus => { console.log('firstname status changed') console.log(newStaus) })
You can also sign up for the top-level form, which is displayed below.
this.reactiveForm.statusChanges.subscribe(newStaus => { console.log('form Status changed event') console.log(newStaus) })
StatusChanges Example
reactiveForm = new FormGroup({ firstname: new FormControl('', [Validators.required]), lastname: new FormControl(), address: new FormGroup({ city: new FormControl(), street: new FormControl(), pincode: new FormControl() }) })
StatusChanges of FormControl
this.reactiveForm.get("firstname").statusChanges.subscribe(newStatus => { console.log('firstname status changed') console.log(newStatus) //latest status console.log(this.reactiveForm.get("firstname").status) //latest status })
this.reactiveForm.get("firstname").statusChanges.subscribe(newStatus=> { console.log('firstname status changed') console.log(newStatus) //latest status console.log(this.reactiveForm.get("firstname").status) //latest status console.log(this.reactiveForm.status) //Previous status })
You can get around this by using setTimeout to wait for the next tick, as illustrated below.
this.reactiveForm.get("firstname").statusChanges.subscribe(newStatus=> { console.log('firstname status changed') console.log(newStatus) //latest status console.log(this.reactiveForm.get("firstname").status) //latest status console.log(this.reactiveForm.status) //Previous status setTimeout(() => { console.log(this.reactiveForm.status) //latest status }) })
StatusChanges of FormGroup
this.reactiveForm.get("address").statusChanges.subscribe(newStaus => { console.log('address status changed') console.log(newStaus) })
StatusChanges of Form
this.reactiveForm.statusChanges.subscribe(newStaus => { console.log('form status changed') console.log(newStaus) })
emitEvent & StatusChanges
this.reactiveForm.get("firstname").setValue("", { emitEvent: false });
onlySelf & StatusChanges
this.reactiveForm.get("firstname").setValue("", { onlySelf: true });
import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl, Validators } from '@angular/forms' import { timeout } from 'q'; @Component({ templateUrl: './reactive.component.html', }) export class ReactiveComponent implements OnInit { title = 'Reactive Forms'; reactiveForm = new FormGroup({ firstname: new FormControl('', [Validators.required]), lastname: new FormControl(), address: new FormGroup({ city: new FormControl(), street: new FormControl(), pincode: new FormControl() }) }) onSubmit() { //console.log(this.reactiveForm.value); } ngOnInit() { this.reactiveForm.get("firstname").statusChanges.subscribe(newStatus=> { console.log('firstname status changed') console.log(newStatus) console.log(this.reactiveForm.get("firstname").status) console.log(this.reactiveForm.status) setTimeout(() => { console.log(this.reactiveForm.status) }) }) this.reactiveForm.get("address").statusChanges.subscribe(newStatus=> { console.log('address status changed') console.log(newStatus) }) this.reactiveForm.statusChanges.subscribe(newStatus=> { console.log('form status changed') console.log(newStatus) }) } setValue() { let contact = { firstname: "Rahul", lastname: "Dravid", address: { city: "Bangalore", street: "Brigade Road", pincode: "600070" } }; this.reactiveForm.setValue(contact); } setAddress() { this.reactiveForm.get("address").setValue( { city: "Bangalore", street: "Brigade Road", pincode: "600070" } ); } setFirstname() { this.reactiveForm.get("firstname").setValue("Saurav") } withoutOnlySelf() { this.reactiveForm.get("firstname").setValue(""); } withOnlySelf() { this.reactiveForm.get("firstname").setValue("", { onlySelf: true }); } withEmitEvent() { this.reactiveForm.get("firstname").setValue("Sachin"); } withoutEmitEvent() { this.reactiveForm.get("firstname").setValue("", { emitEvent: false }); } reset() { this.reactiveForm.reset(); } }
The following code is complete code of the reactive.component.html
<h3>{{title}}</h3> <div style="float: left; width:50%;"> <form [formGroup]="reactiveForm" (ngSubmit)="onSubmit()" novalidate> <p> <label for="firstname">First Name </label> <input type="text" id="firstname" name="firstname" formControlName="firstname"> <label for="lastname">Last Name </label> <input type="text" id="lastname" name="lastname" formControlName="lastname"> </p> <div formGroupName="address"> <p> <label for="city">City</label> <input type="text" class="form-control" name="city" formControlName="city"> <label for="street">Street</label> <input type="text" class="form-control" name="street" formControlName="street"> <label for="pincode">Pin Code</label> <input type="text" class="form-control" name="pincode" formControlName="pincode"> </p> </div> <button>Submit</button> </form> <div> <button type="button" (click)="setValue()">SetValue</button> <button type="button" (click)="setAddress()">Address</button> <button type="button" (click)="setFirstname()">First Name</button> </div> <div> <button type="button" (click)="withoutOnlySelf()">Without Only Self</button> <button type="button" (click)="withOnlySelf()">With Only Self</button> </div> <div> <button type="button" (click)="withouEmitEvent()">Without EmitEvent</button> <button type="button" (click)="withEmitEvent()">With EmitEvent</button> </div> </div> <div style="float: right; width:50%;"> <h3>Form Status</h3> <b>status : </b>{{reactiveForm.status}} <b>valid : </b>{{reactiveForm.valid}} <b>invalid : </b>{{reactiveForm.invalid}} <b>touched : </b>{{reactiveForm.touched}} <b>untouched : </b>{{reactiveForm.untouched}} <b>pristine : </b>{{reactiveForm.pristine}} <b>dirty : </b>{{reactiveForm.dirty}} <b>disabled : </b>{{reactiveForm.disabled}} <b>enabled : </b>{{reactiveForm.enabled}} <h3>Form Value</h3> {{reactiveForm.value |json}} </div>
The following code is complete code of the app.component.html
<h3>Angular StatusChanges Example</h3> <ul> <li> <a [routerLink]="['/template']" routerLinkActive="router-link-active" >Template</a> </li> <li> <a [routerLink]="['/reactive']" routerLinkActive="router-link-active" >Reactive</a> </li> </ul> <router-outlet></router-outlet>
The following code is complete code of the app.component.ts
import { Component} from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { } The following code is complete code of the app.module.ts import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { TemplateComponent } from './template-component'; import { ReactiveComponent } from './reactive.component'; @NgModule({ declarations: [ AppComponent,TemplateComponent,ReactiveComponent ], imports: [ BrowserModule, AppRoutingModule, FormsModule, ReactiveFormsModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
StatusChanges in Template Driven Forms
@ViewChild('templateForm',null) templateForm: NgForm;
import { Component, ViewChild, ElementRef, OnInit, OnDestroy } from '@angular/core'; import { NgForm } from '@angular/forms'; @Component({ templateUrl: './template.component.html', }) export class TemplateComponent implements OnInit { title = 'Template driven forms'; @ViewChild('templateForm',null) templateForm: NgForm; contact: contact; onSubmit() { console.log(this.templateForm.value); } ngOnInit() { setTimeout(() => { this.templateForm.control.get("firstname").statusChanges.subscribe(newStatus=> { console.log('firstname status changed') console.log(newStatus) console.log(this.templateForm.control.get("firstname").status) console.log(this.templateForm.control.status) setTimeout(() => { console.log(this.templateForm.control.status) }) }) this.templateForm.control.get("address").statusChanges.subscribe(newStatus => { console.log('address status changed') console.log(newStatus) }) this.templateForm.control.statusChanges.subscribe(newStatus=> { console.log('form status changed') console.log(newStatus) }) }); } setValue() { let contact = { firstname: "Rahul", lastname: "Dravid", address: { city: "Bangalore", street: "Brigade Road", pincode: "600070" } }; this.templateForm.setValue(contact); } setAddress() { let address= { city: "Bangalore", street: "Brigade Road", pincode: "600070" }; this.templateForm.control.get("address").setValue(address); }; setFirstname() { this.templateForm.control.get("firstname").setValue("Saurav") } withoutOnlySelf() { this.templateForm.control.get("firstname").setValue(""); } withOnlySelf() { this.templateForm.control.get("firstname").setValue("", { onlySelf: true }); } withouEmitEvent() { this.templateForm.control.get("firstname").setValue("Sachin"); } withEmitEvent() { this.templateForm.control.get("firstname").setValue("", { emitEvent: false }); } reset() { this.templateForm.reset(); } } export class contact { firstname:string; lastname:string; gender:string; email:string; isMarried:boolean; country:string; address: { city:string; street:string; pincode:string; } }
Now, add the following code into the template-component.html
<h3>{{title}}</h3> <div style="float: left; width:50%;"> <form #templateForm="ngForm" (ngSubmit)="onSubmit(templateForm)"> <p> <label for="firstname">First Name </label> <input type="text" id="firstname" name="firstname" #fname="ngModel" ngModel> </p> <p> <label for="lastname">Last Name </label> <input type="text" id="lastname" name="lastname" ngModel> </p> <div ngModelGroup="address"> <p> <label for="city">City</label> <input type="text" id="city" name="city" ngModel> <label for="street">Street</label> <input type="text" id="street" name="street" ngModel> <label for="pincode">Pin Code</label> <input type="text" id="pincode" name="pincode" ngModel> </p> </div> <button>Submit</button> </form> <div> <button type="button" (click)="setValue()">SetValue</button> <button type="button" (click)="setAddress()">Address</button> <button type="button" (click)="setFirstname()">First Name</button> </div> <div> <button type="button" (click)="withoutOnlySelf()">Without Only Self</button> <button type="button" (click)="withOnlySelf()">With Only Self</button> </div> <div> <button type="button" (click)="withouEmitEvent()">Without EmitEvent</button> <button type="button" (click)="withEmitEvent()">With EmitEvent</button> </div> </div> <div style="float: right; width:50%;"> <h3>Form Status</h3> <b>status : </b>{{templateForm.status}} <b>valid : </b>{{templateForm.valid}} <b>invalid : </b>{{templateForm.invalid}} <b>touched : </b>{{templateForm.touched}} <b>untouched : </b>{{templateForm.untouched}} <b>pristine : </b>{{templateForm.pristine}} <b>dirty : </b>{{templateForm.dirty}} <b>disabled : </b>{{templateForm.disabled}} <b>enabled : </b>{{templateForm.enabled}} <h3>Form Value</h3> {{templateForm.value | json }} </div>