Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
markwhitfeld opened this issue May 21, 2022 · 2 comments
Open

Comments

@markwhitfeld
Copy link
Member

markwhitfeld commented May 21, 2022

Originally posted by @irowbin in #1745 (comment) 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
Copy link
Member Author

Originally posted by @irowbin in #1745 (comment) 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. 🤔

@markwhitfeld
Copy link
Member Author

@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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant