ngxtension / ngxtension-platform

Utilities for Angular
https://ngxtension.netlify.app/
MIT License
592 stars 87 forks source link

Feature: Creating form based on model utilities #513

Open Harpush opened 3 days ago

Harpush commented 3 days ago

The suggestion is built from two parts:

Part 1 - Model to FormModel type:

So after some experiments with creating such type I had a nice idea I even posted on an angular issue (https://github.com/angular/angular/issues/46864#issuecomment-2370345890) concerning model to form model. The idea allows to do:

interface Test {
  group: { name: 'a' | 'b'; aliases: string[] };
  startDate: Date;
  extra: Record<string, { abc: boolean }>;
}

const testFormSchema = {
  group: { name: 'control', aliases: ['control'] },
  startDate: 'control',
  extra: { [formRecordSchema]: { abc: 'control' } }
} satisfies FormSchema<Test>;

type TestFormControls = ToFormGroupControls<Test, typeof testFormSchema>;

this.formBuilder.group<TestFormControls>(...);

This is not perfect but the best I could think of and gives in most cases (excluding discriminated union groups) a fully typed and scalable solution.

Part 2 - NoInferFormBuilder

Once I had the type from above and started working with it I encountered multiple issues with the form builder types and generally with typescript and forms. The solution I came up with is a new alternative to form builder using the NoInfer typescript type. I also posted a suggestion in the angular repo for it (https://github.com/angular/angular/issues/58001). The main idea is to be a drop in replacement for form builder when you are building a form with an already known form type. In that case you want typescript to infer only from the return type. There are of course caveats like missing discriminate union case inference - but those are better than the shortcomings of not having the NoInfer builder.

Conclusion

As I don't know if it will be accepted by the angular team - I think here is a good place to add it for anyone that wants it as it makes building forms much much easier in cases you have an interface already. Even in cases you don't have an interface - I find it easier to build my form type using regular typescript interfaces and then build the form from it as the auto inference of form types isn't always easy to use.

The implementation isn't complete but here is a stackblitz showing usage of both parts and a comparison between FormBuilder and NoInferFormBuilder: https://stackblitz.com/edit/stackblitz-starters-me8z6o?file=src%2Fmain.ts