ngxs / store

🚀 NGXS - State Management for Angular
http://ngxs.io
MIT License
3.54k stars 403 forks source link

Question: How can the forms plugin update a form array of objects? #1844

Open markwhitfeld opened 2 years ago

markwhitfeld commented 2 years ago

Originally posted by @irowbin in https://github.com/ngxs/store/issues/1745#issuecomment-836540164 on 10 May 2021

Hi there, I have been struggling to patching the state of FormArray values using this plugin.

I have something like this in component:

fg = this.fb.group({
 anyTerms: this.fb.array([this.fb.group({ term: [] })]),
})

now if I add more than one object in that array, it populates state values as [{term:'1'}, {term:'2'}] but it can't be populated from state to FormArray

The structure like this requires to create an AbstractControl but couldn't find a way to do from the state to component.

example:

[{term:'1'}, {term:'2'}].forEach(obj => this.controls.push(this.fb.group(obj)))

Is it done internally or something?

Looks like it was updated using this line which only care about raw value.

Can we have type check and patch value instead of raw?

Thanks, Regards.

markwhitfeld commented 2 years ago

Originally posted by @irowbin in https://github.com/ngxs/store/issues/1745#issuecomment-837781756 on 11 May 2021 UPDATE: So instead of waiting for an update, I tweak a bit in this line to patching the value checking if it was an instance of form array and has object type value.

It looks bit ugly workaround code but I had to do this way until someone comes up with a better solution to patching FormGroup inside the FormArray

Workaround code if someone wondering to have a fix: 😆

   Object.keys(this.form.value).forEach((key) => {
        const ctrl = this.form.get(key)
        const modelValue = model[key]
        const isFormArray = this.form.get(key) instanceof FormArray
        const isObject =
          Array.isArray(modelValue) &&
          modelValue.some(
            (val) => typeof val === 'object' && !Array.isArray(val)
          )

        if (isObject && isFormArray) {
          const ctrlArray = ctrl as FormArray
          // we're going to create new anyway so clear existing first.
          // probably need to refactor so we can optimize performance        
          ctrlArray.clear()
          modelValue.forEach((obj) => {
            const innerObj = Object.keys(obj)
            const formObj = {}
            innerObj.forEach((oKey) => {
              formObj[oKey] = new FormControl(obj[oKey])
            })
            ctrlArray.push(new FormGroup(formObj))
          })
        } else {
          ctrl.patchValue(modelValue)
        }
      })

I see in this line only testing array of string for FormArray which won't be the only scenario cause, it can have FormGroup with FormControl in my case so if we add FormGroup in the FormArray, this plugin becomes unusable which I had to tweak a bit. :thinking:

markwhitfeld commented 2 years ago

@irowbin, I hope you don't mind, I moved your comments to a new issue, because they were seemingly unrelated to the issue where you posted them. The handling of the update of an FormArray with objects is unfortunately no simple feat. It is something that, in Angular forms, is generally left for the developer to struggle with. I think that it is near impossible for a plugin to be able to do this correctly in a generic fasion for every app. That being said, I wonder if there is some sort of callback that we could provide to allow the user to create the new FormGroup object that would be nested inside a FormArray. I really wish that Angular's FormArray had some sort of built in 'createChildFormControl` callback to handle this for us... we can only dream!

As a side note, I wrote a little utility for a project that creates proxies of FromGroup and FromArray which are able to do exactly what I am talking about here to assist with new values in an array or nested objects that could be set to null and then be set to a value. Here is a stackblitz demo of this utility: https://stackblitz.com/edit/form-array-angular-intercept-patch-v3 Let me know what you think?