ValueChanges In Angular Forms

ValueChanges In Angular Forms

In this article, We will learn about the how to use ValueChanges in Angular Forms. When the value of the FormControl, FormGroup, or FormArray changes, the ValueChanges event is raised by the Angular forms. You can subscribe to it because it returns an observable. The control's most recent value is passed to the observable. It allows us to track and respond to changes in the value in real time. It can be used to validate a value, calculate calculated fields, and so on.

How to use ValueChanges

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 ValueChanges event is implemented by the AbstractControl base class.

We can subscribe to ValueChanges by obtaining the control's reference and doing so as indicated below.

this.reactiveForm.get("firstname").valueChanges.subscribe(x => {
   console.log('firstname value changed')
   console.log(x)
})

You can also sign up for the top-level form, which is displayed below.
this.reactiveForm.valueChanges.subscribe(x => {
    console.log('form value changed')
    console.log(x)
})

ValueChanges 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()
  })
})

ValueChanges of FormControl

As demonstrated below, you can subscribe to ValueChanges of a single FormControl. We'll get the most recent value of the firstname in the selectedValue field. You may also use this to get the most recent value of the firstname using the this.reactiveForm.get("firstname").value
this.reactiveForm.get("firstname").valueChanges.subscribe(selectedValue => {
  console.log('firstname value changed')
  console.log(selectedValue)                              //latest value of firstname
  console.log(this.reactiveForm.get("firstname").value)   //latest value of firstname
})

ValueChanges shows previous value

However, the top-level form has not yet been modified, resulting in this. The old value of the firstname is still displayed in this.reactiveForm.value.

The valueChanges event for the firstname fires right after the new value is modified, but before the change is passed on to its parent.

As a result, the value of this.reactiveForm.value remains unchanged.
this.reactiveForm.get("firstname").valueChanges.subscribe(selectedValue => {
  console.log('firstname value changed')
  console.log(selectedValue)
  console.log(this.reactiveForm.get("firstname").value)
  console.log(this.reactiveForm.value)   //still shows the old first name
})

You can get around this by using setTimeout to wait for the next tick, as illustrated below.
this.reactiveForm.get("firstname").valueChanges.subscribe(selectedValue => {
  console.log('firstname value changed')
  console.log(selectedValue)
  console.log(this.reactiveForm.get("firstname").value)
  console.log(this.reactiveForm.value)    //shows the old first name
      
  setTimeout(() => {
    console.log(this.reactiveForm.value)   //shows the latest first name
  })
     
})

ValueChanges of FormGroup

When the value of any of FormGroup or FormArray child controls changes, the ValueChanges event is triggered. The following ValueChanges, for example, will fire if the value of the city, state, or pincode changes.
this.reactiveForm.get("address").valueChanges.subscribe(selectedValue  => {
  console.log('address changed')
  console.log(selectedValue)
})

ValueChanges of Form

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

EmitEvent & ValueChanges

Even when the control's values are modified programmatically, the ValueChanges event is fired. You might not want to raise the ValueChanges event in some cases. To accomplish this, we can use the emitEvent:false command.

Even when the value of the firstname is updated in the following example, the ValueChanges event is not dispatched.
this.reactiveForm.get("firstname").setValue("", { emitEvent: false });

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

OnlySelf & ValueChanges

When onlySelf:true, changes will only effect this FormControl and will not be propagated to its parent. As a result, the parent FormGroup ValueChanges event does not trigger.

For example, the following code will change the firstname ValueChanges. 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").valueChanges.subscribe(selectedValue => {
      console.log('firstname value changed')
      console.log(selectedValue)
      console.log(this.reactiveForm.get("firstname").value)
      console.log(this.reactiveForm.value)
      
      setTimeout(() => {
        console.log(this.reactiveForm.value)
      })
      
    })
 
    this.reactiveForm.get("address").valueChanges.subscribe(selectedValue => {
      console.log('address changed')
      console.log(selectedValue)
    })
 
    this.reactiveForm.valueChanges.subscribe(selectedValue => {
      console.log('form value changed')
      console.log(selectedValue)
    })
  }
 
 
 
  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>
    <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>
 
  </form>
</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 ValueChanges 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 { }

ValueChanges in Template Driven Forms

In template-driven forms, the ValueChanges 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.

Add the following code into the template-component.ts file.
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").valueChanges.subscribe(selectedValue => {
        console.log('firstname value changed')
        console.log(selectedValue)
        console.log(this.templateForm.control.get("firstname").value)
        console.log(this.templateForm.control.value)
        
        setTimeout(() => {
          console.log(this.templateForm.control.value)
        })
      })
  
      this.templateForm.control.get("address").valueChanges.subscribe(selectedValue => {
        console.log('address changed')
        console.log(selectedValue)
      })
 
      this.templateForm.valueChanges.subscribe(selectedValue => {
        console.log('form value changed')
        console.log(selectedValue)
      })      
      
    });
 
  }
 
 
 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, put the below code under the template-component.html file
<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>
    <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>
    
  </form>
</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 ValueChanges in Angular Forms. When the value of the FormControlFormGroup, or FormArray changes, the ValueChanges event is triggered. We can subscribe to it because it is an observable. After that, we will be able to use it to validate the forms. recalculate the computed fields, and so on. When modifying the value and validity of form controls, the ValueChanges 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