5

I'd like to listen to valuechanges of my form, but not for the entire form but only for the formcontrol that was changed.

If for example my form looks like this.

this.form = this._fb.group({
  firstName: [''],
  lastName: [''],
  ... // other stuff.
});

If I then subscribe to valuechanges

this.form.valueChanges.subscribe((e) => {
  console.log(e);
});

Then filling in a firstname in the form would result in a printout of the entire form value object.

{firstName: 'input', lastName: '', ...}

But what I want to know is which form control (in this case the firstName) was altered without subscribing to each individual form control. Such that my desired output is only

{firstName: 'input'}

  • 1
    Does this answer your question? [Angular 2 Reactive Forms only get the value from the changed control](https://stackoverflow.com/questions/49296549/angular-2-reactive-forms-only-get-the-value-from-the-changed-control) – Akirus Jul 16 '21 at 07:43

3 Answers3

4

Firstly, multiple AbstractControls(e.g FormControl, FormGroup, FormArray) are stored internally as a tree.

// FG - FormGroup
// FA - FormArray
// FC - FormControl

    FG
  /   \
FC    FG
    /    \
  FC     FA
        / | \
      FC FC FC

The above snippet is excerpted from A thorough exploration of Angular Forms.

Because of that reason, a FormGroup.valueChanges emits when a FormGroup child's valueChanges emits:

updateValueAndValidity (/* ... */) {
  /* ... */

  if (opts.emitEvent !== false) {
    (this.valueChanges as EventEmitter<any>).emit(this.value);
    (this.statusChanges as EventEmitter<string>).emit(this.status);
  }
  
  if (this._parent && !opts.onlySelf) {
    this._parent.updateValueAndValidity(opts);
  }
  
  /* ... */
}

In the above snippet, if we were to consider your example, _parent could refer to the this.form FormGroup instance when the firstName FormControl receives some input.


So, a way to achieve what you're looking for would be this:

merge(
  ...Object.keys(this.form.controls).map(
    k => this.form.controls[k].valueChanges.pipe(
      // Getting a decent format: `{ formControlName: formControlValue }`
      map(v => ({ [k]: v })),
    )
  )
).subscribe(console.log)

The FormGroup.controls has this signature:

public controls: {[key: string]: AbstractControl},
Andrei Gătej
  • 11,116
  • 1
  • 14
  • 31
1

You can try something like this

  this.form.get("firstName").valueChanges.subscribe(selectedValue => {
      console.log(selectedValue)     
    })
     
  • No, I explicitly said that I did not want to subscribe to each individual form control. In this generic example, there are only 2 hardcoded controls but in my real case scenario there are many more controls which names are not known beforehand but can be dynamic. – Wouter Vandenputte Jul 16 '21 at 07:40
  • 2
    Yes. Got your point . Please follow this link - https://stackoverflow.com/questions/56729248/how-to-get-formcontrolname-of-the-field-which-value-changed-in-angular-reactive – Souvanik Dev Jul 16 '21 at 07:51
0

There are multiple ways to listen form control changes:

custom validation method:

this.form = this._fb.group({
  firstName: ['', [this.fieldValidator]],
  lastName: [''],
  ... // other stuff.
});

fieldValidator(control: FormControl) {
     // control?.value available here
     // return null
}

or cross-validation method:

this.form = this._fb.group({
  firstName: [''],
  lastName: [''],
  ... // other stuff.
}, { validators: exampleValidator});

export const exampleValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
  const firstName = control.get('firstName');
  // firstName?.value available here, etc
  // return condition ? null : { inValid: true};
};