2

I have a dynamically created form with all inputs. I am subscribing for changes, but when a control is changed I get the values from all the controls, so I don't really know which one is changed. Is it possible to get the changed value only from the changed control with the valueChanges function?

The form is pretty big, so subscribing every control to valueChanges is not an option.

The function currently looks like this:

checkForValueChanges() {
    this.metadataForm.valueChanges.subscribe(controls => {
        // how to get the changed control form name here ?
    });
}

Since the project is really big, I just made a simple example to show my issue: StackBlitz example You can see in console that the result I get is all of the controls instead of just the one it's changed.

Boanta Ionut
  • 402
  • 8
  • 24
  • Could you make an example on StackBlitz ? –  Mar 15 '18 at 10:09
  • I'am afraid the only way is save the oldvalues in a variable and compare the two objects, (the oldValues and values -the data you received when subscribe): valueChanges.subscribe(values=>{Compare(oldValues,values}) – Eliseo Mar 15 '18 at 10:44
  • I thoought so, but I really hoped for a better solution since I have to update some local variables based on the control that is changed. – Boanta Ionut Mar 15 '18 at 10:48

2 Answers2

5

The cleanest way I can imagine is to subscribe dynamicaly to all the form controls :

const subscriptions = [];
for (const key of Object.keys(this.metadataForm.controls)) {

    const subscription = this.metadataForm.controls[key].valueChanges
    .subscribe(value => console.log('value :' + value[key] + ' and key : ' + key));

    subscriptions.push(subscription);
}

I added an array of subscriptions to handle the unsubscribe on destroy.

ibenjelloun
  • 7,425
  • 2
  • 29
  • 53
  • 1
    Thank you for the time spent. However, since the form has a huge amount of fields, all inputs (the form is to edit some metadata which is really big), I think that I will have a pretty significant performance drop. The component is supposed to be light since it is part of a way bigger module which already has a lot of components doing other hard works. If it is not possible with just the form control subscription I will find another way to do my operations. – Boanta Ionut Mar 15 '18 at 11:10
4
this.imagSub = this.imagingForm.valueChanges.pipe(
    pairwise(),
    map(([oldState, newState]) => {
      let changes = {};
      for (const key in newState) {
        if (oldState[key] !== newState[key] && 
            oldState[key] !== undefined) {
          changes[key] = newState[key];
        }
      }
      return changes;
    }),
    filter(changes => Object.keys(changes).length !== 0 && !this.imagingForm.invalid)
  ).subscribe(
    value => {
      console.log("Form has changed:", value);
    }
  );

That subscription became active on ngOnInit and download some data from API (and after that put them into a form field, so I don't need first value [and value == undefined], because of form setting fields by herself)

For me, that solution works fine. But I guess if you have a form with "that has a huge amount of fields", this method will be even worse than saving subscriptions.