StatusChanges In Angular Forms

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. FormControlFormGroup, 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

Make a reactive form like the one below.
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

As illustrated below, you can subscribe to a single FormControl StatusChanges. We will obtain the most recent status of the firstname in the newStatus variable. You may also use this to get the most recent status of the firstname using the this.reactiveForm.get("firstname").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
})

However, the top-level form has not yet been modified, resulting in this. The former state of the firstname, as well as the form, is still visible in reactiveForm.status.

The firstname statusChanges event triggers immediately after the new status is updated, but before the change is passed on to its parent.

As a result, this.reactiveForm.status still displays the previous state.
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

When the status of any of FormGroup or FormArray child controls is calculated, the StatusChanges event is dispatched. The following StatusChanges, for example, will trigger whenever the status of the city, state, or pincode is calculated.
this.reactiveForm.get("address").statusChanges.subscribe(newStaus => {
  console.log('address status changed')
  console.log(newStaus)
})

StatusChanges of Form

The following example demonstrates how we can subscribe to the complete form's updates.
this.reactiveForm.statusChanges.subscribe(newStaus => {
  console.log('form status changed')
  console.log(newStaus)
})

emitEvent & StatusChanges

Even whether angular calculates the state of the control via UI or programmatically, the statusChanges event is fired. You might not want to raise the statusChanges event in some cases. To accomplish this, we can use the emitEvent:false command.

Even when the value of the firstname is altered, making it and the form INVALID, the statusChanges event is not fired in the following example.
this.reactiveForm.get("firstname").setValue("", { emitEvent: false });

With the setValue, patchValue, markAsPending, disable, enable, updateValueAndValidity, and setErrors methods, you can use emitEvent:false.

onlySelf & StatusChanges

Only this FormControl will be affected when onlySelf:true, and the modification will not be propagated to its parent. As a result, the parent FormGroup StatusChanges event does not trigger.

For example, the following code will return the firstname StatusChanges. but, not of its parent (i.e. top-level form)
this.reactiveForm.get("firstname").setValue("", { onlySelf: true });

With the setValue, patchValue, markAsUntouched, markAsDirty, markAsPristine, markAsPending, disable, enable, and updateValueAndValidity methods, you can use the onlySelf:true parameter.

The following code is complete code of the reactive.component.ts
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

In template-driven forms, the StatusChanges event can also be used. All you have to do is get the Form Model reference from the component, as seen below.
@ViewChild('templateForm',null) templateForm: NgForm;

You can use the example code below as a guide.

Open, the template-component.ts file and add the following code:
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>

Conclusion

 In this article, We covered how to use StatusChanges in Angular Forms. When the angular calculates the validity state of the FormControlFormGroup, or FormArray, the StatusChanges event is called. We can subscribe to it because it is an observable. When modifying the value and validity of form controls, the StatusChanges event does not fire regardless of how emitEvent or onlySelf is set.

I hope this article helps you and you will like it.👍

If you have any doubt or confusion then free to ask in comment section.

Post a Comment

Previous Post Next Post