ng-dynamic-forms
ng-dynamic-forms copied to clipboard
Custom Form Control sample example Needs
I'm submitting a
[ ] Bug / Regression
[ ] Feature Request / Proposal
[x] Question
I'm using
NG Dynamic Forms Version: `X.Y.Z`
[ ] Basic UI
[x] Bootstrap UI
[ ] Foundation UI
[ ] Ionic UI
[ ] Kendo UI
[ ] Material
[ ] NG Bootstrap
[ ] Prime NG
Description
Hello, I'm trying to make a custom control but at some point, I didn't understand as well, if there is example it's good because the documentation not explains enough.
for the hard part I didn't get it is: in corresponding DynamicFormControlModel what should we put ?
@Input() model: /* corresponding DynamicFormControlModel */;
providers: [
{
provide: DYNAMIC_FORM_CONTROL_MAP_FN,
useValue: (model: DynamicFormControlModel): Type<DynamicFormControl> | null => {
switch (model.type) {
case /* corresponding DynamicFormControlModel */:
return MyDynamicCustomFormControlComponent;
}
}
}
]
@NuruddinBadawi
You need to state which DynamicFormControlModel should map to your custom component.
For example if you'd like to replace the default select component of a certain UI package you would do this:
@Input() model: DynamicSelectModel;
providers: [
{
provide: DYNAMIC_FORM_CONTROL_MAP_FN,
useValue: (model: DynamicFormControlModel): Type<DynamicFormControl> | null => {
switch (model.type) {
case DynamicSelectModel:
return MyDynamicCustomFormControlComponent;
}
}
}
]
Thanks to your replay @udos86, but if I need to make new type not override, for example, I have ng-select component custom view to show colors in the list, it's possible to create a new type and use my component?
@NuruddinBadawi
Yes, it technically should be possible to introduce a custom DynamicFormControlModel.
But please beware that there's currently no support for recreating a custom dynamic form control model from JSON.
@udos86 As for the Recreating a custom dynamic form control model from JSON, is this something you plan to implement one day for custom dynamic form model or the complexity of supporting it could lead to problems you think are not worth the effort?
I'm just asking a question here. We are having a similar need at the moment, I'm evaluating our options.
Thanks!
I'm trying to build a custom component based on the autocomplete from primeng with a rest-backend to get a list of suggestions, do u have an example on how to build your own model?
@udos86 Can you clarify the below
But please beware that there's currently no support for recreating a custom dynamic form control model from JSON.
Where does this limitation come from? I'm happy to put in some work to enable this as this is a key requirement for us.
Here is what we are trying to achieve:
- Build custom custom controls (for example a lookup control that needs additional data parameters to figure out where to get its data from)
- Create a form leveraging these custom controls from JSON (we generate this json inkl parameters for lookup fields from a datamodel with annotations hence we cannot use the coding route)
At first glance introducing custom modelType would be the sensible route but I'm open to other suggestions. Again also happy to put in some time to enable this scenario if its not possible as for now.
Did some digging: https://github.com/udos86/ng-dynamic-forms/blob/edd11d9b808175ac0f69844d756956f935800f69/packages/core/src/service/dynamic-form.service.ts#L406
Would be the right approach to implement DYNAMIC_FORM_CONTROL_MODEL_MAP_FN similar to the ones for form controls?
Yes, it technically should be possible to introduce a custom
DynamicFormControlModel.But please beware that there's currently no support for recreating a custom dynamic form control model from JSON.
I have a proof-of-concept working where I can create a dynamic form from JSON with a custom control.
I implemented a custom DynamicFormControlModel by looking at how the Input control was implemented. I extended DynamicInputControlModel<string> and also implemented a config class to extend DynamicInputControlModelConfig<string>.
In order to get loading from JSON working, I had to extend DynamicFormService and re-implement fromJSON(), adding in my custom type to the switch statement:
export class MyFormService extends DynamicFormService {
...
fromJSON(json: string | object[]): DynamicFormModel | never {
...
case CUSTOM_DYNAMIC_FORM_CONTROL_TYPE:
formModel.push(new MyCustomModel(model, layout));
break;
...
}
}
Then I set my app to use my overriden DynamicFormService class via app.module.ts providers:
providers: [
{
provide: DYNAMIC_FORM_CONTROL_MAP_FN,
useValue: (model: DynamicFormControlModel): Type<DynamicFormControl> | null => {
switch (model.type) {
case CUSTOM_DYNAMIC_FORM_CONTROL_TYPE:
return MyDynamicCustomFormControlComponent;
}
}
},
{
provide: DynamicFormService,
useClass: MyFormService
}
],
This works, but it doesn't feel like the most robust way of doing this.
The documentation states that by using this approach we can override the default mapping however, it is not clear for the first sight that this library is NOT extendable. I choose this library because I saw the custom controls section in documentation and it seemed to do exactly what I wanted. Only when got familiar with the library I had to realize that I have to implement the actual behavior myself. I find the documentation to be misleading in this regard.
I read a few issues here and I saw that a lot of us would be happy to contribute in order to make this library better, but there was no answer from the maintainer side. I am a bit disappointed about this. I had to implement visibility relations myself and I am going to implement true customization as well, because we need this behavior in our project.
It would also be good if I was able to use my forked source in my project, but due to the fact that the library contains multiple packages I did not find a way to include this git in packages.json. I had to copy and paste the relevant parts of source code into my project - which sounds ridiculous.
So if anyone can share some solution about these problems, I would be happy to share my contribution to this library. Thx
Now, I describe here what I basically did in order to implement this behavior, maybe it helps someone.
In order to implement a real custom control behavior I declared a new function signature and an injection token for the model selector in dynamic-form.service.ts
export type DynamicFormModelMapFn = (type: string, model: any, layout: any) => DynamicFormControlModel | null;
export const DYNAMIC_FORM_MODEL_MAP_FN = new InjectionToken<DynamicFormModelMapFn>("DYNAMIC_FORM_MODEL_MAP_FN");
this should be injected in constructor:
constructor(
@Inject(DYNAMIC_FORM_MODEL_MAP_FN) @Optional() private readonly DYNAMIC_FORM_MODEL_MAP_FN: any,
private validationService: DynamicFormValidationService) {
this.DYNAMIC_FORM_MODEL_MAP_FN = DYNAMIC_FORM_MODEL_MAP_FN as DynamicFormModelMapFn;
}
then in DynamicFormService.fromJSON the default case has been extended as:
default:
{
if (this.DYNAMIC_FORM_MODEL_MAP_FN !== undefined) {
var model = this.DYNAMIC_FORM_MODEL_MAP_FN(model.type, model, layout);
if (model !== null) {
formModel.push(model);
} else {
throw new Error(`custom form control model is not resolved by type "${model.type}"`);
}
} else {
throw new Error(`unknown form control model defined on JSON with id "${model.id}"`);
}
}
break;
then I use it via a provider in my module:
providers: [{
provide: DYNAMIC_FORM_CONTROL_MAP_FN,
useValue: (model: DynamicFormControlModel): Type<DynamicFormControl> | null => {
switch (model.type) {
case MY_CUSTOM_DYNAMIC_FORM_CONTROL_TYPE_CONSTANT:
return MyDynamicCustomFormControlComponent;
}
}
}, {
provide: DYNAMIC_FORM_MODEL_MAP_FN,
useValue: (type: string, model: any, layout: any): DynamicFormControlModel | null => {
switch (type) {
case MY_CUSTOM_DYNAMIC_FORM_CONTROL_TYPE_CONSTANT:
return new MyDynamicCustomControlModel(model, layout);
}
}
}],
the custom model should define the inherited type field as serializable:
@serializable() readonly type: string = MY_CUSTOM_DYNAMIC_FORM_CONTROL_TYPE_CONSTANT;
everything else is done by the docs
Thanks it's really helpful. Do u have this in any stackblitz or any github repo. I am new to this lib and find it difficult where all to make the changes
On Thu, 28 Mar, 2019, 3:54 AM danielleiszen, [email protected] wrote:
Now, I describe here what I basically did in order to implement this behavior, maybe it helps someone.
In order to implement a real custom control behavior I declared a new function signature and an injection token for the model selector in dynamic-form.service.ts
export type DynamicFormModelMapFn = (type: string, model: any, layout: any) => DynamicFormControlModel | null; export const DYNAMIC_FORM_MODEL_MAP_FN = new InjectionToken<DynamicFormModelMapFn>("DYNAMIC_FORM_MODEL_MAP_FN");
this should be injected in constructor:
constructor( @Inject(DYNAMIC_FORM_MODEL_MAP_FN) @Optional() private readonly DYNAMIC_FORM_MODEL_MAP_FN: any, private validationService: DynamicFormValidationService) { this.DYNAMIC_FORM_MODEL_MAP_FN = DYNAMIC_FORM_MODEL_MAP_FN as DynamicFormModelMapFn; }
then in DynamicFormService.fromJSON the default case has been extended as:
default: { if (this.DYNAMIC_FORM_MODEL_MAP_FN !== undefined) { var model = this.DYNAMIC_FORM_MODEL_MAP_FN(model.type, model, layout);
if (model !== null) { formModel.push(model); } } else { throw new Error(`unknown form control model type defined on JSON object with id "${model.id}"`); }} break;
then I use it via a provider in my module:
providers: [{ provide: DYNAMIC_FORM_CONTROL_MAP_FN, useValue: (model: DynamicFormControlModel): Type<DynamicFormControl> | null => {
switch (model.type) { case MY_CUSTOM_DYNAMIC_FORM_CONTROL_TYPE_CONSTANT: return MyDynamicCustomFormControlComponent; }} }, { provide: DYNAMIC_FORM_MODEL_MAP_FN, useValue: (type: string, model: any, layout: any): DynamicFormControlModel | null => { switch (type) { case MY_CUSTOM_DYNAMIC_FORM_CONTROL_TYPE_CONSTANT: return new MyDynamicCustomControlModel(model, layout); } } }],
the custom model should define the inherited type field as serializable:
@serializable() readonly type: string = MY_CUSTOM_DYNAMIC_FORM_CONTROL_TYPE_CONSTANT;
everything else is done by the docs
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/udos86/ng-dynamic-forms/issues/762#issuecomment-477370340, or mute the thread https://github.com/notifications/unsubscribe-auth/AQi9nJaL8vGziDg8ZTFYVn0EpmbxnU_3ks5va--SgaJpZM4UP8de .
@rockeshub: I am trying to integrate these changes into my repo asap.
Thanks it's really helpful. Do u have this in any stackblitz or any github repo. I am new to this lib and find it difficult where all to make the changes
Thank you
On Fri, 29 Mar, 2019, 7:34 PM danielleiszen, [email protected] wrote:
@rockeshub https://github.com/rockeshub: I am trying to integrate these changes into my repo asap.
Thanks it's really helpful. Do u have this in any stackblitz or any github repo. I am new to this lib and find it difficult where all to make the changes
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/udos86/ng-dynamic-forms/issues/762#issuecomment-478009199, or mute the thread https://github.com/notifications/unsubscribe-auth/AQi9nFdAb2RvL1RTdLpbWP9UfCZ76xOrks5vbh1YgaJpZM4UP8de .
default: { if (this.DYNAMIC_FORM_MODEL_MAP_FN !== undefined) { var model = this.DYNAMIC_FORM_MODEL_MAP_FN(model.type, model, layout); if (model !== null) { formModel.push(model); } else { throw new Error(`custom form control model is not resolved by type "${model.type}"`); } } else { throw new Error(`unknown form control model defined on JSON with id "${model.id}"`); } } break;
I'm not sure if this would work in my use case, as I need to inject helper services into the form service to resolve data for the custom form control models.
Perhaps the custom logic added in this example could be implemented in an separate method on the DynamicFormService class, which could then be overridded in a subclass?
Hi, I've stumbled on this issue after opening a PR exactly for this case.
https://github.com/udos86/ng-dynamic-forms/pull/1009
Now, I describe here what I basically did in order to implement this behavior, maybe it helps someone.
In order to implement a real custom control behavior I declared a new function signature and an injection token for the model selector in dynamic-form.service.ts
export type DynamicFormModelMapFn = (type: string, model: any, layout: any) => DynamicFormControlModel | null; export const DYNAMIC_FORM_MODEL_MAP_FN = new InjectionToken<DynamicFormModelMapFn>("DYNAMIC_FORM_MODEL_MAP_FN");this should be injected in constructor:
constructor( @Inject(DYNAMIC_FORM_MODEL_MAP_FN) @Optional() private readonly DYNAMIC_FORM_MODEL_MAP_FN: any, private validationService: DynamicFormValidationService) { this.DYNAMIC_FORM_MODEL_MAP_FN = DYNAMIC_FORM_MODEL_MAP_FN as DynamicFormModelMapFn; }then in DynamicFormService.fromJSON the
defaultcase has been extended as:default: { if (this.DYNAMIC_FORM_MODEL_MAP_FN !== undefined) { var model = this.DYNAMIC_FORM_MODEL_MAP_FN(model.type, model, layout); if (model !== null) { formModel.push(model); } else { throw new Error(`custom form control model is not resolved by type "${model.type}"`); } } else { throw new Error(`unknown form control model defined on JSON with id "${model.id}"`); } } break;then I use it via a provider in my module:
providers: [{ provide: DYNAMIC_FORM_CONTROL_MAP_FN, useValue: (model: DynamicFormControlModel): Type<DynamicFormControl> | null => { switch (model.type) { case MY_CUSTOM_DYNAMIC_FORM_CONTROL_TYPE_CONSTANT: return MyDynamicCustomFormControlComponent; } } }, { provide: DYNAMIC_FORM_MODEL_MAP_FN, useValue: (type: string, model: any, layout: any): DynamicFormControlModel | null => { switch (type) { case MY_CUSTOM_DYNAMIC_FORM_CONTROL_TYPE_CONSTANT: return new MyDynamicCustomControlModel(model, layout); } } }],the custom model should define the inherited type field as serializable:
@serializable() readonly type: string = MY_CUSTOM_DYNAMIC_FORM_CONTROL_TYPE_CONSTANT;everything else is done by the docs
That's exactly what I did except that my custom map fn runs before the default fromJSON's switch
Let me know what you think
@ronnetzer - thanks for this solution! hope it will make it into the package.. it's a life saver for anyone relying on custom controls and fromJSON model.