5

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 Answers 2

7

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.newPhoneGroup(x))
Sign up to request clarification or add additional context in comments.

2 Comments

Yes, correct. This works, The only challenge is that the rules will run every time you "push" into the array, even though not full data is in the reactive form yet. Therefore I think Angular is missing a feature to pass onlySelf to the push for this approach to work the same way as patch entire form.
You can't put plain js objects in a FormArray, only form controls (/groups/arrays).
4

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.

2 Comments

Thank you! I mentioned onlySelf because the bigger problem I am trying to solve is to patch FormArray with number of entries larger than what exists there at the moment. patchValue will blow up. If I fire push, that method does not have an onlySelf option, therefore will run the rules on the entire form before the entire form is patched fully with new array items. I guess I will submit an enhancement request to Angular team for that. Thanks again!
Opened this issue on GitHub github.com/angular/angular/issues/53042

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.