How to patch form array in typed angular reactive forms

179 Views Asked by At

I am trying to figure out how to patch form array in a typed reactive Angular form. patchValue and setValue does not work in my mind consistently with FormControl for example. Here is a form

 this.form = fb.group<IPersonForm>({
      name: new FormControl<string | null>('bob'),
      phones: new FormArray<FormGroup<IPhoneForm>>([]),
    });

I can patch name control easily enough:

this.form.controls.name.patchValue('Jim', { onlySelf: true, emitEvent: false });

But form array is a challenge. Say I create a replacemenmt array

const myPhone = this.fb.group<IPhoneForm>({
      phone: new FormControl<string | null | undefined>('bob')
    });
const array = new FormArray<FormGroup<IPhoneForm>>([myPhone]);

Now if I try to patch (or set), neither compiles:

    //this.form.controls.phones.setValue(array);
    //this.form.controls.phones.setValue(array.controls);
    //this.form.controls.phones.patchValue(array);
    //this.form.controls.phones.patchValue(array.controls);

I can use setControl, but that method does not have an option for onlySelf:

this.form.setControl('phones', array, { emitEvent: false });

I feel like onlySelf is needed there because if I am replacing the data and have complex validations, I do not want them to run on any control until entire form is patched. Thank you!

Here is a stackblitz with a demo: https://stackblitz.com/edit/angular-path-typed-array?file=src%2Fmain.ts

2

There are 2 best solutions below

2
On BEST ANSWER

you should pass the logical value, not another form to the patch

this.form.controls.phones.patchValue([{phone: '123'}]);

or

this.form.patchValue({phones: [{phone: '123'}]})

also possible to do in one call with the name at the same time

this.form.patchValue({name: 'Jim', phones: [{phone: '123'}]})

you can add onlySelf or emitEvent if you want. they are both unrelated to the question.

1
On

When you patch an formArray, If the formArray have not enough elements this elements are not added. If less element, only the first elements are changed.

e.g.

  formArray=new FormArray<FormGroup<IPhoneForm>>([this.newPhoneGroup()])
  ngOnInit(){
    const dataArray=[{phone:'222222'},{phone:'33333'}]
    this.formArray.patchValue(dataArray)
  }
  newPhoneGroup()
  {
    return new FormGroup({
      phone:new FormControl()
    })
  }

your formArrayElement only have only one element!

You need

    dataArray=[{phone:'222222'},{phone:'33333'}]

    this.formArray.clear();
    while (this.formArray.controls.length<dataArray.length)
      this.formArray.push(this.newPhoneGroup());

    
    this.formArray.patchValue(dataArray)

You can also take another aproach:

Change the function newPhoneGroup like

  newPhoneGroup(data:any=null)
  {
    data=data || {phone:''}
    return new FormGroup({
      phone:new FormControl(data.phone)
    })
  }

And write

  this.formArray=dataArray.map(x=>this.newFormGroup(x))