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

FieldConfig Library #3208

Closed
MaxKless opened this issue Mar 1, 2022 · 8 comments
Closed

FieldConfig Library #3208

MaxKless opened this issue Mar 1, 2022 · 8 comments

Comments

@MaxKless
Copy link
Collaborator

MaxKless commented Mar 1, 2022

Is your feature request related to a problem? Please describe.

Formly makes reusing forms easy by its very nature. However, after using Formly to build a large application, I have to come to realize that very often, I don't just need to reuse field types (and wrappers) but also specific configurations.

For example, imagine a first name field that needs to be reused throughout the application (login, addresses, etc). This field will always have the same labels, validation etc. In that case, I have to re-write this configuration throughout the app.

Describe the solution you'd like

It would be great to support this additional layer of reusability in formly to make it even more versatile and maintainable.

I have built a third-party solution that introduces a FieldConfigLibrary that you can inject anywhere in the app (or use without injecting with a special extension) to re-use FormlyFieldConfigs. It works really well but I think it could both profit from a first degree integration and be useful to other formly users.

Read on for an overview of how you would use it if it's implemented how I envision it:

Injecting the FormlyFieldLibrary

The simplest version, just inject the library that is configured with a list of preset configurations and use it like this:

...
constructor(private fieldLibrary: FormlyFieldLibrary) { }

fieldConfig: FormlyFieldConfig[];

ngOnInit() {
  this.fieldConfig = [
    this.fieldLibrary.getConfiguration('firstName'),
    this.fieldLibrary.getConfiguration('lastName')
  ]
}

Of course, if you want to reuse a configuration but make some small adjustments, it will be supported to pass an override object that will be deep merged with the configuration:

this.fieldConfig = [
  this.fieldLibrary.getConfiguration('firstName', { templateOptions: { label: 'new label' } })
]

Automatic config replacement

It's not really nice to inject a service everywhere you want to use formly. So it will be possible to reuse presets like this (note that the preset keyword is obviously WIP):

this.fieldConfig = [
  { preset: 'firstName'  }
]

An extension that injects the FieldConfigLibrary will then substitute the config with the preset.

Overriding can also be done right in the template:

this.fieldConfig = [
  {
    preset: 'lastName',
    templateOptions: {
      label: 'new label'
    }
  }
] 

Looks cleaner and is just as functional! 😄

Support preconfigured configuration groups

An additional feature that could be supported is grouping fields into configuration groups:

this.fieldConfig = this.fieldLibrary.getConfigurationGroup('personalInfo')

Also, overriding can be supported (in a case where personalInfo resolves to firstName & lastName:

this.fieldConfig = this.fieldLibrary.getConfigurationGroup('personalInfo', {
  firstName: { templateOptions: { label: 'new label' } },
})

Something I haven't been able to easily solve but which would probably work with first-level formly support is to enable configuration groups with the shorthand syntax without injecting the service.

** Configuring presets **
This would (probably) not be solved in the FormlyConfig service but its own but it could still be configured in the FormlyModule.forRoot/forChild. Maybe it can work like this:

// first-name.preset.ts
export class FirstNamePreset implements FieldConfigLibraryPreset {
  id = 'firstName';

  getConfiguration(): FormlyFieldConfig {
    return {
    ...
    }
  }
}

// library-config.module.ts
...
FormlyModule.forChild({
  presets: [
    { name: 'firstName', preset: FirstNamePreset },
    ...
    { name: 'personalInfo', resolvesTo: ['firstName', 'lastName'] },
  ]
}

The nice part is that the preset classes can inject services etc and take care of configuring themselves, extracting more display logic from components and making them dumber.

@aitboudad let me know what you think, I can probably build this pretty quickly as I've done it before.

Describe alternatives you've considered

Do this with extending field types, re-type configurations everywhere seperately, continue to use my own solution.

Additional context

Check out intershop/intershop-pwa#1017 for my third party implementation of this. You can see that I had to used pseudo-types like type: #firstName to do the automatic replacement. That could be nicer.

@aitboudad
Copy link
Member

Hi, I'll check further and let you know my thought/plan for that feature soon.

@aitboudad
Copy link
Member

It looks an interesting feature, I've seen some example that rely on type for a such use-case so preset present a better way.
So here are some few notes for the implementations:

  • Implement it as separate sub-package similar to @ngx-formly/core/testing in order to avoid an extra bundle size for those who doesn't rely on it.
  • I do prefer pseudo-types (as mentioned above type: '#firstName') compared to { preset: 'firstName' }.
  • I don't think we may need FormlyFieldLibrary service unless its necessary and in that case it should be marked as private.
  • For naming: @ngx-formly/core/field-preset or @ngx-formly/core/preset (its up to you!)

@kenisteward
Copy link
Collaborator

kenisteward commented Mar 18, 2022 via email

@MaxKless
Copy link
Collaborator Author

Alright cool, I'll get on it! I'm sure there will be plenty of details to discuss later but for now I'll do it according to your comments @aitboudad

@MaxKless
Copy link
Collaborator Author

Just a note to myself and anyone else: I will also have to adjust the testing framework if we use the type: #xyz syntax because those types don't actually exist.

@MaxKless
Copy link
Collaborator Author

MaxKless commented Apr 5, 2022

@aitboudad check out the draft PR for the first implementation.
I created a 'other/presets' example that shows how to use the preset.

Would love some feedback before I commit fully and write tests/docs/etc. Open questions:

  • Defining presets via the FormlyConfig service or create an own service and define like FomlyPresetModule.forRoot({... })?
  • Should we automatically use the preset name as the default key for a field (would still be overridable, ofc)?
  • What do you think about the preset groups I talk about above? Worth trying or not?

@aitboudad
Copy link
Member

Defining presets via the FormlyConfig service or create an own service and define like FomlyPresetModule.forRoot({... })?

its ok to use FormlyConfig service, we may think of a generic way later.

Should we automatically use the preset name as the default key for a field (would still be overridable, ofc)?

Not needed for the moment, IMO pseudo-types (#firstName) would be enough.

What do you think about the preset groups I talk about above? Worth trying or not?

I think it would be better to pass the original config to getConfiguration and let users do the merge by themselves.

@aitboudad
Copy link
Member

This issue has been fixed and released as part of v6.0.0-beta.0 release.

Please let us know, in case you are still encountering a similar issue/problem.
Thank you!

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

No branches or pull requests

3 participants