From 69ea14a0f6878a4c0084ddc649631d723f3c4951 Mon Sep 17 00:00:00 2001 From: Ron Netzer Date: Sat, 17 Aug 2019 19:30:31 +0300 Subject: [PATCH 1/5] feat(core): support custom model json map fn injectionToken The map func is of type DynamicFormControlModelConfigMapFn which passes an instance of the service with the model. this is done in order to support recursive calling fromJSON and other methods for cases like ARRAY. The motivation behind this PR is to truly support custom controls. Currently you can only extend known types and the extension is limited, you cannot really add new options to the json config. this allowes to map model config json to a new model class with custom options and configurations. My use case is a draggable form control which can be enabled from the json. --- .../lib/service/dynamic-form.service.spec.ts | 26 ++++++++++++++++- .../src/lib/service/dynamic-form.service.ts | 28 +++++++++++++++++-- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.spec.ts b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.spec.ts index 664f5dd7a..f9afea33a 100644 --- a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.spec.ts +++ b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.spec.ts @@ -7,7 +7,7 @@ import { NG_VALIDATORS, NG_ASYNC_VALIDATORS } from "@angular/forms"; -import { DynamicFormService } from "./dynamic-form.service"; +import { DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN, DynamicFormControlModelConfigMapFn, DynamicFormService } from './dynamic-form.service'; import { DynamicFormValidationService } from "./dynamic-form-validation.service"; import { DynamicFormModel } from "../model/dynamic-form.model"; import { DynamicCheckboxModel } from "../model/checkbox/dynamic-checkbox.model"; @@ -35,6 +35,22 @@ describe("DynamicFormService test suite", () => { let testModel: DynamicFormModel, service: DynamicFormService; + const DYNAMIC_FORM_CONTROL_TYPE_CUSTOM = "CUSTOM"; + + class DynamicCustomModel extends DynamicInputModel { + readonly type = DYNAMIC_FORM_CONTROL_TYPE_CUSTOM; + } + + const dynamicFormControlModelConfigMapFn: DynamicFormControlModelConfigMapFn = (model, formService) => { + const layout = model.layout; + switch (model.type) { + case DYNAMIC_FORM_CONTROL_TYPE_CUSTOM: + return new DynamicCustomModel(model, layout); + default: + return null; + } + } + function testValidator() { return {testValidator: {valid: true}}; } @@ -50,6 +66,7 @@ describe("DynamicFormService test suite", () => { providers: [ DynamicFormService, DynamicFormValidationService, + {provide: DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN, useValue: dynamicFormControlModelConfigMapFn}, {provide: NG_VALIDATORS, useValue: testValidator, multi: true}, {provide: NG_ASYNC_VALIDATORS, useValue: testAsyncValidator, multi: true} ] @@ -219,6 +236,13 @@ describe("DynamicFormService test suite", () => { expect(formModel[15] instanceof DynamicColorPickerModel).toBe(true); }); + it("should parse dynamic custom control JSON", () => { + const json = JSON.stringify([new DynamicCustomModel({ id: 'custom '})]), + formModel = service.fromJSON(json); + + expect(formModel[0] instanceof DynamicCustomModel).toBe(true); + expect(formModel[0].type).toEqual(DYNAMIC_FORM_CONTROL_TYPE_CUSTOM); + }); it("should throw when unknown DynamicFormControlModel id is specified in JSON", () => { diff --git a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.ts b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.ts index 1aee7bce2..d2626921c 100644 --- a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.ts +++ b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from "@angular/core"; +import { Inject, Injectable, InjectionToken, Optional } from '@angular/core'; import { AbstractControl, FormArray, FormControl, FormGroup } from "@angular/forms"; import { AbstractControlOptions } from "@angular/forms"; import { DynamicFormControlModel, FormHooks } from "../model/dynamic-form-control.model"; @@ -46,14 +46,24 @@ import { DynamicFormModel, DynamicUnionFormModel } from "../model/dynamic-form.m import { DynamicPathable } from "../model/misc/dynamic-form-control-path.model"; import { DynamicValidatorsConfig } from "../model/misc/dynamic-form-control-validation.model"; import { maskFromString, parseReviver } from "../utils/json.utils"; -import { isString } from "../utils/core.utils"; +import { isFunction, isString } from '../utils/core.utils'; + +export type DynamicFormControlModelConfigMapFn = (model: any, layout: any, formService: DynamicFormService) => DynamicFormControlModel | null; +export const DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN = new InjectionToken( + 'DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN' +); @Injectable({ providedIn: "root" }) export class DynamicFormService { - constructor(private validationService: DynamicFormValidationService) {} + constructor(private validationService: DynamicFormValidationService, + @Inject(DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN) @Optional() + private readonly DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN: DynamicFormControlModelConfigMapFn, + ) { + this.DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN = DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN; + } private createAbstractControlOptions(validatorsConfig: DynamicValidatorsConfig | null = null, @@ -323,6 +333,12 @@ export class DynamicFormService { formModelJSON.forEach((model: any) => { let layout = model.layout || null; + let customModel = this.getCustomComponentModel(model, layout); + + if (customModel) { + formModel.push(customModel); + return; + } switch (model.type) { @@ -421,4 +437,10 @@ export class DynamicFormService { return formModel; } + + getCustomComponentModel(model: object, layout: any): DynamicFormControlModel | null { + return isFunction(this.DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN) + ? this.DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN(model, layout, this) + : null; + } } From 1a9faf0ea426062072b1cf631c4d35b1ebf6ed9d Mon Sep 17 00:00:00 2001 From: Ron Netzer Date: Sat, 17 Aug 2019 19:55:36 +0300 Subject: [PATCH 2/5] test(core): get layout from map fn --- .../core/src/lib/service/dynamic-form.service.spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.spec.ts b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.spec.ts index f9afea33a..cbede42b1 100644 --- a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.spec.ts +++ b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.spec.ts @@ -41,8 +41,7 @@ describe("DynamicFormService test suite", () => { readonly type = DYNAMIC_FORM_CONTROL_TYPE_CUSTOM; } - const dynamicFormControlModelConfigMapFn: DynamicFormControlModelConfigMapFn = (model, formService) => { - const layout = model.layout; + const dynamicFormControlModelConfigMapFn: DynamicFormControlModelConfigMapFn = (model, layout, formService) => { switch (model.type) { case DYNAMIC_FORM_CONTROL_TYPE_CUSTOM: return new DynamicCustomModel(model, layout); From ed78b24773666c66c0af8d35165bec5ec66a459d Mon Sep 17 00:00:00 2001 From: Ron Netzer Date: Sat, 17 Aug 2019 20:07:26 +0300 Subject: [PATCH 3/5] fix(core): remove config map fn type from costructor --- .../core/src/lib/service/dynamic-form.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.ts b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.ts index d2626921c..4f7497d62 100644 --- a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.ts +++ b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.ts @@ -60,9 +60,9 @@ export class DynamicFormService { constructor(private validationService: DynamicFormValidationService, @Inject(DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN) @Optional() - private readonly DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN: DynamicFormControlModelConfigMapFn, + private readonly DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN: any, ) { - this.DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN = DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN; + this.DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN = DYNAMIC_FORM_CONTROL_MODEL_CONFIG_MAP_FN as DynamicFormControlModelConfigMapFn; } From 84fd0d046b9b0850dd135f55dc8ea60b34339fcf Mon Sep 17 00:00:00 2001 From: Ron Netzer Date: Sat, 17 Aug 2019 20:34:09 +0300 Subject: [PATCH 4/5] test(core): cover getCustomComponentModel method with test --- .../core/src/lib/service/dynamic-form.service.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.spec.ts b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.spec.ts index cbede42b1..72008f507 100644 --- a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.spec.ts +++ b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.spec.ts @@ -236,11 +236,11 @@ describe("DynamicFormService test suite", () => { }); it("should parse dynamic custom control JSON", () => { - const json = JSON.stringify([new DynamicCustomModel({ id: 'custom '})]), - formModel = service.fromJSON(json); + const model = { id: 'custom', type: DYNAMIC_FORM_CONTROL_TYPE_CUSTOM }, + unknownModel = { id: 'unknownType', type: 'UNKNOWN' }; - expect(formModel[0] instanceof DynamicCustomModel).toBe(true); - expect(formModel[0].type).toEqual(DYNAMIC_FORM_CONTROL_TYPE_CUSTOM); + expect(service.getCustomComponentModel(model, null)).toBeDefined(); + expect(service.getCustomComponentModel(unknownModel, null)).toBeNull(); }); it("should throw when unknown DynamicFormControlModel id is specified in JSON", () => { From 23ad5254729665f324287c40ed608b65c4d5d7d5 Mon Sep 17 00:00:00 2001 From: Ron Netzer Date: Sun, 18 Aug 2019 11:25:28 +0300 Subject: [PATCH 5/5] test(core): import coverage in dynamicFormService --- .../core/src/lib/service/dynamic-form.service.spec.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.spec.ts b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.spec.ts index 72008f507..c4f047aa0 100644 --- a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.spec.ts +++ b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form.service.spec.ts @@ -235,10 +235,14 @@ describe("DynamicFormService test suite", () => { expect(formModel[15] instanceof DynamicColorPickerModel).toBe(true); }); + it("should parse dynamic custom control JSON", () => { const model = { id: 'custom', type: DYNAMIC_FORM_CONTROL_TYPE_CUSTOM }, - unknownModel = { id: 'unknownType', type: 'UNKNOWN' }; + unknownModel = { id: 'unknownType', type: "UNKNOWN" }, + json = JSON.stringify([model]), + formModel = service.fromJSON(json); + expect(formModel[0] instanceof DynamicCustomModel).toBe(true); expect(service.getCustomComponentModel(model, null)).toBeDefined(); expect(service.getCustomComponentModel(unknownModel, null)).toBeNull(); });