Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to watch for form changes in Angular

Tags:

angular

People also ask

What is valueChanges in Angular?

The ValueChanges is an event raised by the Angular forms whenever the value of the FormControl, FormGroup or FormArray changes. It returns an observable so that you can subscribe to it. The observable gets the latest value of the control. It allows us to track changes made to the value in real-time and respond to it.

Which class is applied on a form control if its value is changed in Angular?

ng-touched will be applied if the condition is true and ng-untouched will be applied if false. This pair of classes defines the state of the control whether its value has been changed or not. ng-dirty will be applied if the condition is true and ng-pristine will be applied if false.

What is NgForm in Angular?

NgForm is used to create a top-level form group Instance, and it binds the form to the given form value.


UPD. The answer and demo are updated to align with latest Angular.


You can subscribe to entire form changes due to the fact that FormGroup representing a form provides valueChanges property which is an Observerable instance:

this.form.valueChanges.subscribe(data => console.log('Form changes', data));

In this case you would need to construct form manually using FormBuilder. Something like this:

export class App {
  constructor(private formBuilder: FormBuilder) {
    this.form = formBuilder.group({
      firstName: 'Thomas',
      lastName: 'Mann'
    })

    this.form.valueChanges.subscribe(data => {
      console.log('Form changes', data)
      this.output = data
    })
  }
}

Check out valueChanges in action in this demo: http://plnkr.co/edit/xOz5xaQyMlRzSrgtt7Wn?p=preview


If you are using FormBuilder, see @dfsq's answer.

If you are not using FormBuilder, there are two ways to be notified of changes.

Method 1

As discussed in the comments on the question, use an event binding on each input element. Add to your template:

<input type="text" class="form-control" required [ngModel]="model.first_name"
         (ngModelChange)="doSomething($event)">

Then in your component:

doSomething(newValue) {
  model.first_name = newValue;
  console.log(newValue)
}

The Forms page has some additional information about ngModel that is relevant here:

The ngModelChange is not an <input> element event. It is actually an event property of the NgModel directive. When Angular sees a binding target in the form [(x)], it expects the x directive to have an x input property and an xChange output property.

The other oddity is the template expression, model.name = $event. We're used to seeing an $event object coming from a DOM event. The ngModelChange property doesn't produce a DOM event; it's an Angular EventEmitter property that returns the input box value when it fires..

We almost always prefer [(ngModel)]. We might split the binding if we had to do something special in the event handling such as debounce or throttle the key strokes.

In your case, I suppose you want to do something special.

Method 2

Define a local template variable and set it to ngForm.
Use ngControl on the input elements.
Get a reference to the form's NgForm directive using @ViewChild, then subscribe to the NgForm's ControlGroup for changes:

<form #myForm="ngForm" (ngSubmit)="onSubmit()">
  ....
  <input type="text" ngControl="firstName" class="form-control" 
   required [(ngModel)]="model.first_name">
  ...
  <input type="text" ngControl="lastName" class="form-control" 
   required [(ngModel)]="model.last_name">

class MyForm {
  @ViewChild('myForm') form;
  ...
  ngAfterViewInit() {
    console.log(this.form)
    this.form.control.valueChanges
      .subscribe(values => this.doSomething(values));
  }
  doSomething(values) {
    console.log(values);
  }
}

plunker

For more information on Method 2, see Savkin's video.

See also @Thierry's answer for more information on what you can do with the valueChanges observable (such as debouncing/waiting a bit before processing changes).


To complete a bit more previous great answers, you need to be aware that forms leverage observables to detect and handle value changes. It's something really important and powerful. Both Mark and dfsq described this aspect in their answers.

Observables allow not only to use the subscribe method (something similar to the then method of promises in Angular 1). You can go further if needed to implement some processing chains for updated data in forms.

I mean you can specify at this level the debounce time with the debounceTime method. This allows you to wait for an amount of time before handling the change and correctly handle several inputs:

this.form.valueChanges
    .debounceTime(500)
    .subscribe(data => console.log('form changes', data));

You can also directly plug the processing you want to trigger (some asynchronous one for example) when values are updated. For example, if you want to handle a text value to filter a list based on an AJAX request, you can leverage the switchMap method:

this.textValue.valueChanges
    .debounceTime(500)
    .switchMap(data => this.httpService.getListValues(data))
    .subscribe(data => console.log('new list values', data));

You even go further by linking the returned observable directly to a property of your component:

this.list = this.textValue.valueChanges
    .debounceTime(500)
    .switchMap(data => this.httpService.getListValues(data))
    .subscribe(data => console.log('new list values', data));

and display it using the async pipe:

<ul>
  <li *ngFor="#elt of (list | async)">{{elt.name}}</li>
</ul>

Just to say that you need to think the way to handle forms differently in Angular2 (a much more powerful way ;-)).

Hope it helps you, Thierry


For angular 5+ version. Putting version helps as angular makes lot of changes.

ngOnInit() {

 this.myForm = formBuilder.group({
      firstName: 'Thomas',
      lastName: 'Mann'
    })
this.formControlValueChanged() // Note if you are doing an edit/fetching data from an observer this must be called only after your form is properly initialized otherwise you will get error.
}

formControlValueChanged(): void {       
        this.myForm.valueChanges.subscribe(value => {
            console.log('value changed', value)
        })
}