最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to get FormControlName of the field which value changed in Angular reactive forms - Stack Overflow

programmeradmin3浏览0评论

I have a reactive form with over 10 form controls and using subscription on valueChanges observable to detect changes. It works perfectly but output is always the entire form value object(meaning all the form controls and their values). Is there a way to simply get the form control name of the field that changed?

this.form = this.fb.group({
    field1: ['', Validators.required],
    field2: ['', Validators.required],
    field3: ['', Validators.required],
    field4: ['', Validators.required],
    field5: ['', Validators.required],
    field6: ['', Validators.required],
    field7: ['', Validators.required],
    field8: ['', Validators.required],
    field9: ['', Validators.required],
    field10: ['', Validators.required],
    field11: ['', Validators.required],
    field12: ['', Validators.required],
    field13: [{ value: '', disabled: true }, Validators.required]
});

this.form.valueChanges.subscribe(
    result => this.calculateParams(result)
);

calculateParams(result) {
    console.log(result); // giving the entire form.value object
}

I have a reactive form with over 10 form controls and using subscription on valueChanges observable to detect changes. It works perfectly but output is always the entire form value object(meaning all the form controls and their values). Is there a way to simply get the form control name of the field that changed?

this.form = this.fb.group({
    field1: ['', Validators.required],
    field2: ['', Validators.required],
    field3: ['', Validators.required],
    field4: ['', Validators.required],
    field5: ['', Validators.required],
    field6: ['', Validators.required],
    field7: ['', Validators.required],
    field8: ['', Validators.required],
    field9: ['', Validators.required],
    field10: ['', Validators.required],
    field11: ['', Validators.required],
    field12: ['', Validators.required],
    field13: [{ value: '', disabled: true }, Validators.required]
});

this.form.valueChanges.subscribe(
    result => this.calculateParams(result)
);

calculateParams(result) {
    console.log(result); // giving the entire form.value object
}
Share Improve this question asked Jun 24, 2019 at 2:35 dimitridimitri 751 gold badge1 silver badge5 bronze badges 0
Add a comment  | 

7 Answers 7

Reset to default 5

The rxjs way of Eliseo's answer

this.form.valueChanges.pipe(
    startWith(this.form.value),
    pairwise(),
    map(([oldValues, newValues]) => {
        return Object.keys(newValues).find(k => newValues[k] != oldValues[k]);
    }),
).subscribe(key => {
    console.log( key )
});

It's a work-around but if store the old values you can do some like

this.old={...this.myForm.value}
this.myForm.valueChanges.subscribe(res=>{
  const key=Object.keys(res).find(k=>res[k]!=this.old[k])
  this.old={...this.myForm.value}
})

You can isolate the formcontrol from the formgroup by using the get method and the access the valueChanges method on it.

this.form.get('formcontrolName').valueChanges().

Note: form.get returns you the AbstractControl, which also has the valueChanges method on it.

Haven't fully tested the code but the idea is to pair the controls and their key then listen to valueChanges on each control simultaneously and return object key instead of value (and of course you can map both value and key to the output)

const fields={
    field1: ['', Validators.required],
    field2: ['', Validators.required],
    field3: ['', Validators.required],
    field4: ['', Validators.required],
    field5: ['', Validators.required],
    field6: ['', Validators.required],
}

zip(
 from(Object.values(fb.group(fields).controls)),
 from(Object.keys(fields))
).pipe(mergeMap([control,key])=>control.valueChanges.pipe(mapTo(key)))

You could subscribe to each change individually.

for (const controlProperty in this.myForm.controls) {
    if (this.myForm.controls.hasOwnProperty(controlProperty)) {
      this.myForm.controls[controlProperty].valueChanges.pipe(untilDestroyed(this)).subscribe(result => {
        console.log(controlProperty + " is now ", result);
      });
    }
}

This just felt more angular-y to me.

Use valueChanges for each control after adding control dynamically to FormGroup.

const ct = {key: 'myControl'};    
this.myForm.addControl(ct.key, new FormControl('', Validators.required))
this.myForm.controls[ct.key].valueChanges
.subscribe(d => {
    // here ct will be available as closure , so that we can access every form control here and do operation accordingly
    console.log(d, ct.key);
})

Here ct object will be available inside subscribe as a closure.

The method from @alex-walker (https://stackoverflow.com/a/64830665/134120) seems to be the best, but it only works well on flat forms, with simple key-value pairs. If you have nested forms, with FormArrays of FormGroups, the key property in that method will only return the top-level key of the form, which is not very useful if you want e.g. to indicate to the user the particular input field that triggered the event.

So I combined this method with a deep object diff method described here: https://stackoverflow.com/a/50278133/134120

To get something like this:

this.form.valueChanges
    .pipe(
        startWith(this.form.value),
        pairwise(),
        map(([oldValues, newValues]) => {
            return bdiff(oldValues, newValues);
        })
    )
    .subscribe(keys => {
        console.log(keys);
    });

bdiff(a: Record<string, any>, b: Record<string, any>): string[] {
    return _reduce(
        a,
        (res, val, key) =>
            res.concat(
                (_isPlainObject(val) || Array.isArray(val)) && b
                    ? this.bdiff(val, b[key]).map(x => key + (key.trim ? '' : ']') + (x.search(/^\d/) ? '.' : '[') + x)
                    : !b || val !== b[key]
                    ? [key + (key.trim ? '' : ']')]
                    : []
            ),
        []
    );
}

Which will return an array of nested keys such as keys = ['fieldgroup2.fieldArray[3].myNestedField'] which you should easily access and set as invalid with form.get(keys[0]).valid.

发布评论

评论列表(0)

  1. 暂无评论