angular / angular

Deliver web apps with confidence 🚀
https://angular.dev
MIT License
95.22k stars 24.99k forks source link

Dynamic forms to auto create reactive forms based on a schema #43219

Open chickencoding123 opened 2 years ago

chickencoding123 commented 2 years ago

Which @angular/* package(s) are relevant/releated to the feature request?

forms

Description

Problem Currently the only way to add additional controls is by using methods such as the addControl on FormGroup or insert on FormArray. However this approach introduces a significant boilerplate for apps since they must entirely manage their own dynamic forms. These are reactive form objects that perform dynamic CRUD operations on their child controls. There's an abundance of issues in this repo that are rooted in lack of a managed solution for dynamic form, that is, a form that can auto-manage its children. FormBuilder is a fantastic tool, but it lack automated CRUD operations for controls.

Proposed solution

To address this we can extend the FormBuilder and add:

  1. A dynamic form object that wraps an AbstractControl, reason is discussed further below
  2. Additional method auto on FormBuilder which will take a schema and returns a dynamic form builder. This schema will mainly represent the structure of auto created AbstractControl including all the additional parameters required to initialize an AbstractControl.
// data can be primitives or complex objects that adhere to the schema
type DynamicFormBuilder = (data: any) => AbstractControl
auto(DynamicFormSchema schema): DynamicFormBuilder

So why not just write a standard recursive method called auto on FormBuilder? This is actually a problem for a few reasons. Firstly there's the case of immediate subscriptions:

// common code in angular apps, won't work for a raw dynamic solution
const fb = new FormBuilder()
const control = fb.auto({})
const valueChanges$ = control.valueChanges.subscribe((value) => {/** Some operation */})

This can break if the auto method hasn't created the control yet. So instead dynamic form builder (which is returned by the auto) will output the proper error since it'll wrap valueChange with a _isCreated check.

_isCreated: boolean = false
create(value: any) {
  // check constrains, if any E.g. node count
  // now do the recursion
  this._isCreated = true
}

valueChanges(fn) {
  if(!this._isCreated) throw Error('Control has not been initialized yet')
  /** */
}

Another issue is the data size. Passing an astronomical array data to create a dynamic FormArray can slow down the page which will only result in more issues to pop up in this repo. Instead we add a constraint to the dynamic form to restrict the amount of nodes that can go in the AbstractControl tree and output a few suggestions to the developer such as to introduce pagination on their data array.

What about the performance? As mentioned above we'll add constrains to the auto for these common cases, but the recursion itself is an O(n) where n is the total number of nested keys in any given data object that's passed in.

Alternatives considered

I'm open to alternatives, but I can't think of any

angular-robot[bot] commented 1 year ago

Just a heads up that we kicked off a community voting process for your feature request. There are 20 days until the voting process ends.

Find more details about Angular's feature request process in our documentation.

angular-robot[bot] commented 1 year ago

Thank you for submitting your feature request! Looks like during the polling process it didn't collect a sufficient number of votes to move to the next stage.

We want to keep Angular rich and ergonomic and at the same time be mindful about its scope and learning journey. If you think your request could live outside Angular's scope, we'd encourage you to collaborate with the community on publishing it as an open source package.

You can find more details about the feature request process in our documentation.

dylhunn commented 1 year ago

We agree that dynamic forms could use some love. We're marking this canonical since we know design work is needed here.