Deep copy of Angular Reactive Form?

2020-07-19 05:23发布

I'm trying to build a function that will produce a copy of a given FormGroup. I started with:

function copyForm(form: FormGroup): FormGroup {
  const copy = new FormGroup({});
  for (let key of Object.keys(form.value)) {
    const control = form.controls[key];

    /* Copy the data from the control into a new control */
    const copyControl = new FormControl({[key]: control.value});

    copy.addControl(key, copyControl);
 }

But that doesn't work if there is a FormArray or FormGroup. This one might work if it were recursive, but I couldn't get a good handle on it.

I also attempted to solve it with

function copyForm(form: FormGroup): FormGroup {
  const copy = new FormGroup({});
  for (let key of Object.keys(form.value)) {
    const control = form.controls[key];
    const copyControl = new FormControl({...control.value});
    copy.addControl(key, copyControl);
  }
  return copy;

}

But that didn't work for double-nested FormGroups, any FormArrays or regular FormControls...

I also tried:

function copyForm(form: FormGroup): FormGroup {
  const copy = new FormGroup(Object.assign({}, form.value));
  return copy;
}

But that gives me the error:

ERROR TypeError: control.setParent is not a function

I'm stumped.

4条回答
乱世女痞
2楼-- · 2020-07-19 05:58

I personally use lodash cloneDeep() function found here:

https://lodash.com/docs/#cloneDeep

I use it this way:

const newFormGroup: any = _.cloneDeep(myFormGroup);

And you if you want it strongly typed, you can add as FormGroup as @Andre Elrico suggested in the comments:

const newFormGroup = _.cloneDeep(myFormGroup) as FormGroup;

查看更多
手持菜刀,她持情操
3楼-- · 2020-07-19 06:00

If you have simple FormGroups that contain only FormControls (i.e. not FormGroups or FormArrays), and you know their structure in advance, then this is a simple solution:

Note: by using initaliseFormGroup for initalising the original FormGroup, you will match the validators for each control.

initaliseFormGroup(){
    return new FormGroup({
        x : new FormControl('', Validators.required),
        y : new FormControl('', Validators.minLength(10))
    });
}

cloneFormGroup(oldForm: FormGroup){
    let newForm = this.initaliseFormGroup()
    newForm.patchValue(oldForm.value);
    return newForm;
}

If you have more complex forms (with child FormGroups), or want to clone them dynamically without knowing their structure in advance, then the other answers will better suited.

查看更多
The star\"
4楼-- · 2020-07-19 06:06

This is the deep copy function I came up with which also retains the associated validator / async validator functions and disabled status of each AbstractControl.

/**
 * Deep clones the given AbstractControl, preserving values, validators, async validators, and disabled status.
 * @param control AbstractControl
 * @returns AbstractControl
 */
export function cloneAbstractControl<T extends AbstractControl>(control: T): T {
  let newControl: T;

  if (control instanceof FormGroup) {
    const formGroup = new FormGroup({}, control.validator, control.asyncValidator);
    const controls = control.controls;

    Object.keys(controls).forEach(key => {
      formGroup.addControl(key, cloneAbstractControl(controls[key]));
    })

    newControl = formGroup as any;
  }
  else if (control instanceof FormArray) {
    const formArray = new FormArray([], control.validator, control.asyncValidator);

    control.controls.forEach(formControl => formArray.push(cloneAbstractControl(formControl)))

    newControl = formArray as any;
  }
  else if (control instanceof FormControl) {
    newControl = new FormControl(control.value, control.validator, control.asyncValidator) as any;
  }
  else {
    throw new Error('Error: unexpected control value');
  }

  if (control.disabled) newControl.disable({emitEvent: false});

  return newControl;
}
查看更多
我想做一个坏孩纸
5楼-- · 2020-07-19 06:09

This is how I would do it:

copyFormControl(control: AbstractControl) {
    if (control instanceof FormControl) {
        return new FormControl(control.value);
    } else if (control instanceof FormGroup) {
        const copy = new FormGroup({});
        Object.keys(control.getRawValue()).forEach(key => {
            copy.addControl(key, copyFormControl(control.controls[key]));
        });
        return copy;
    } else if (control instanceof FormArray) {
        const copy = new FormArray([]);
        control.controls.forEach(control => {
            copy.push(copyFormControl(control));
        })
        return copy;
    }
}

I'm using getRawValue() instead of value, because value won't include controls which are disabled.

查看更多
登录 后发表回答