4
votes

I need to make a deep copy of a reactive form. I have tried with ES6 spread syntax for cloning a form. I have used Typescript generics to typecast the return value of the spread syntax to FormGroup. Also, the type of clone form variable is set to FormGroup.

But when the form is cloned by this way, the type is lost on the cloned form variable, that is, it is no longer of type of FormGroup. So, the cloned form is not usable.

Here is the stackblitz for the same: form cloning

What is going wrong in above way of cloning? Why the type FormGroup is lost on the cloned form?

4
You can't clone types. They are just for convenience. Javascript don't have types (sort of), typescript does. - Nosheep
What you're doing is creating a POJO (not an instance of FormGroup: that would require a call to its constructor) and copying the properties of the FormGroup to the POJO. It's not a deep copy at all, but a shallow copy, and it doesn't create an instance of FormGroup. Why would you want to clone a FormGroup? What concrete problem are you trying to solve? - JB Nizet
this is just the wrong approach to whatever problem you're trying to solve. Generally, you need a limited set of forms, just create a method that creates the forms you need so you can instantiate them as needed. - bryan60
Is anything wrong with the typecasting the result to FormGroup with TypeScript generics after using spread syntax? Isn't that typecasting at all? - Abhinandan Khilari
Does this answer your question? Deep copy of Angular Reactive Form? - Vahid

4 Answers

7
votes

Just use a function that return a FormGroup. You can use setValue or patchValue to give the same value. Some like (I use directly the constructor of FormGroup but you can use FormBuilder too)

  createForm(data:any)
  {
    data=data || {name:null,group:{prop1:null,prop2:null}}
    return new FormGroup({
      name: new FormControl(data.name),
      group:new FormGroup({
        prop1:new FormControl(data.group.prop1),
        prop2:new FormControl(data.group.prop2)
      })
    })
  }

/*you can also
  createForm(data:any)
  {
    const form=new FormGroup({
      name: new FormControl(),
      group:new FormGroup({
        prop1:new FormControl(),
        prop2:new FormControl()
      })
    })
    if (data)
      form.patchValue(data)

    return form
  }
  */

And in ngOnInit, e.g.

  ngOnInit() {
       this.myForm=this.createForm({name:"name",group:{prop1:"prop1",prop2:"prop2"}});
       this.myForm2=this.createForm(null);
       this.myForm2.patchValue(this.myForm.value)
       //or this.myForm2.setValue(this.myForm.value)
  }

link

1
votes

Lodash works pretty well deeply cloning FormGroups, even though they are nested. In case you are dealing with dynamic forms, recreating your form manually might result in large amount of lines of code (e.g. checking for existence of conditional forms/controls). Given this scenario, in most cases we could solve it by importing say * as _ from 'lodash' and cloning the FormGroup, FormArray, or FormControl with .cloneDeep. E.g.:

const clonedForm = _.cloneDeep(this.myForm);

Then you will be able to use clonedForm and its inner controls as you would with your myForm without modifying your myForm's nested controls.

0
votes

Here is my solution:

app.component.ts

export class AppComponent {
  formulario = new Formulario();
}

export class Formulario {
  getFormulario() {
    return {
      nombre: new FormControl('', [Validators.required]),
      email: new FormControl('', [Validators.required, Validators.email]),
      password: new FormControl(''),
      cod_perfil: new FormControl('', [Validators.required]),
      validoHasta: new FormControl('')
    };
  }
}

app.component.html

<child [formularios]="formulario"></child>

Now here and wherever you can get different instance of your form,

child.component.ts

export class ChildComponent implements OnInit {
  @Input() formularios: any;
  @Output() formulariosIdiomas = new EventEmitter<any>();
  langs = ['es', 'en'];    

  ngOnInit() {
    console.log(this.formularios.getFormulario());

    // Different instances
    this.langs.forEach(lang => {
      this.formulariosIdiomas[lang] = new FormGroup(
        this.formularios.getFormulario()
      );
    });

  }
}
-4
votes

You can use assign() method of Object with the following syntax:

Object.assign(target, ...sources)

In your case, you can do like this:

this.myForm2 = Object.assign({}, this.myForm)

So, what Object.assign() does?

This method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.

For details, head over here

Hope this helps.