From 481a16bdbffc08976510dfebf948fb71930d2c34 Mon Sep 17 00:00:00 2001 From: stelios kontarinis Date: Thu, 18 Jun 2020 15:55:12 +0300 Subject: [PATCH 1/3] add findByPath function on dynamic-form.service --- .../src/lib/service/dynamic-form.service.ts | 890 +++++++++++------- 1 file changed, 524 insertions(+), 366 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 da3ae8894..6a3322273 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,424 +1,582 @@ import { Injectable } from "@angular/core"; -import { AbstractControl, FormArray, FormControl, FormGroup } from "@angular/forms"; +import { + AbstractControl, + FormArray, + FormControl, + FormGroup, +} from "@angular/forms"; import { AbstractControlOptions } from "@angular/forms"; import { DynamicFormControlModel } from "../model/dynamic-form-control.model"; import { DynamicFormValueControlModel } from "../model/dynamic-form-value-control.model"; import { - DynamicFormArrayModel, - DYNAMIC_FORM_CONTROL_TYPE_ARRAY, - DynamicFormArrayGroupModel + DynamicFormArrayModel, + DYNAMIC_FORM_CONTROL_TYPE_ARRAY, + DynamicFormArrayGroupModel, } from "../model/form-array/dynamic-form-array.model"; -import { DYNAMIC_FORM_CONTROL_TYPE_GROUP, DynamicFormGroupModel } from "../model/form-group/dynamic-form-group.model"; import { - DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP, - DynamicCheckboxGroupModel + DYNAMIC_FORM_CONTROL_TYPE_GROUP, + DynamicFormGroupModel, +} from "../model/form-group/dynamic-form-group.model"; +import { + DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP, + DynamicCheckboxGroupModel, } from "../model/checkbox/dynamic-checkbox-group.model"; -import { DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX, DynamicCheckboxModel } from "../model/checkbox/dynamic-checkbox.model"; import { - DYNAMIC_FORM_CONTROL_TYPE_COLORPICKER, - DynamicColorPickerModel + DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX, + DynamicCheckboxModel, +} from "../model/checkbox/dynamic-checkbox.model"; +import { + DYNAMIC_FORM_CONTROL_TYPE_COLORPICKER, + DynamicColorPickerModel, } from "../model/colorpicker/dynamic-colorpicker.model"; import { - DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER, - DynamicDatePickerModel + DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER, + DynamicDatePickerModel, } from "../model/datepicker/dynamic-datepicker.model"; -import { DYNAMIC_FORM_CONTROL_TYPE_EDITOR, DynamicEditorModel } from "../model/editor/dynamic-editor.model"; import { - DYNAMIC_FORM_CONTROL_TYPE_FILE_UPLOAD, - DynamicFileUploadModel + DYNAMIC_FORM_CONTROL_TYPE_EDITOR, + DynamicEditorModel, +} from "../model/editor/dynamic-editor.model"; +import { + DYNAMIC_FORM_CONTROL_TYPE_FILE_UPLOAD, + DynamicFileUploadModel, } from "../model/file-upload/dynamic-file-upload.model"; -import { DYNAMIC_FORM_CONTROL_TYPE_INPUT, DynamicInputModel } from "../model/input/dynamic-input.model"; import { - DYNAMIC_FORM_CONTROL_TYPE_RADIO_GROUP, - DynamicRadioGroupModel + DYNAMIC_FORM_CONTROL_TYPE_INPUT, + DynamicInputModel, +} from "../model/input/dynamic-input.model"; +import { + DYNAMIC_FORM_CONTROL_TYPE_RADIO_GROUP, + DynamicRadioGroupModel, } from "../model/radio/dynamic-radio-group.model"; -import { DYNAMIC_FORM_CONTROL_TYPE_RATING, DynamicRatingModel } from "../model/rating/dynamic-rating.model"; -import { DYNAMIC_FORM_CONTROL_TYPE_SELECT, DynamicSelectModel } from "../model/select/dynamic-select.model"; -import { DYNAMIC_FORM_CONTROL_TYPE_SLIDER, DynamicSliderModel } from "../model/slider/dynamic-slider.model"; -import { DYNAMIC_FORM_CONTROL_TYPE_SWITCH, DynamicSwitchModel } from "../model/switch/dynamic-switch.model"; -import { DYNAMIC_FORM_CONTROL_TYPE_TEXTAREA, DynamicTextAreaModel } from "../model/textarea/dynamic-textarea.model"; import { - DYNAMIC_FORM_CONTROL_TYPE_TIMEPICKER, - DynamicTimePickerModel + DYNAMIC_FORM_CONTROL_TYPE_RATING, + DynamicRatingModel, +} from "../model/rating/dynamic-rating.model"; +import { + DYNAMIC_FORM_CONTROL_TYPE_SELECT, + DynamicSelectModel, +} from "../model/select/dynamic-select.model"; +import { + DYNAMIC_FORM_CONTROL_TYPE_SLIDER, + DynamicSliderModel, +} from "../model/slider/dynamic-slider.model"; +import { + DYNAMIC_FORM_CONTROL_TYPE_SWITCH, + DynamicSwitchModel, +} from "../model/switch/dynamic-switch.model"; +import { + DYNAMIC_FORM_CONTROL_TYPE_TEXTAREA, + DynamicTextAreaModel, +} from "../model/textarea/dynamic-textarea.model"; +import { + DYNAMIC_FORM_CONTROL_TYPE_TIMEPICKER, + DynamicTimePickerModel, } from "../model/timepicker/dynamic-timepicker.model"; import { DynamicFormValidationService } from "./dynamic-form-validation.service"; -import { DynamicFormModel, DynamicUnionFormModel } from "../model/dynamic-form.model"; +import { + DynamicFormModel, + DynamicUnionFormModel, +} from "../model/dynamic-form.model"; import { DynamicPathable } from "../model/misc/dynamic-form-control-path.model"; -import { DynamicFormHook, DynamicValidatorsConfig } from "../model/misc/dynamic-form-control-validation.model"; +import { + DynamicFormHook, + DynamicValidatorsConfig, +} from "../model/misc/dynamic-form-control-validation.model"; import { maskFromString, parseReviver } from "../utils/json.utils"; import { isString } from "../utils/core.utils"; import { DynamicFormComponent } from "../component/dynamic-form.component"; import { DynamicFormComponentService } from "./dynamic-form-component.service"; @Injectable({ - providedIn: "root" + providedIn: "root", }) export class DynamicFormService { - - constructor(private componentService: DynamicFormComponentService, - private validationService: DynamicFormValidationService) { - } - - private createAbstractControlOptions(validatorsConfig: DynamicValidatorsConfig | null = null, - asyncValidatorsConfig: DynamicValidatorsConfig | null = null, - updateOn: DynamicFormHook | null = null): AbstractControlOptions { - - return { - asyncValidators: asyncValidatorsConfig !== null ? this.validationService.getAsyncValidators(asyncValidatorsConfig) : null, - validators: validatorsConfig !== null ? this.validationService.getValidators(validatorsConfig) : null, - updateOn: updateOn !== null && this.validationService.isFormHook(updateOn) ? updateOn : DynamicFormHook.Change - }; - } - - createFormArray(formArrayModel: DynamicFormArrayModel): FormArray { - - const controls: AbstractControl[] = []; - const options = this.createAbstractControlOptions(formArrayModel.validators, formArrayModel.asyncValidators, - formArrayModel.updateOn); - - for (let index = 0; index < formArrayModel.size; index++) { - - const groupModel = formArrayModel.get(index); - const groupOptions = this.createAbstractControlOptions(formArrayModel.groupValidators, - formArrayModel.groupAsyncValidators, formArrayModel.updateOn); - - controls.push(this.createFormGroup(groupModel.group, groupOptions, groupModel)); - } - - return new FormArray(controls, options); + constructor( + private componentService: DynamicFormComponentService, + private validationService: DynamicFormValidationService + ) {} + + private createAbstractControlOptions( + validatorsConfig: DynamicValidatorsConfig | null = null, + asyncValidatorsConfig: DynamicValidatorsConfig | null = null, + updateOn: DynamicFormHook | null = null + ): AbstractControlOptions { + return { + asyncValidators: + asyncValidatorsConfig !== null + ? this.validationService.getAsyncValidators(asyncValidatorsConfig) + : null, + validators: + validatorsConfig !== null + ? this.validationService.getValidators(validatorsConfig) + : null, + updateOn: + updateOn !== null && this.validationService.isFormHook(updateOn) + ? updateOn + : DynamicFormHook.Change, + }; + } + + createFormArray(formArrayModel: DynamicFormArrayModel): FormArray { + const controls: AbstractControl[] = []; + const options = this.createAbstractControlOptions( + formArrayModel.validators, + formArrayModel.asyncValidators, + formArrayModel.updateOn + ); + + for (let index = 0; index < formArrayModel.size; index++) { + const groupModel = formArrayModel.get(index); + const groupOptions = this.createAbstractControlOptions( + formArrayModel.groupValidators, + formArrayModel.groupAsyncValidators, + formArrayModel.updateOn + ); + + controls.push( + this.createFormGroup(groupModel.group, groupOptions, groupModel) + ); } - createFormGroup(formModel: DynamicFormModel, options: AbstractControlOptions | null = null, - parent: DynamicPathable | null = null): FormGroup { - - const controls: { [controlId: string]: AbstractControl; } = {}; - - formModel.forEach(model => { - - model.parent = parent; - - switch (model.type) { - - case DYNAMIC_FORM_CONTROL_TYPE_ARRAY: - - controls[model.id] = this.createFormArray(model as DynamicFormArrayModel); - break; - - case DYNAMIC_FORM_CONTROL_TYPE_GROUP: - case DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP: - - const groupModel = model as DynamicFormGroupModel; - const groupOptions = this.createAbstractControlOptions(groupModel.validators, - groupModel.asyncValidators, groupModel.updateOn); - - controls[model.id] = this.createFormGroup(groupModel.group, groupOptions, groupModel); - break; - - default: - - const controlModel = model as DynamicFormValueControlModel; - const controlState = {value: controlModel.value, disabled: controlModel.disabled}; - const controlOptions = this.createAbstractControlOptions(controlModel.validators, - controlModel.asyncValidators, controlModel.updateOn); - - controls[model.id] = new FormControl(controlState, controlOptions); - } - }); - - return new FormGroup(controls, options); + return new FormArray(controls, options); + } + + createFormGroup( + formModel: DynamicFormModel, + options: AbstractControlOptions | null = null, + parent: DynamicPathable | null = null + ): FormGroup { + const controls: { [controlId: string]: AbstractControl } = {}; + + formModel.forEach(model => { + model.parent = parent; + + switch (model.type) { + case DYNAMIC_FORM_CONTROL_TYPE_ARRAY: + controls[model.id] = this.createFormArray( + model as DynamicFormArrayModel + ); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_GROUP: + case DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP: + const groupModel = model as DynamicFormGroupModel; + const groupOptions = this.createAbstractControlOptions( + groupModel.validators, + groupModel.asyncValidators, + groupModel.updateOn + ); + + controls[model.id] = this.createFormGroup( + groupModel.group, + groupOptions, + groupModel + ); + break; + + default: + const controlModel = model as DynamicFormValueControlModel; + const controlState = { + value: controlModel.value, + disabled: controlModel.disabled, + }; + const controlOptions = this.createAbstractControlOptions( + controlModel.validators, + controlModel.asyncValidators, + controlModel.updateOn + ); + + controls[model.id] = new FormControl(controlState, controlOptions); + } + }); + + return new FormGroup(controls, options); + } + + getPathSegment(model: DynamicPathable): string { + return model instanceof DynamicFormArrayGroupModel + ? model.index.toString() + : (model as DynamicFormControlModel).id; + } + + getPath(model: DynamicPathable, join: boolean = false): string[] | string { + const path = [this.getPathSegment(model)]; + let parent = model.parent; + + while (parent) { + path.unshift(this.getPathSegment(parent)); + parent = parent.parent; } - getPathSegment(model: DynamicPathable): string { - return model instanceof DynamicFormArrayGroupModel ? model.index.toString() : (model as DynamicFormControlModel).id; + return join ? path.join(".") : path; + } + + addFormGroupControl( + formGroup: FormGroup, + formModel: DynamicUnionFormModel, + ...models: DynamicFormModel + ): void { + if (formModel instanceof DynamicFormGroupModel) { + this.insertFormGroupControl( + formModel.size(), + formGroup, + formModel, + ...models + ); + } else { + const model = formModel as DynamicFormModel; + this.insertFormGroupControl(model.length, formGroup, model, ...models); } - - getPath(model: DynamicPathable, join: boolean = false): string[] | string { - - const path = [this.getPathSegment(model)]; - let parent = model.parent; - - while (parent) { - - path.unshift(this.getPathSegment(parent)); - parent = parent.parent; - } - - return join ? path.join(".") : path; + } + + moveFormGroupControl( + index: number, + step: number, + formModel: DynamicUnionFormModel + ): void { + if (formModel instanceof DynamicFormGroupModel) { + formModel.move(index, step); + } else { + const model = formModel as DynamicFormModel; + model.splice(index + step, 0, ...model.splice(index, 1)); } - - addFormGroupControl(formGroup: FormGroup, formModel: DynamicUnionFormModel, ...models: DynamicFormModel): void { - - if (formModel instanceof DynamicFormGroupModel) { - - this.insertFormGroupControl(formModel.size(), formGroup, formModel, ...models); - - } else { - - const model = formModel as DynamicFormModel; - this.insertFormGroupControl(model.length, formGroup, model, ...models); - } + } + + insertFormGroupControl( + index: number, + formGroup: FormGroup, + formModel: DynamicUnionFormModel, + ...models: DynamicFormModel + ): void { + const parent = + formModel instanceof DynamicFormGroupModel ? formModel : null; + const controls = this.createFormGroup(models, null, parent).controls; + + Object.keys(controls).forEach((controlName, idx) => { + const controlModel = models[idx]; + + if (formModel instanceof DynamicFormGroupModel) { + formModel.insert(index, controlModel); + } else { + (formModel as DynamicFormModel).splice(index, 0, controlModel); + } + + formGroup.addControl(controlName, controls[controlName]); + }); + } + + removeFormGroupControl( + index: number, + formGroup: FormGroup, + formModel: DynamicUnionFormModel + ): void { + if (formModel instanceof DynamicFormGroupModel) { + formGroup.removeControl(formModel.get(index).id); + formModel.remove(index); + } else { + formGroup.removeControl(formModel[index].id); + (formModel as DynamicFormModel).splice(index, 1); } - - moveFormGroupControl(index: number, step: number, formModel: DynamicUnionFormModel): void { - - if (formModel instanceof DynamicFormGroupModel) { - - formModel.move(index, step); - + } + + addFormArrayGroup( + formArray: FormArray, + formArrayModel: DynamicFormArrayModel + ): void { + const groupModel = formArrayModel.addGroup(); + + formArray.push(this.createFormGroup(groupModel.group, null, groupModel)); + } + + insertFormArrayGroup( + index: number, + formArray: FormArray, + formArrayModel: DynamicFormArrayModel + ): void { + const groupModel = formArrayModel.insertGroup(index); + + formArray.insert( + index, + this.createFormGroup(groupModel.group, null, groupModel) + ); + } + + moveFormArrayGroup( + index: number, + step: number, + formArray: FormArray, + formArrayModel: DynamicFormArrayModel + ): void { + const newIndex = index + step; + const moveUp = step >= 0; + + if ( + index >= 0 && + index < formArrayModel.size && + newIndex >= 0 && + newIndex < formArrayModel.size + ) { + const movingGroups: AbstractControl[] = []; + + for ( + let i = moveUp ? index : newIndex; + i <= (moveUp ? newIndex : index); + i++ + ) { + movingGroups.push(formArray.at(i)); + } + + movingGroups.forEach((formControl, idx) => { + let position; + + if (moveUp) { + position = idx === 0 ? newIndex : index + idx - 1; } else { - - const model = formModel as DynamicFormModel; - model.splice(index + step, 0, ...model.splice(index, 1)); + position = + idx === movingGroups.length - 1 ? newIndex : newIndex + idx + 1; } - } - - insertFormGroupControl(index: number, formGroup: FormGroup, formModel: DynamicUnionFormModel, - ...models: DynamicFormModel): void { - - const parent = formModel instanceof DynamicFormGroupModel ? formModel : null; - const controls = this.createFormGroup(models, null, parent).controls; - Object.keys(controls).forEach((controlName, idx) => { - - const controlModel = models[idx]; - - if (formModel instanceof DynamicFormGroupModel) { - formModel.insert(index, controlModel); - - } else { - (formModel as DynamicFormModel).splice(index, 0, controlModel); - } + formArray.setControl(position, formControl); + }); - formGroup.addControl(controlName, controls[controlName]); - }); + formArrayModel.moveGroup(index, step); + } else { + throw new Error( + `form array group cannot be moved due to index or new index being out of bounds` + ); } - - removeFormGroupControl(index: number, formGroup: FormGroup, formModel: DynamicUnionFormModel): void { - - if (formModel instanceof DynamicFormGroupModel) { - - formGroup.removeControl(formModel.get(index).id); - formModel.remove(index); - - } else { - - formGroup.removeControl(formModel[index].id); - (formModel as DynamicFormModel).splice(index, 1); + } + + removeFormArrayGroup( + index: number, + formArray: FormArray, + formArrayModel: DynamicFormArrayModel + ): void { + formArray.removeAt(index); + formArrayModel.removeGroup(index); + } + + clearFormArray( + formArray: FormArray, + formArrayModel: DynamicFormArrayModel + ): void { + formArray.clear(); + formArrayModel.clear(); + } + + findById( + id: string, + formModel: DynamicFormModel + ): DynamicFormControlModel | null { + let result = null; + + const findByIdFn = ( + modelId: string, + groupModel: DynamicFormModel + ): void => { + for (const controlModel of groupModel) { + if (controlModel.id === modelId) { + result = controlModel; + break; } - } - - addFormArrayGroup(formArray: FormArray, formArrayModel: DynamicFormArrayModel): void { - const groupModel = formArrayModel.addGroup(); - - formArray.push(this.createFormGroup(groupModel.group, null, groupModel)); - } - - insertFormArrayGroup(index: number, formArray: FormArray, formArrayModel: DynamicFormArrayModel): void { - - const groupModel = formArrayModel.insertGroup(index); - - formArray.insert(index, this.createFormGroup(groupModel.group, null, groupModel)); - } - - moveFormArrayGroup(index: number, step: number, formArray: FormArray, formArrayModel: DynamicFormArrayModel): void { - - const newIndex = index + step; - const moveUp = step >= 0; - - if ((index >= 0 && index < formArrayModel.size) && (newIndex >= 0 && newIndex < formArrayModel.size)) { - - const movingGroups: AbstractControl[] = []; - - for (let i = moveUp ? index : newIndex; i <= (moveUp ? newIndex : index); i++) { - movingGroups.push(formArray.at(i)); - } - - movingGroups.forEach((formControl, idx) => { - - let position; - - if (moveUp) { - position = idx === 0 ? newIndex : index + idx - 1; - - } else { - position = idx === movingGroups.length - 1 ? newIndex : newIndex + idx + 1; - } - - formArray.setControl(position, formControl); - }); - - formArrayModel.moveGroup(index, step); - - } else { - throw new Error(`form array group cannot be moved due to index or new index being out of bounds`); + if (controlModel instanceof DynamicFormGroupModel) { + findByIdFn(modelId, (controlModel as DynamicFormGroupModel).group); } - } - - removeFormArrayGroup(index: number, formArray: FormArray, formArrayModel: DynamicFormArrayModel): void { - - formArray.removeAt(index); - formArrayModel.removeGroup(index); - } - - clearFormArray(formArray: FormArray, formArrayModel: DynamicFormArrayModel): void { - - formArray.clear(); - formArrayModel.clear(); - } - - findById(id: string, formModel: DynamicFormModel): DynamicFormControlModel | null { - - let result = null; - - const findByIdFn = (modelId: string, groupModel: DynamicFormModel): void => { - - for (const controlModel of groupModel) { - - if (controlModel.id === modelId) { - result = controlModel; - break; - } - - if (controlModel instanceof DynamicFormGroupModel) { - findByIdFn(modelId, (controlModel as DynamicFormGroupModel).group); + } + }; + + findByIdFn(id, formModel); + + return result; + } + + findByPath = ( + fullPath: string, + formModel: DynamicFormControlModel[] + ): DynamicFormControlModel | null => { + let result = null, + findByPathFn = (path: string, groupModel: any): void => { + let id: string = path.split(".")[0]; + let pathArray: string[] = path.split("."); + pathArray.splice(0, 1); + path = pathArray.join("."); + + for (let controlModel of groupModel) { + if (controlModel.id === id) { + if (path.length === 0) { + result = controlModel; + break; + } else if ( + path.split(".").length == 1 && + Number.isInteger(Number(path.split(".")[0])) && + !controlModel["multiple"] + ) { + result = (controlModel as DynamicFormArrayModel).groups[ + path.split(".")[0] + ]; + break; + } else { + if (controlModel instanceof DynamicFormGroupModel) { + findByPathFn(path, controlModel.group); + } else if (controlModel instanceof DynamicFormArrayModel) { + id = path.split(".")[0]; + pathArray = path.split("."); + pathArray.splice(0, 1); + path = pathArray.join("."); + if ( + controlModel.groups && + controlModel.groups[id] && + controlModel.groups[id].group + ) { + findByPathFn(path, controlModel.groups[id].group); } + } } - }; - - findByIdFn(id, formModel); - - return result; - } - - findModelById(id: string, formModel: DynamicFormModel): T | null { - return this.findById(id, formModel) as T; - } - - findControlByModel(model: DynamicFormControlModel, group: FormGroup): T | null { - return group.root.get(this.getPath(model, true)) as T; - } - - detectChanges(formComponent?: DynamicFormComponent): void { - - if (formComponent instanceof DynamicFormComponent) { - - formComponent.markForCheck(); - formComponent.detectChanges(); - - } else { - - for (const form of this.componentService.getForms()) { - form.markForCheck(); - form.detectChanges(); - } + } } + }; + + findByPathFn(fullPath, formModel); + + return result; + }; + + findModelById( + id: string, + formModel: DynamicFormModel + ): T | null { + return this.findById(id, formModel) as T; + } + + findControlByModel( + model: DynamicFormControlModel, + group: FormGroup + ): T | null { + return group.root.get(this.getPath(model, true)) as T; + } + + detectChanges(formComponent?: DynamicFormComponent): void { + if (formComponent instanceof DynamicFormComponent) { + formComponent.markForCheck(); + formComponent.detectChanges(); + } else { + for (const form of this.componentService.getForms()) { + form.markForCheck(); + form.detectChanges(); + } } + } + + fromJSON(json: string | object[]): DynamicFormModel | never { + const formModelJSON = isString(json) + ? JSON.parse(json, parseReviver) + : json; + const formModel: DynamicFormModel = []; + + formModelJSON.forEach((model: any) => { + const layout = model.layout ?? null; + + switch (model.type) { + case DYNAMIC_FORM_CONTROL_TYPE_ARRAY: + const formArrayModel = model as DynamicFormArrayModel; + + if (Array.isArray(formArrayModel.groups)) { + formArrayModel.groups.forEach( + (groupModel: DynamicFormArrayGroupModel) => { + groupModel.group = this.fromJSON( + groupModel.group + ) as DynamicFormModel; + } + ); + } + + formArrayModel.groupFactory = () => { + return this.fromJSON(formArrayModel.groupPrototype); + }; + + formModel.push(new DynamicFormArrayModel(model, layout)); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX: + formModel.push(new DynamicCheckboxModel(model, layout)); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP: + model.group = this.fromJSON(model.group) as DynamicCheckboxModel[]; + formModel.push(new DynamicCheckboxGroupModel(model, layout)); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_COLORPICKER: + formModel.push(new DynamicColorPickerModel(model, layout)); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER: + formModel.push(new DynamicDatePickerModel(model, layout)); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_EDITOR: + formModel.push(new DynamicEditorModel(model, layout)); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_FILE_UPLOAD: + model.value = null; + formModel.push(new DynamicFileUploadModel(model, layout)); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_GROUP: + model.group = this.fromJSON(model.group); + formModel.push(new DynamicFormGroupModel(model, layout)); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_INPUT: + const inputModel = model as DynamicInputModel; + + if (inputModel.mask !== null) { + if (!(inputModel.mask instanceof Function)) { + inputModel.mask = maskFromString(inputModel.mask as string); + } + } - fromJSON(json: string | object[]): DynamicFormModel | never { - - const formModelJSON = isString(json) ? JSON.parse(json, parseReviver) : json; - const formModel: DynamicFormModel = []; - - formModelJSON.forEach((model: any) => { - - const layout = model.layout ?? null; - - switch (model.type) { - - case DYNAMIC_FORM_CONTROL_TYPE_ARRAY: - const formArrayModel = model as DynamicFormArrayModel; - - if (Array.isArray(formArrayModel.groups)) { - - formArrayModel.groups.forEach((groupModel: DynamicFormArrayGroupModel) => { - groupModel.group = this.fromJSON(groupModel.group) as DynamicFormModel; - }); - } - - formArrayModel.groupFactory = () => { - return this.fromJSON(formArrayModel.groupPrototype); - }; - - formModel.push(new DynamicFormArrayModel(model, layout)); - break; - - case DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX: - formModel.push(new DynamicCheckboxModel(model, layout)); - break; - - case DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP: - model.group = this.fromJSON(model.group) as DynamicCheckboxModel[]; - formModel.push(new DynamicCheckboxGroupModel(model, layout)); - break; - - case DYNAMIC_FORM_CONTROL_TYPE_COLORPICKER: - formModel.push(new DynamicColorPickerModel(model, layout)); - break; - - case DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER: - formModel.push(new DynamicDatePickerModel(model, layout)); - break; - - case DYNAMIC_FORM_CONTROL_TYPE_EDITOR: - formModel.push(new DynamicEditorModel(model, layout)); - break; - - case DYNAMIC_FORM_CONTROL_TYPE_FILE_UPLOAD: - model.value = null; - formModel.push(new DynamicFileUploadModel(model, layout)); - break; - - case DYNAMIC_FORM_CONTROL_TYPE_GROUP: - model.group = this.fromJSON(model.group); - formModel.push(new DynamicFormGroupModel(model, layout)); - break; - - case DYNAMIC_FORM_CONTROL_TYPE_INPUT: - const inputModel = model as DynamicInputModel; - - if (inputModel.mask !== null) { - if (!(inputModel.mask instanceof Function)) { - inputModel.mask = maskFromString(inputModel.mask as string); - } - } - - formModel.push(new DynamicInputModel(model, layout)); - break; + formModel.push(new DynamicInputModel(model, layout)); + break; - case DYNAMIC_FORM_CONTROL_TYPE_RADIO_GROUP: - formModel.push(new DynamicRadioGroupModel(model, layout)); - break; + case DYNAMIC_FORM_CONTROL_TYPE_RADIO_GROUP: + formModel.push(new DynamicRadioGroupModel(model, layout)); + break; - case DYNAMIC_FORM_CONTROL_TYPE_RATING: - formModel.push(new DynamicRatingModel(model, layout)); - break; + case DYNAMIC_FORM_CONTROL_TYPE_RATING: + formModel.push(new DynamicRatingModel(model, layout)); + break; - case DYNAMIC_FORM_CONTROL_TYPE_SELECT: - formModel.push(new DynamicSelectModel(model, layout)); - break; + case DYNAMIC_FORM_CONTROL_TYPE_SELECT: + formModel.push(new DynamicSelectModel(model, layout)); + break; - case DYNAMIC_FORM_CONTROL_TYPE_SLIDER: - formModel.push(new DynamicSliderModel(model, layout)); - break; + case DYNAMIC_FORM_CONTROL_TYPE_SLIDER: + formModel.push(new DynamicSliderModel(model, layout)); + break; - case DYNAMIC_FORM_CONTROL_TYPE_SWITCH: - formModel.push(new DynamicSwitchModel(model, layout)); - break; + case DYNAMIC_FORM_CONTROL_TYPE_SWITCH: + formModel.push(new DynamicSwitchModel(model, layout)); + break; - case DYNAMIC_FORM_CONTROL_TYPE_TEXTAREA: - formModel.push(new DynamicTextAreaModel(model, layout)); - break; + case DYNAMIC_FORM_CONTROL_TYPE_TEXTAREA: + formModel.push(new DynamicTextAreaModel(model, layout)); + break; - case DYNAMIC_FORM_CONTROL_TYPE_TIMEPICKER: - formModel.push(new DynamicTimePickerModel(model, layout)); - break; + case DYNAMIC_FORM_CONTROL_TYPE_TIMEPICKER: + formModel.push(new DynamicTimePickerModel(model, layout)); + break; - default: - throw new Error(`unknown form control model type defined on JSON object with id "${model.id}"`); - } - }); + default: + throw new Error( + `unknown form control model type defined on JSON object with id "${model.id}"` + ); + } + }); - return formModel; - } + return formModel; + } } From 7292c578ee288c1a44c8fe319dddae06700cd6f2 Mon Sep 17 00:00:00 2001 From: stelios kontarinis Date: Thu, 18 Jun 2020 16:30:05 +0300 Subject: [PATCH 2/3] add test for dynamic-from.service findByPath method --- .../lib/service/dynamic-form.service.spec.ts | 1115 ++++++++++------- 1 file changed, 666 insertions(+), 449 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 286defa5b..cbea8beea 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 @@ -1,11 +1,11 @@ import { TestBed, inject } from "@angular/core/testing"; import { - ReactiveFormsModule, - FormGroup, - FormControl, - FormArray, - NG_VALIDATORS, - NG_ASYNC_VALIDATORS + ReactiveFormsModule, + FormGroup, + FormControl, + FormArray, + NG_VALIDATORS, + NG_ASYNC_VALIDATORS, } from "@angular/forms"; import { DynamicFormService } from "./dynamic-form.service"; import { DynamicFormComponentService } from "./dynamic-form-component.service"; @@ -32,449 +32,666 @@ import { DynamicTimePickerModel } from "../model/timepicker/dynamic-timepicker.m import { DynamicFormValueControlModel } from "../model/dynamic-form-value-control.model"; describe("DynamicFormService test suite", () => { - - let testModel: DynamicFormModel, - service: DynamicFormService; - - function testValidator() { - return {testValidator: {valid: true}}; - } - - function testAsyncValidator() { - return new Promise(resolve => setTimeout(() => resolve(true), 0)); - } - - beforeEach(() => { - - TestBed.configureTestingModule({ - imports: [ReactiveFormsModule], - providers: [ - DynamicFormService, - DynamicFormComponentService, - DynamicFormValidationService, - {provide: NG_VALIDATORS, useValue: testValidator, multi: true}, - {provide: NG_ASYNC_VALIDATORS, useValue: testAsyncValidator, multi: true} - ] - }); - - testModel = [ - - new DynamicSelectModel( - { - id: "testSelect", - options: [ - { - label: "Option 1", - value: "option-1" - }, - { - label: "Option 2", - value: "option-2" - } - ], - value: "option-3" - } - ), - - new DynamicInputModel( - { - id: "testInput", - mask: ["(", /[1-9]/, /\d/, /\d/, ")", " ", /\d/, /\d/, /\d/, "-", /\d/, /\d/, /\d/, /\d/], - } - ), - - new DynamicCheckboxGroupModel( - { - id: "testCheckboxGroup", - group: [ - new DynamicCheckboxModel( - { - id: "testCheckboxGroup1", - value: true - } - ), - new DynamicCheckboxModel( - { - id: "testCheckboxGroup2", - value: true - } - ) - ] - } - ), - - new DynamicRadioGroupModel( - { - id: "testRadioGroup", - options: [ - { - label: "Option 1", - value: "option-1" - }, - { - label: "Option 2", - value: "option-2" - } - ], - value: "option-3" - } - ), - - new DynamicTextAreaModel({id: "testTextArea"}), - - new DynamicCheckboxModel({id: "testCheckbox"}), - - new DynamicFormArrayModel( - { - id: "testFormArray", - initialCount: 5, - groupFactory: () => { - return [ - new DynamicInputModel({id: "testFormArrayGroupInput"}), - new DynamicFormArrayModel({ - id: "testNestedFormArray", groupFactory: () => [ - new DynamicInputModel({id: "testNestedFormArrayGroupInput"}) - ] - }) - ]; - } - } - ), - - new DynamicFormGroupModel( - { - id: "testFormGroup", - group: [ - new DynamicInputModel({id: "nestedTestInput"}), - new DynamicTextAreaModel({id: "nestedTestTextArea"}) - ] - } - ), - - new DynamicSliderModel({id: "testSlider"}), - - new DynamicSwitchModel({id: "testSwitch"}), - - new DynamicDatePickerModel({id: "testDatepicker", value: new Date()}), - - new DynamicFileUploadModel({id: "testFileUpload"}), - - new DynamicEditorModel({id: "testEditor"}), - - new DynamicTimePickerModel({id: "testTimePicker"}), - - new DynamicRatingModel({id: "testRating"}), - - new DynamicColorPickerModel({id: "testColorPicker"}), - ]; - }); - - - beforeEach(inject([DynamicFormService], (formService: DynamicFormService) => service = formService)); - - - it("should create create a form group", () => { - - let formGroup = service.createFormGroup(testModel); - - expect(formGroup instanceof FormGroup).toBe(true); - - expect(formGroup.get("testCheckbox") instanceof FormControl).toBe(true); - expect(formGroup.get("testCheckboxGroup") instanceof FormGroup).toBe(true); - expect(formGroup.get("testDatepicker") instanceof FormControl).toBe(true); - expect(formGroup.get("testFormArray") instanceof FormArray).toBe(true); - expect(formGroup.get("testInput") instanceof FormControl).toBe(true); - expect(formGroup.get("testRadioGroup") instanceof FormControl).toBe(true); - expect(formGroup.get("testSelect") instanceof FormControl).toBe(true); - expect(formGroup.get("testTextArea") instanceof FormControl).toBe(true); - expect(formGroup.get("testFileUpload") instanceof FormControl).toBe(true); - expect(formGroup.get("testEditor") instanceof FormControl).toBe(true); - expect(formGroup.get("testTimePicker") instanceof FormControl).toBe(true); - expect(formGroup.get("testRating") instanceof FormControl).toBe(true); - expect(formGroup.get("testColorPicker") instanceof FormControl).toBe(true); - }); - - - it("should parse dynamic form JSON", () => { - - let json = JSON.stringify(testModel), - formModel = service.fromJSON(json); - - expect(Array.isArray(formModel) as boolean).toBe(true); - - expect(formModel[0] instanceof DynamicSelectModel).toBe(true); - expect(formModel[1] instanceof DynamicInputModel).toBe(true); - expect(formModel[2] instanceof DynamicCheckboxGroupModel).toBe(true); - expect(formModel[3] instanceof DynamicRadioGroupModel).toBe(true); - expect(formModel[4] instanceof DynamicTextAreaModel).toBe(true); - expect(formModel[5] instanceof DynamicCheckboxModel).toBe(true); - expect(formModel[6] instanceof DynamicFormArrayModel).toBe(true); - expect(formModel[7] instanceof DynamicFormGroupModel).toBe(true); - expect(formModel[8] instanceof DynamicSliderModel).toBe(true); - expect(formModel[9] instanceof DynamicSwitchModel).toBe(true); - expect(formModel[10] instanceof DynamicDatePickerModel).toBe(true); - expect((formModel[10] as DynamicDateControlModel).value instanceof Date).toBe(true); - expect(formModel[11] instanceof DynamicFileUploadModel).toBe(true); - expect(formModel[12] instanceof DynamicEditorModel).toBe(true); - expect(formModel[13] instanceof DynamicTimePickerModel).toBe(true); - expect(formModel[14] instanceof DynamicRatingModel).toBe(true); - expect(formModel[15] instanceof DynamicColorPickerModel).toBe(true); - }); - - - it("should throw when unknown DynamicFormControlModel id is specified in JSON", () => { - - expect(() => service.fromJSON([{id: "test"}])) - .toThrow(new Error(`unknown form control model type defined on JSON object with id "test"`)); - }); - - - it("should find a dynamic form control model by id", () => { - - expect(service.findById("testCheckbox", testModel) instanceof DynamicFormControlModel).toBe(true); - expect(service.findById("testCheckboxGroup", testModel) instanceof DynamicFormControlModel).toBe(true); - expect(service.findById("testDatepicker", testModel) instanceof DynamicFormControlModel).toBe(true); - expect(service.findById("testFormArray", testModel) instanceof DynamicFormControlModel).toBe(true); - expect(service.findById("testInput", testModel) instanceof DynamicFormControlModel).toBe(true); - expect(service.findById("testRadioGroup", testModel) instanceof DynamicFormControlModel).toBe(true); - expect(service.findById("testSelect", testModel) instanceof DynamicFormControlModel).toBe(true); - expect(service.findById("testSlider", testModel) instanceof DynamicFormControlModel).toBe(true); - expect(service.findById("testSwitch", testModel) instanceof DynamicFormControlModel).toBe(true); - expect(service.findById("testTextArea", testModel) instanceof DynamicFormControlModel).toBe(true); - expect(service.findById("testFileUpload", testModel) instanceof DynamicFormControlModel).toBe(true); - expect(service.findById("testEditor", testModel) instanceof DynamicEditorModel).toBe(true); - expect(service.findById("testTimePicker", testModel) instanceof DynamicTimePickerModel).toBe(true); - expect(service.findById("testRating", testModel) instanceof DynamicRatingModel).toBe(true); - expect(service.findById("testColorPicker", testModel) instanceof DynamicColorPickerModel).toBe(true); - }); - - - it("should find a nested dynamic form control model by id", () => { - - expect(service.findById("testCheckboxGroup1", testModel) instanceof DynamicFormControlModel).toBe(true); - expect(service.findById("testCheckboxGroup2", testModel) instanceof DynamicFormControlModel).toBe(true); - expect(service.findById("nestedTestInput", testModel) instanceof DynamicFormControlModel).toBe(true); + let testModel: DynamicFormModel, service: DynamicFormService; + + function testValidator() { + return { testValidator: { valid: true } }; + } + + function testAsyncValidator() { + return new Promise(resolve => setTimeout(() => resolve(true), 0)); + } + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ReactiveFormsModule], + providers: [ + DynamicFormService, + DynamicFormComponentService, + DynamicFormValidationService, + { provide: NG_VALIDATORS, useValue: testValidator, multi: true }, + { + provide: NG_ASYNC_VALIDATORS, + useValue: testAsyncValidator, + multi: true, + }, + ], }); - - it("should resolve array group path", () => { - - service.createFormGroup(testModel); - - let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, - nestedModel = (model.get(0).get(1) as DynamicFormArrayModel).get(0); - - expect(service.getPath(model)).toEqual(["testFormArray"]); - expect(service.getPath(nestedModel)).toEqual(["testFormArray", "0", "testNestedFormArray", "0"]); - }); - - - it("should add a form control to an existing form group", () => { - - let formGroup = service.createFormGroup(testModel), - nestedFormGroup = formGroup.controls["testFormGroup"] as FormGroup, - nestedFormGroupModel = testModel[7] as DynamicFormGroupModel, - newModel1 = new DynamicInputModel({id: "newInput1"}), - newModel2 = new DynamicInputModel({id: "newInput2"}); - - service.addFormGroupControl(formGroup, testModel, newModel1); - service.addFormGroupControl(nestedFormGroup, nestedFormGroupModel, newModel2); - - expect(formGroup.controls[newModel1.id]).toBeTruthy(); - expect(testModel[testModel.length - 1] === newModel1).toBe(true); - - expect((formGroup.controls["testFormGroup"] as FormGroup).controls[newModel2.id]).toBeTruthy(); - expect(nestedFormGroupModel.get(nestedFormGroupModel.group.length - 1) === newModel2).toBe(true); - }); - - - it("should insert a form control to an existing form group", () => { - - let formGroup = service.createFormGroup(testModel), - nestedFormGroup = formGroup.controls["testFormGroup"] as FormGroup, - nestedFormGroupModel = testModel[7] as DynamicFormGroupModel, - newModel1 = new DynamicInputModel({id: "newInput1"}), - newModel2 = new DynamicInputModel({id: "newInput2"}); - - service.insertFormGroupControl(4, formGroup, testModel, newModel1); - service.insertFormGroupControl(0, nestedFormGroup, nestedFormGroupModel, newModel2); - - expect(formGroup.controls[newModel1.id]).toBeTruthy(); - expect(testModel[4] === newModel1).toBe(true); - expect(service.getPath(testModel[4])).toEqual(["newInput1"]); - - expect((formGroup.controls["testFormGroup"] as FormGroup).controls[newModel2.id]).toBeTruthy(); - expect(nestedFormGroupModel.get(0) === newModel2).toBe(true); - expect(service.getPath(nestedFormGroupModel.get(0))).toEqual(["testFormGroup", "newInput2"]); - }); - - - it("should move an existing form control to a different group position", () => { - - let formGroup = service.createFormGroup(testModel), - nestedFormGroupModel = testModel[7] as DynamicFormGroupModel, - model1 = testModel[0], - model2 = nestedFormGroupModel.get(0); - - service.moveFormGroupControl(0, 2, testModel); - - expect(formGroup.controls[model1.id]).toBeTruthy(); - expect(testModel[2] === model1).toBe(true); - - service.moveFormGroupControl(0, 1, nestedFormGroupModel); - - expect((formGroup.controls["testFormGroup"] as FormGroup).controls[model2.id]).toBeTruthy(); - expect(nestedFormGroupModel.get(1) === model2).toBe(true); - }); - - - it("should remove a form control from an existing form group", () => { - - let formGroup = service.createFormGroup(testModel), - nestedFormGroup = formGroup.controls["testFormGroup"] as FormGroup, - nestedFormGroupModel = testModel[7] as DynamicFormGroupModel, - length = testModel.length, - size = nestedFormGroupModel.size(), - index = 1, - id1 = testModel[index].id, - id2 = nestedFormGroupModel.get(index).id; - - service.removeFormGroupControl(index, formGroup, testModel); - - expect(Object.keys(formGroup.controls).length).toBe(length - 1); - expect(formGroup.controls[id1]).toBeUndefined(); - - expect(testModel.length).toBe(length - 1); - expect(service.findById(id1, testModel)).toBeNull(); - - service.removeFormGroupControl(index, nestedFormGroup, nestedFormGroupModel); - - expect(Object.keys(nestedFormGroup.controls).length).toBe(size - 1); - expect(nestedFormGroup.controls[id2]).toBeUndefined(); - - expect(nestedFormGroupModel.size()).toBe(size - 1); - expect(service.findById(id2, testModel)).toBeNull(); - }); - - - it("should create a form array", () => { - - let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, - formArray; - - expect(service.createFormArray).toBeTruthy(); - - formArray = service.createFormArray(model); - - expect(formArray instanceof FormArray).toBe(true); - expect(formArray.length).toBe(model.initialCount); - }); - - - it("should add a form array group", () => { - - let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, - formArray = service.createFormArray(model); - - service.addFormArrayGroup(formArray, model); - - expect(formArray.length).toBe(model.initialCount + 1); - }); - - - it("should insert a form array group", () => { - - let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, - formArray = service.createFormArray(model); - - service.insertFormArrayGroup(0, formArray, model); - - expect(formArray.length).toBe(model.initialCount + 1); - }); - - - it("should move up a form array group", () => { - - let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, - formArray = service.createFormArray(model), - index = 3, - step = 1; - - (formArray.at(index) as FormGroup).controls["testFormArrayGroupInput"].setValue("next test value 1"); - (formArray.at(index + step) as FormGroup).controls["testFormArrayGroupInput"].setValue("next test value 2"); - - (model.get(index).get(0) as DynamicFormValueControlModel).value = "next test value 1"; - (model.get(index + step).get(0) as DynamicFormValueControlModel).value = "next test value 2"; - - service.moveFormArrayGroup(index, step, formArray, model); - - expect(formArray.length).toBe(model.initialCount); - - expect((formArray.at(index) as FormGroup).controls["testFormArrayGroupInput"].value).toEqual("next test value 2"); - expect((formArray.at(index + step) as FormGroup).controls["testFormArrayGroupInput"].value).toEqual("next test value 1"); - - expect((model.get(index).get(0) as DynamicFormValueControlModel).value).toEqual("next test value 2"); - expect((model.get(index + step).get(0) as DynamicFormValueControlModel).value).toEqual("next test value 1"); - }); - - - it("should move down a form array group", () => { - - let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, - formArray = service.createFormArray(model), - index = 3, - step = -1; - - (formArray.at(index) as FormGroup).controls["testFormArrayGroupInput"].setValue("next test value 1"); - (formArray.at(index + step) as FormGroup).controls["testFormArrayGroupInput"].setValue("next test value 2"); - - (model.get(index).get(0) as DynamicFormValueControlModel).value = "next test value 1"; - (model.get(index + step).get(0) as DynamicFormValueControlModel).value = "next test value 2"; - - service.moveFormArrayGroup(index, step, formArray, model); - - expect(formArray.length).toBe(model.initialCount); - - expect((formArray.at(index) as FormGroup).controls["testFormArrayGroupInput"].value).toEqual("next test value 2"); - expect((formArray.at(index + step) as FormGroup).controls["testFormArrayGroupInput"].value).toEqual("next test value 1"); - - expect((model.get(index).get(0) as DynamicFormValueControlModel).value).toEqual("next test value 2"); - expect((model.get(index + step).get(0) as DynamicFormValueControlModel).value).toEqual("next test value 1"); - }); - - - it("should throw when form array group is to be moved out of bounds", () => { - - let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, - formArray = service.createFormArray(model); - - expect(() => service.moveFormArrayGroup(2, -5, formArray, model)) - .toThrow(new Error(`form array group cannot be moved due to index or new index being out of bounds`)); - }); - - - it("should remove a form array group", () => { - - let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, - formArray = service.createFormArray(model); - - service.removeFormArrayGroup(0, formArray, model); - - const newCount = model.initialCount - 1; - - expect(model.size).toBe(newCount); - expect(formArray.length).toBe(newCount); - }); - - - it("should clear a form array", () => { - - let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, - formArray = service.createFormArray(model); - - service.clearFormArray(formArray, model); - - expect(model.size === 0).toBe(true); - expect(formArray.length === 0).toBe(true); - }); + testModel = [ + new DynamicSelectModel({ + id: "testSelect", + options: [ + { + label: "Option 1", + value: "option-1", + }, + { + label: "Option 2", + value: "option-2", + }, + ], + value: "option-3", + }), + + new DynamicInputModel({ + id: "testInput", + mask: [ + "(", + /[1-9]/, + /\d/, + /\d/, + ")", + " ", + /\d/, + /\d/, + /\d/, + "-", + /\d/, + /\d/, + /\d/, + /\d/, + ], + }), + + new DynamicCheckboxGroupModel({ + id: "testCheckboxGroup", + group: [ + new DynamicCheckboxModel({ + id: "testCheckboxGroup1", + value: true, + }), + new DynamicCheckboxModel({ + id: "testCheckboxGroup2", + value: true, + }), + ], + }), + + new DynamicRadioGroupModel({ + id: "testRadioGroup", + options: [ + { + label: "Option 1", + value: "option-1", + }, + { + label: "Option 2", + value: "option-2", + }, + ], + value: "option-3", + }), + + new DynamicTextAreaModel({ id: "testTextArea" }), + + new DynamicCheckboxModel({ id: "testCheckbox" }), + + new DynamicFormArrayModel({ + id: "testFormArray", + initialCount: 5, + groupFactory: () => { + return [ + new DynamicInputModel({ id: "testFormArrayGroupInput" }), + new DynamicFormArrayModel({ + id: "testNestedFormArray", + initialCount: 3, + groupFactory: () => [ + new DynamicInputModel({ id: "testNestedFormArrayGroupInput" }), + ], + }), + ]; + }, + }), + + new DynamicFormGroupModel({ + id: "testFormGroup", + group: [ + new DynamicInputModel({ id: "nestedTestInput" }), + new DynamicTextAreaModel({ id: "nestedTestTextArea" }), + ], + }), + + new DynamicSliderModel({ id: "testSlider" }), + + new DynamicSwitchModel({ id: "testSwitch" }), + + new DynamicDatePickerModel({ id: "testDatepicker", value: new Date() }), + + new DynamicFileUploadModel({ id: "testFileUpload" }), + + new DynamicEditorModel({ id: "testEditor" }), + + new DynamicTimePickerModel({ id: "testTimePicker" }), + + new DynamicRatingModel({ id: "testRating" }), + + new DynamicColorPickerModel({ id: "testColorPicker" }), + ]; + }); + + beforeEach(inject( + [DynamicFormService], + (formService: DynamicFormService) => (service = formService) + )); + + it("should create create a form group", () => { + let formGroup = service.createFormGroup(testModel); + + expect(formGroup instanceof FormGroup).toBe(true); + + expect(formGroup.get("testCheckbox") instanceof FormControl).toBe(true); + expect(formGroup.get("testCheckboxGroup") instanceof FormGroup).toBe(true); + expect(formGroup.get("testDatepicker") instanceof FormControl).toBe(true); + expect(formGroup.get("testFormArray") instanceof FormArray).toBe(true); + expect(formGroup.get("testInput") instanceof FormControl).toBe(true); + expect(formGroup.get("testRadioGroup") instanceof FormControl).toBe(true); + expect(formGroup.get("testSelect") instanceof FormControl).toBe(true); + expect(formGroup.get("testTextArea") instanceof FormControl).toBe(true); + expect(formGroup.get("testFileUpload") instanceof FormControl).toBe(true); + expect(formGroup.get("testEditor") instanceof FormControl).toBe(true); + expect(formGroup.get("testTimePicker") instanceof FormControl).toBe(true); + expect(formGroup.get("testRating") instanceof FormControl).toBe(true); + expect(formGroup.get("testColorPicker") instanceof FormControl).toBe(true); + }); + + it("should parse dynamic form JSON", () => { + let json = JSON.stringify(testModel), + formModel = service.fromJSON(json); + + expect(Array.isArray(formModel) as boolean).toBe(true); + + expect(formModel[0] instanceof DynamicSelectModel).toBe(true); + expect(formModel[1] instanceof DynamicInputModel).toBe(true); + expect(formModel[2] instanceof DynamicCheckboxGroupModel).toBe(true); + expect(formModel[3] instanceof DynamicRadioGroupModel).toBe(true); + expect(formModel[4] instanceof DynamicTextAreaModel).toBe(true); + expect(formModel[5] instanceof DynamicCheckboxModel).toBe(true); + expect(formModel[6] instanceof DynamicFormArrayModel).toBe(true); + expect(formModel[7] instanceof DynamicFormGroupModel).toBe(true); + expect(formModel[8] instanceof DynamicSliderModel).toBe(true); + expect(formModel[9] instanceof DynamicSwitchModel).toBe(true); + expect(formModel[10] instanceof DynamicDatePickerModel).toBe(true); + expect( + (formModel[10] as DynamicDateControlModel).value instanceof Date + ).toBe(true); + expect(formModel[11] instanceof DynamicFileUploadModel).toBe(true); + expect(formModel[12] instanceof DynamicEditorModel).toBe(true); + expect(formModel[13] instanceof DynamicTimePickerModel).toBe(true); + expect(formModel[14] instanceof DynamicRatingModel).toBe(true); + expect(formModel[15] instanceof DynamicColorPickerModel).toBe(true); + }); + + it("should throw when unknown DynamicFormControlModel id is specified in JSON", () => { + expect(() => service.fromJSON([{ id: "test" }])).toThrow( + new Error( + `unknown form control model type defined on JSON object with id "test"` + ) + ); + }); + + it("should find a dynamic form control model by id", () => { + expect( + service.findById("testCheckbox", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findById("testCheckboxGroup", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findById("testDatepicker", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findById("testFormArray", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findById("testInput", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findById("testRadioGroup", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findById("testSelect", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findById("testSlider", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findById("testSwitch", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findById("testTextArea", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findById("testFileUpload", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findById("testEditor", testModel) instanceof DynamicEditorModel + ).toBe(true); + expect( + service.findById("testTimePicker", testModel) instanceof + DynamicTimePickerModel + ).toBe(true); + expect( + service.findById("testRating", testModel) instanceof DynamicRatingModel + ).toBe(true); + expect( + service.findById("testColorPicker", testModel) instanceof + DynamicColorPickerModel + ).toBe(true); + }); + + it("should find a dynamic form control model by path", () => { + expect( + service.findByPath("testCheckbox", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findByPath("testCheckboxGroup", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findByPath("testDatepicker", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findByPath("testFormArray", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findByPath("testInput", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findByPath("testRadioGroup", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findByPath("testSelect", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findByPath("testSlider", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findByPath("testSwitch", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findByPath("testTextArea", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findByPath("testFileUpload", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findByPath("testEditor", testModel) instanceof DynamicEditorModel + ).toBe(true); + expect( + service.findById("testTimePicker", testModel) instanceof + DynamicTimePickerModel + ).toBe(true); + expect( + service.findByPath("testRating", testModel) instanceof DynamicRatingModel + ).toBe(true); + expect( + service.findByPath("testColorPicker", testModel) instanceof + DynamicColorPickerModel + ).toBe(true); + expect( + service.findByPath("testFormGroup.nestedTestInput", testModel) instanceof + DynamicInputModel + ).toBe(true); + expect( + service.findByPath( + "testFormGroup.nestedTestTextArea", + testModel + ) instanceof DynamicTextAreaModel + ).toBe(true); + expect( + service.findByPath( + "testFormArray.0.testFormArrayGroupInput", + testModel + ) instanceof DynamicInputModel + ).toBe(true); + expect( + service.findByPath( + "testFormArray.1.testFormArrayGroupInput", + testModel + ) instanceof DynamicInputModel + ).toBe(true); + expect( + service.findByPath( + "testFormArray.0.testNestedFormArray.0.testNestedFormArrayGroupInput", + testModel + ) instanceof DynamicInputModel + ).toBe(true); + expect( + service.findByPath( + "testFormArray.1.testNestedFormArray.1.testNestedFormArrayGroupInput", + testModel + ) instanceof DynamicInputModel + ).toBe(true); + }); + + it("should find a nested dynamic form control model by id", () => { + expect( + service.findById("testCheckboxGroup1", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findById("testCheckboxGroup2", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + expect( + service.findById("nestedTestInput", testModel) instanceof + DynamicFormControlModel + ).toBe(true); + }); + + it("should resolve array group path", () => { + service.createFormGroup(testModel); + + let model = service.findById( + "testFormArray", + testModel + ) as DynamicFormArrayModel, + nestedModel = (model.get(0).get(1) as DynamicFormArrayModel).get(0); + + expect(service.getPath(model)).toEqual(["testFormArray"]); + expect(service.getPath(nestedModel)).toEqual([ + "testFormArray", + "0", + "testNestedFormArray", + "0", + ]); + }); + + it("should add a form control to an existing form group", () => { + let formGroup = service.createFormGroup(testModel), + nestedFormGroup = formGroup.controls["testFormGroup"] as FormGroup, + nestedFormGroupModel = testModel[7] as DynamicFormGroupModel, + newModel1 = new DynamicInputModel({ id: "newInput1" }), + newModel2 = new DynamicInputModel({ id: "newInput2" }); + + service.addFormGroupControl(formGroup, testModel, newModel1); + service.addFormGroupControl( + nestedFormGroup, + nestedFormGroupModel, + newModel2 + ); + + expect(formGroup.controls[newModel1.id]).toBeTruthy(); + expect(testModel[testModel.length - 1] === newModel1).toBe(true); + + expect( + (formGroup.controls["testFormGroup"] as FormGroup).controls[newModel2.id] + ).toBeTruthy(); + expect( + nestedFormGroupModel.get(nestedFormGroupModel.group.length - 1) === + newModel2 + ).toBe(true); + }); + + it("should insert a form control to an existing form group", () => { + let formGroup = service.createFormGroup(testModel), + nestedFormGroup = formGroup.controls["testFormGroup"] as FormGroup, + nestedFormGroupModel = testModel[7] as DynamicFormGroupModel, + newModel1 = new DynamicInputModel({ id: "newInput1" }), + newModel2 = new DynamicInputModel({ id: "newInput2" }); + + service.insertFormGroupControl(4, formGroup, testModel, newModel1); + service.insertFormGroupControl( + 0, + nestedFormGroup, + nestedFormGroupModel, + newModel2 + ); + + expect(formGroup.controls[newModel1.id]).toBeTruthy(); + expect(testModel[4] === newModel1).toBe(true); + expect(service.getPath(testModel[4])).toEqual(["newInput1"]); + + expect( + (formGroup.controls["testFormGroup"] as FormGroup).controls[newModel2.id] + ).toBeTruthy(); + expect(nestedFormGroupModel.get(0) === newModel2).toBe(true); + expect(service.getPath(nestedFormGroupModel.get(0))).toEqual([ + "testFormGroup", + "newInput2", + ]); + }); + + it("should move an existing form control to a different group position", () => { + let formGroup = service.createFormGroup(testModel), + nestedFormGroupModel = testModel[7] as DynamicFormGroupModel, + model1 = testModel[0], + model2 = nestedFormGroupModel.get(0); + + service.moveFormGroupControl(0, 2, testModel); + + expect(formGroup.controls[model1.id]).toBeTruthy(); + expect(testModel[2] === model1).toBe(true); + + service.moveFormGroupControl(0, 1, nestedFormGroupModel); + + expect( + (formGroup.controls["testFormGroup"] as FormGroup).controls[model2.id] + ).toBeTruthy(); + expect(nestedFormGroupModel.get(1) === model2).toBe(true); + }); + + it("should remove a form control from an existing form group", () => { + let formGroup = service.createFormGroup(testModel), + nestedFormGroup = formGroup.controls["testFormGroup"] as FormGroup, + nestedFormGroupModel = testModel[7] as DynamicFormGroupModel, + length = testModel.length, + size = nestedFormGroupModel.size(), + index = 1, + id1 = testModel[index].id, + id2 = nestedFormGroupModel.get(index).id; + + service.removeFormGroupControl(index, formGroup, testModel); + + expect(Object.keys(formGroup.controls).length).toBe(length - 1); + expect(formGroup.controls[id1]).toBeUndefined(); + + expect(testModel.length).toBe(length - 1); + expect(service.findById(id1, testModel)).toBeNull(); + + service.removeFormGroupControl( + index, + nestedFormGroup, + nestedFormGroupModel + ); + + expect(Object.keys(nestedFormGroup.controls).length).toBe(size - 1); + expect(nestedFormGroup.controls[id2]).toBeUndefined(); + + expect(nestedFormGroupModel.size()).toBe(size - 1); + expect(service.findById(id2, testModel)).toBeNull(); + }); + + it("should create a form array", () => { + let model = service.findById( + "testFormArray", + testModel + ) as DynamicFormArrayModel, + formArray; + + expect(service.createFormArray).toBeTruthy(); + + formArray = service.createFormArray(model); + + expect(formArray instanceof FormArray).toBe(true); + expect(formArray.length).toBe(model.initialCount); + }); + + it("should add a form array group", () => { + let model = service.findById( + "testFormArray", + testModel + ) as DynamicFormArrayModel, + formArray = service.createFormArray(model); + + service.addFormArrayGroup(formArray, model); + + expect(formArray.length).toBe(model.initialCount + 1); + }); + + it("should insert a form array group", () => { + let model = service.findById( + "testFormArray", + testModel + ) as DynamicFormArrayModel, + formArray = service.createFormArray(model); + + service.insertFormArrayGroup(0, formArray, model); + + expect(formArray.length).toBe(model.initialCount + 1); + }); + + it("should move up a form array group", () => { + let model = service.findById( + "testFormArray", + testModel + ) as DynamicFormArrayModel, + formArray = service.createFormArray(model), + index = 3, + step = 1; + + (formArray.at(index) as FormGroup).controls[ + "testFormArrayGroupInput" + ].setValue("next test value 1"); + (formArray.at(index + step) as FormGroup).controls[ + "testFormArrayGroupInput" + ].setValue("next test value 2"); + + (model.get(index).get(0) as DynamicFormValueControlModel).value = + "next test value 1"; + (model.get(index + step).get(0) as DynamicFormValueControlModel< + any + >).value = "next test value 2"; + + service.moveFormArrayGroup(index, step, formArray, model); + + expect(formArray.length).toBe(model.initialCount); + + expect( + (formArray.at(index) as FormGroup).controls["testFormArrayGroupInput"] + .value + ).toEqual("next test value 2"); + expect( + (formArray.at(index + step) as FormGroup).controls[ + "testFormArrayGroupInput" + ].value + ).toEqual("next test value 1"); + + expect( + (model.get(index).get(0) as DynamicFormValueControlModel).value + ).toEqual("next test value 2"); + expect( + (model.get(index + step).get(0) as DynamicFormValueControlModel) + .value + ).toEqual("next test value 1"); + }); + + it("should move down a form array group", () => { + let model = service.findById( + "testFormArray", + testModel + ) as DynamicFormArrayModel, + formArray = service.createFormArray(model), + index = 3, + step = -1; + + (formArray.at(index) as FormGroup).controls[ + "testFormArrayGroupInput" + ].setValue("next test value 1"); + (formArray.at(index + step) as FormGroup).controls[ + "testFormArrayGroupInput" + ].setValue("next test value 2"); + + (model.get(index).get(0) as DynamicFormValueControlModel).value = + "next test value 1"; + (model.get(index + step).get(0) as DynamicFormValueControlModel< + any + >).value = "next test value 2"; + + service.moveFormArrayGroup(index, step, formArray, model); + + expect(formArray.length).toBe(model.initialCount); + + expect( + (formArray.at(index) as FormGroup).controls["testFormArrayGroupInput"] + .value + ).toEqual("next test value 2"); + expect( + (formArray.at(index + step) as FormGroup).controls[ + "testFormArrayGroupInput" + ].value + ).toEqual("next test value 1"); + + expect( + (model.get(index).get(0) as DynamicFormValueControlModel).value + ).toEqual("next test value 2"); + expect( + (model.get(index + step).get(0) as DynamicFormValueControlModel) + .value + ).toEqual("next test value 1"); + }); + + it("should throw when form array group is to be moved out of bounds", () => { + let model = service.findById( + "testFormArray", + testModel + ) as DynamicFormArrayModel, + formArray = service.createFormArray(model); + + expect(() => service.moveFormArrayGroup(2, -5, formArray, model)).toThrow( + new Error( + `form array group cannot be moved due to index or new index being out of bounds` + ) + ); + }); + + it("should remove a form array group", () => { + let model = service.findById( + "testFormArray", + testModel + ) as DynamicFormArrayModel, + formArray = service.createFormArray(model); + + service.removeFormArrayGroup(0, formArray, model); + + const newCount = model.initialCount - 1; + + expect(model.size).toBe(newCount); + expect(formArray.length).toBe(newCount); + }); + + it("should clear a form array", () => { + let model = service.findById( + "testFormArray", + testModel + ) as DynamicFormArrayModel, + formArray = service.createFormArray(model); + + service.clearFormArray(formArray, model); + + expect(model.size === 0).toBe(true); + expect(formArray.length === 0).toBe(true); + }); }); From 9aea1aff89ad32d882e7d108b8e631e708259846 Mon Sep 17 00:00:00 2001 From: stelios kontarinis Date: Thu, 18 Jun 2020 16:40:18 +0300 Subject: [PATCH 3/3] format dynamic-form.service & .spec --- .../lib/service/dynamic-form.service.spec.ts | 1141 +++++++---------- .../src/lib/service/dynamic-form.service.ts | 913 ++++++------- 2 files changed, 878 insertions(+), 1176 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 cbea8beea..c2ec32496 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 @@ -1,11 +1,11 @@ import { TestBed, inject } from "@angular/core/testing"; import { - ReactiveFormsModule, - FormGroup, - FormControl, - FormArray, - NG_VALIDATORS, - NG_ASYNC_VALIDATORS, + ReactiveFormsModule, + FormGroup, + FormControl, + FormArray, + NG_VALIDATORS, + NG_ASYNC_VALIDATORS } from "@angular/forms"; import { DynamicFormService } from "./dynamic-form.service"; import { DynamicFormComponentService } from "./dynamic-form-component.service"; @@ -32,666 +32,475 @@ import { DynamicTimePickerModel } from "../model/timepicker/dynamic-timepicker.m import { DynamicFormValueControlModel } from "../model/dynamic-form-value-control.model"; describe("DynamicFormService test suite", () => { - let testModel: DynamicFormModel, service: DynamicFormService; - - function testValidator() { - return { testValidator: { valid: true } }; - } - - function testAsyncValidator() { - return new Promise(resolve => setTimeout(() => resolve(true), 0)); - } - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ReactiveFormsModule], - providers: [ - DynamicFormService, - DynamicFormComponentService, - DynamicFormValidationService, - { provide: NG_VALIDATORS, useValue: testValidator, multi: true }, - { - provide: NG_ASYNC_VALIDATORS, - useValue: testAsyncValidator, - multi: true, - }, - ], + + let testModel: DynamicFormModel, + service: DynamicFormService; + + function testValidator() { + return {testValidator: {valid: true}}; + } + + function testAsyncValidator() { + return new Promise(resolve => setTimeout(() => resolve(true), 0)); + } + + beforeEach(() => { + + TestBed.configureTestingModule({ + imports: [ReactiveFormsModule], + providers: [ + DynamicFormService, + DynamicFormComponentService, + DynamicFormValidationService, + {provide: NG_VALIDATORS, useValue: testValidator, multi: true}, + {provide: NG_ASYNC_VALIDATORS, useValue: testAsyncValidator, multi: true} + ] + }); + + testModel = [ + + new DynamicSelectModel( + { + id: "testSelect", + options: [ + { + label: "Option 1", + value: "option-1" + }, + { + label: "Option 2", + value: "option-2" + } + ], + value: "option-3" + } + ), + + new DynamicInputModel( + { + id: "testInput", + mask: ["(", /[1-9]/, /\d/, /\d/, ")", " ", /\d/, /\d/, /\d/, "-", /\d/, /\d/, /\d/, /\d/], + } + ), + + new DynamicCheckboxGroupModel( + { + id: "testCheckboxGroup", + group: [ + new DynamicCheckboxModel( + { + id: "testCheckboxGroup1", + value: true + } + ), + new DynamicCheckboxModel( + { + id: "testCheckboxGroup2", + value: true + } + ) + ] + } + ), + + new DynamicRadioGroupModel( + { + id: "testRadioGroup", + options: [ + { + label: "Option 1", + value: "option-1" + }, + { + label: "Option 2", + value: "option-2" + } + ], + value: "option-3" + } + ), + + new DynamicTextAreaModel({id: "testTextArea"}), + + new DynamicCheckboxModel({id: "testCheckbox"}), + + new DynamicFormArrayModel( + { + id: "testFormArray", + initialCount: 5, + groupFactory: () => { + return [ + new DynamicInputModel({id: "testFormArrayGroupInput"}), + new DynamicFormArrayModel({ + id: "testNestedFormArray", + initialCount: 3, + groupFactory: () => [ + new DynamicInputModel({id: "testNestedFormArrayGroupInput"}) + ] + }) + ]; + } + } + ), + + new DynamicFormGroupModel( + { + id: "testFormGroup", + group: [ + new DynamicInputModel({id: "nestedTestInput"}), + new DynamicTextAreaModel({id: "nestedTestTextArea"}) + ] + } + ), + + new DynamicSliderModel({id: "testSlider"}), + + new DynamicSwitchModel({id: "testSwitch"}), + + new DynamicDatePickerModel({id: "testDatepicker", value: new Date()}), + + new DynamicFileUploadModel({id: "testFileUpload"}), + + new DynamicEditorModel({id: "testEditor"}), + + new DynamicTimePickerModel({id: "testTimePicker"}), + + new DynamicRatingModel({id: "testRating"}), + + new DynamicColorPickerModel({id: "testColorPicker"}), + ]; + }); + + + beforeEach(inject([DynamicFormService], (formService: DynamicFormService) => service = formService)); + + + it("should create create a form group", () => { + + let formGroup = service.createFormGroup(testModel); + + expect(formGroup instanceof FormGroup).toBe(true); + + expect(formGroup.get("testCheckbox") instanceof FormControl).toBe(true); + expect(formGroup.get("testCheckboxGroup") instanceof FormGroup).toBe(true); + expect(formGroup.get("testDatepicker") instanceof FormControl).toBe(true); + expect(formGroup.get("testFormArray") instanceof FormArray).toBe(true); + expect(formGroup.get("testInput") instanceof FormControl).toBe(true); + expect(formGroup.get("testRadioGroup") instanceof FormControl).toBe(true); + expect(formGroup.get("testSelect") instanceof FormControl).toBe(true); + expect(formGroup.get("testTextArea") instanceof FormControl).toBe(true); + expect(formGroup.get("testFileUpload") instanceof FormControl).toBe(true); + expect(formGroup.get("testEditor") instanceof FormControl).toBe(true); + expect(formGroup.get("testTimePicker") instanceof FormControl).toBe(true); + expect(formGroup.get("testRating") instanceof FormControl).toBe(true); + expect(formGroup.get("testColorPicker") instanceof FormControl).toBe(true); + }); + + + it("should parse dynamic form JSON", () => { + + let json = JSON.stringify(testModel), + formModel = service.fromJSON(json); + + expect(Array.isArray(formModel) as boolean).toBe(true); + + expect(formModel[0] instanceof DynamicSelectModel).toBe(true); + expect(formModel[1] instanceof DynamicInputModel).toBe(true); + expect(formModel[2] instanceof DynamicCheckboxGroupModel).toBe(true); + expect(formModel[3] instanceof DynamicRadioGroupModel).toBe(true); + expect(formModel[4] instanceof DynamicTextAreaModel).toBe(true); + expect(formModel[5] instanceof DynamicCheckboxModel).toBe(true); + expect(formModel[6] instanceof DynamicFormArrayModel).toBe(true); + expect(formModel[7] instanceof DynamicFormGroupModel).toBe(true); + expect(formModel[8] instanceof DynamicSliderModel).toBe(true); + expect(formModel[9] instanceof DynamicSwitchModel).toBe(true); + expect(formModel[10] instanceof DynamicDatePickerModel).toBe(true); + expect((formModel[10] as DynamicDateControlModel).value instanceof Date).toBe(true); + expect(formModel[11] instanceof DynamicFileUploadModel).toBe(true); + expect(formModel[12] instanceof DynamicEditorModel).toBe(true); + expect(formModel[13] instanceof DynamicTimePickerModel).toBe(true); + expect(formModel[14] instanceof DynamicRatingModel).toBe(true); + expect(formModel[15] instanceof DynamicColorPickerModel).toBe(true); + }); + + + it("should throw when unknown DynamicFormControlModel id is specified in JSON", () => { + + expect(() => service.fromJSON([{id: "test"}])) + .toThrow(new Error(`unknown form control model type defined on JSON object with id "test"`)); + }); + + + it("should find a dynamic form control model by id", () => { + + expect(service.findById("testCheckbox", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findById("testCheckboxGroup", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findById("testDatepicker", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findById("testFormArray", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findById("testInput", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findById("testRadioGroup", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findById("testSelect", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findById("testSlider", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findById("testSwitch", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findById("testTextArea", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findById("testFileUpload", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findById("testEditor", testModel) instanceof DynamicEditorModel).toBe(true); + expect(service.findById("testTimePicker", testModel) instanceof DynamicTimePickerModel).toBe(true); + expect(service.findById("testRating", testModel) instanceof DynamicRatingModel).toBe(true); + expect(service.findById("testColorPicker", testModel) instanceof DynamicColorPickerModel).toBe(true); + }); + + + it("should find a nested dynamic form control model by id", () => { + + expect(service.findById("testCheckboxGroup1", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findById("testCheckboxGroup2", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findById("nestedTestInput", testModel) instanceof DynamicFormControlModel).toBe(true); + }); + + it("should find a dynamic form control model by path", () => { + expect(service.findByPath("testCheckbox", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findByPath("testCheckboxGroup", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findByPath("testDatepicker", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findByPath("testFormArray", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findByPath("testInput", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findByPath("testRadioGroup", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findByPath("testSelect", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findByPath("testSlider", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findByPath("testSwitch", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findByPath("testTextArea", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findByPath("testFileUpload", testModel) instanceof DynamicFormControlModel).toBe(true); + expect(service.findByPath("testEditor", testModel) instanceof DynamicEditorModel).toBe(true); + expect(service.findById("testTimePicker", testModel) instanceof DynamicTimePickerModel).toBe(true); + expect(service.findByPath("testRating", testModel) instanceof DynamicRatingModel ).toBe(true); + expect(service.findByPath("testColorPicker", testModel) instanceof DynamicColorPickerModel).toBe(true); + expect(service.findByPath("testFormGroup.nestedTestInput", testModel) instanceof DynamicInputModel).toBe(true); + expect(service.findByPath("testFormGroup.nestedTestTextArea",testModel) instanceof DynamicTextAreaModel).toBe(true); + expect(service.findByPath("testFormArray.0.testFormArrayGroupInput",testModel) instanceof DynamicInputModel).toBe(true); + expect(service.findByPath("testFormArray.1.testFormArrayGroupInput",testModel) instanceof DynamicInputModel).toBe(true); + expect(service.findByPath("testFormArray.0.testNestedFormArray.0.testNestedFormArrayGroupInput",testModel) instanceof DynamicInputModel).toBe(true); + expect(service.findByPath("testFormArray.1.testNestedFormArray.1.testNestedFormArrayGroupInput",testModel) instanceof DynamicInputModel).toBe(true); + }); + + + it("should resolve array group path", () => { + + service.createFormGroup(testModel); + + let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, + nestedModel = (model.get(0).get(1) as DynamicFormArrayModel).get(0); + + expect(service.getPath(model)).toEqual(["testFormArray"]); + expect(service.getPath(nestedModel)).toEqual(["testFormArray", "0", "testNestedFormArray", "0"]); + }); + + + it("should add a form control to an existing form group", () => { + + let formGroup = service.createFormGroup(testModel), + nestedFormGroup = formGroup.controls["testFormGroup"] as FormGroup, + nestedFormGroupModel = testModel[7] as DynamicFormGroupModel, + newModel1 = new DynamicInputModel({id: "newInput1"}), + newModel2 = new DynamicInputModel({id: "newInput2"}); + + service.addFormGroupControl(formGroup, testModel, newModel1); + service.addFormGroupControl(nestedFormGroup, nestedFormGroupModel, newModel2); + + expect(formGroup.controls[newModel1.id]).toBeTruthy(); + expect(testModel[testModel.length - 1] === newModel1).toBe(true); + + expect((formGroup.controls["testFormGroup"] as FormGroup).controls[newModel2.id]).toBeTruthy(); + expect(nestedFormGroupModel.get(nestedFormGroupModel.group.length - 1) === newModel2).toBe(true); + }); + + + it("should insert a form control to an existing form group", () => { + + let formGroup = service.createFormGroup(testModel), + nestedFormGroup = formGroup.controls["testFormGroup"] as FormGroup, + nestedFormGroupModel = testModel[7] as DynamicFormGroupModel, + newModel1 = new DynamicInputModel({id: "newInput1"}), + newModel2 = new DynamicInputModel({id: "newInput2"}); + + service.insertFormGroupControl(4, formGroup, testModel, newModel1); + service.insertFormGroupControl(0, nestedFormGroup, nestedFormGroupModel, newModel2); + + expect(formGroup.controls[newModel1.id]).toBeTruthy(); + expect(testModel[4] === newModel1).toBe(true); + expect(service.getPath(testModel[4])).toEqual(["newInput1"]); + + expect((formGroup.controls["testFormGroup"] as FormGroup).controls[newModel2.id]).toBeTruthy(); + expect(nestedFormGroupModel.get(0) === newModel2).toBe(true); + expect(service.getPath(nestedFormGroupModel.get(0))).toEqual(["testFormGroup", "newInput2"]); }); - testModel = [ - new DynamicSelectModel({ - id: "testSelect", - options: [ - { - label: "Option 1", - value: "option-1", - }, - { - label: "Option 2", - value: "option-2", - }, - ], - value: "option-3", - }), - - new DynamicInputModel({ - id: "testInput", - mask: [ - "(", - /[1-9]/, - /\d/, - /\d/, - ")", - " ", - /\d/, - /\d/, - /\d/, - "-", - /\d/, - /\d/, - /\d/, - /\d/, - ], - }), - - new DynamicCheckboxGroupModel({ - id: "testCheckboxGroup", - group: [ - new DynamicCheckboxModel({ - id: "testCheckboxGroup1", - value: true, - }), - new DynamicCheckboxModel({ - id: "testCheckboxGroup2", - value: true, - }), - ], - }), - - new DynamicRadioGroupModel({ - id: "testRadioGroup", - options: [ - { - label: "Option 1", - value: "option-1", - }, - { - label: "Option 2", - value: "option-2", - }, - ], - value: "option-3", - }), - - new DynamicTextAreaModel({ id: "testTextArea" }), - - new DynamicCheckboxModel({ id: "testCheckbox" }), - - new DynamicFormArrayModel({ - id: "testFormArray", - initialCount: 5, - groupFactory: () => { - return [ - new DynamicInputModel({ id: "testFormArrayGroupInput" }), - new DynamicFormArrayModel({ - id: "testNestedFormArray", - initialCount: 3, - groupFactory: () => [ - new DynamicInputModel({ id: "testNestedFormArrayGroupInput" }), - ], - }), - ]; - }, - }), - - new DynamicFormGroupModel({ - id: "testFormGroup", - group: [ - new DynamicInputModel({ id: "nestedTestInput" }), - new DynamicTextAreaModel({ id: "nestedTestTextArea" }), - ], - }), - - new DynamicSliderModel({ id: "testSlider" }), - - new DynamicSwitchModel({ id: "testSwitch" }), - - new DynamicDatePickerModel({ id: "testDatepicker", value: new Date() }), - - new DynamicFileUploadModel({ id: "testFileUpload" }), - - new DynamicEditorModel({ id: "testEditor" }), - - new DynamicTimePickerModel({ id: "testTimePicker" }), - - new DynamicRatingModel({ id: "testRating" }), - - new DynamicColorPickerModel({ id: "testColorPicker" }), - ]; - }); - - beforeEach(inject( - [DynamicFormService], - (formService: DynamicFormService) => (service = formService) - )); - - it("should create create a form group", () => { - let formGroup = service.createFormGroup(testModel); - - expect(formGroup instanceof FormGroup).toBe(true); - - expect(formGroup.get("testCheckbox") instanceof FormControl).toBe(true); - expect(formGroup.get("testCheckboxGroup") instanceof FormGroup).toBe(true); - expect(formGroup.get("testDatepicker") instanceof FormControl).toBe(true); - expect(formGroup.get("testFormArray") instanceof FormArray).toBe(true); - expect(formGroup.get("testInput") instanceof FormControl).toBe(true); - expect(formGroup.get("testRadioGroup") instanceof FormControl).toBe(true); - expect(formGroup.get("testSelect") instanceof FormControl).toBe(true); - expect(formGroup.get("testTextArea") instanceof FormControl).toBe(true); - expect(formGroup.get("testFileUpload") instanceof FormControl).toBe(true); - expect(formGroup.get("testEditor") instanceof FormControl).toBe(true); - expect(formGroup.get("testTimePicker") instanceof FormControl).toBe(true); - expect(formGroup.get("testRating") instanceof FormControl).toBe(true); - expect(formGroup.get("testColorPicker") instanceof FormControl).toBe(true); - }); - - it("should parse dynamic form JSON", () => { - let json = JSON.stringify(testModel), - formModel = service.fromJSON(json); - - expect(Array.isArray(formModel) as boolean).toBe(true); - - expect(formModel[0] instanceof DynamicSelectModel).toBe(true); - expect(formModel[1] instanceof DynamicInputModel).toBe(true); - expect(formModel[2] instanceof DynamicCheckboxGroupModel).toBe(true); - expect(formModel[3] instanceof DynamicRadioGroupModel).toBe(true); - expect(formModel[4] instanceof DynamicTextAreaModel).toBe(true); - expect(formModel[5] instanceof DynamicCheckboxModel).toBe(true); - expect(formModel[6] instanceof DynamicFormArrayModel).toBe(true); - expect(formModel[7] instanceof DynamicFormGroupModel).toBe(true); - expect(formModel[8] instanceof DynamicSliderModel).toBe(true); - expect(formModel[9] instanceof DynamicSwitchModel).toBe(true); - expect(formModel[10] instanceof DynamicDatePickerModel).toBe(true); - expect( - (formModel[10] as DynamicDateControlModel).value instanceof Date - ).toBe(true); - expect(formModel[11] instanceof DynamicFileUploadModel).toBe(true); - expect(formModel[12] instanceof DynamicEditorModel).toBe(true); - expect(formModel[13] instanceof DynamicTimePickerModel).toBe(true); - expect(formModel[14] instanceof DynamicRatingModel).toBe(true); - expect(formModel[15] instanceof DynamicColorPickerModel).toBe(true); - }); - - it("should throw when unknown DynamicFormControlModel id is specified in JSON", () => { - expect(() => service.fromJSON([{ id: "test" }])).toThrow( - new Error( - `unknown form control model type defined on JSON object with id "test"` - ) - ); - }); - - it("should find a dynamic form control model by id", () => { - expect( - service.findById("testCheckbox", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findById("testCheckboxGroup", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findById("testDatepicker", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findById("testFormArray", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findById("testInput", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findById("testRadioGroup", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findById("testSelect", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findById("testSlider", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findById("testSwitch", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findById("testTextArea", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findById("testFileUpload", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findById("testEditor", testModel) instanceof DynamicEditorModel - ).toBe(true); - expect( - service.findById("testTimePicker", testModel) instanceof - DynamicTimePickerModel - ).toBe(true); - expect( - service.findById("testRating", testModel) instanceof DynamicRatingModel - ).toBe(true); - expect( - service.findById("testColorPicker", testModel) instanceof - DynamicColorPickerModel - ).toBe(true); - }); - - it("should find a dynamic form control model by path", () => { - expect( - service.findByPath("testCheckbox", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findByPath("testCheckboxGroup", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findByPath("testDatepicker", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findByPath("testFormArray", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findByPath("testInput", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findByPath("testRadioGroup", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findByPath("testSelect", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findByPath("testSlider", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findByPath("testSwitch", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findByPath("testTextArea", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findByPath("testFileUpload", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findByPath("testEditor", testModel) instanceof DynamicEditorModel - ).toBe(true); - expect( - service.findById("testTimePicker", testModel) instanceof - DynamicTimePickerModel - ).toBe(true); - expect( - service.findByPath("testRating", testModel) instanceof DynamicRatingModel - ).toBe(true); - expect( - service.findByPath("testColorPicker", testModel) instanceof - DynamicColorPickerModel - ).toBe(true); - expect( - service.findByPath("testFormGroup.nestedTestInput", testModel) instanceof - DynamicInputModel - ).toBe(true); - expect( - service.findByPath( - "testFormGroup.nestedTestTextArea", - testModel - ) instanceof DynamicTextAreaModel - ).toBe(true); - expect( - service.findByPath( - "testFormArray.0.testFormArrayGroupInput", - testModel - ) instanceof DynamicInputModel - ).toBe(true); - expect( - service.findByPath( - "testFormArray.1.testFormArrayGroupInput", - testModel - ) instanceof DynamicInputModel - ).toBe(true); - expect( - service.findByPath( - "testFormArray.0.testNestedFormArray.0.testNestedFormArrayGroupInput", - testModel - ) instanceof DynamicInputModel - ).toBe(true); - expect( - service.findByPath( - "testFormArray.1.testNestedFormArray.1.testNestedFormArrayGroupInput", - testModel - ) instanceof DynamicInputModel - ).toBe(true); - }); - - it("should find a nested dynamic form control model by id", () => { - expect( - service.findById("testCheckboxGroup1", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findById("testCheckboxGroup2", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - expect( - service.findById("nestedTestInput", testModel) instanceof - DynamicFormControlModel - ).toBe(true); - }); - - it("should resolve array group path", () => { - service.createFormGroup(testModel); - - let model = service.findById( - "testFormArray", - testModel - ) as DynamicFormArrayModel, - nestedModel = (model.get(0).get(1) as DynamicFormArrayModel).get(0); - - expect(service.getPath(model)).toEqual(["testFormArray"]); - expect(service.getPath(nestedModel)).toEqual([ - "testFormArray", - "0", - "testNestedFormArray", - "0", - ]); - }); - - it("should add a form control to an existing form group", () => { - let formGroup = service.createFormGroup(testModel), - nestedFormGroup = formGroup.controls["testFormGroup"] as FormGroup, - nestedFormGroupModel = testModel[7] as DynamicFormGroupModel, - newModel1 = new DynamicInputModel({ id: "newInput1" }), - newModel2 = new DynamicInputModel({ id: "newInput2" }); - - service.addFormGroupControl(formGroup, testModel, newModel1); - service.addFormGroupControl( - nestedFormGroup, - nestedFormGroupModel, - newModel2 - ); - - expect(formGroup.controls[newModel1.id]).toBeTruthy(); - expect(testModel[testModel.length - 1] === newModel1).toBe(true); - - expect( - (formGroup.controls["testFormGroup"] as FormGroup).controls[newModel2.id] - ).toBeTruthy(); - expect( - nestedFormGroupModel.get(nestedFormGroupModel.group.length - 1) === - newModel2 - ).toBe(true); - }); - - it("should insert a form control to an existing form group", () => { - let formGroup = service.createFormGroup(testModel), - nestedFormGroup = formGroup.controls["testFormGroup"] as FormGroup, - nestedFormGroupModel = testModel[7] as DynamicFormGroupModel, - newModel1 = new DynamicInputModel({ id: "newInput1" }), - newModel2 = new DynamicInputModel({ id: "newInput2" }); - - service.insertFormGroupControl(4, formGroup, testModel, newModel1); - service.insertFormGroupControl( - 0, - nestedFormGroup, - nestedFormGroupModel, - newModel2 - ); - - expect(formGroup.controls[newModel1.id]).toBeTruthy(); - expect(testModel[4] === newModel1).toBe(true); - expect(service.getPath(testModel[4])).toEqual(["newInput1"]); - - expect( - (formGroup.controls["testFormGroup"] as FormGroup).controls[newModel2.id] - ).toBeTruthy(); - expect(nestedFormGroupModel.get(0) === newModel2).toBe(true); - expect(service.getPath(nestedFormGroupModel.get(0))).toEqual([ - "testFormGroup", - "newInput2", - ]); - }); - - it("should move an existing form control to a different group position", () => { - let formGroup = service.createFormGroup(testModel), - nestedFormGroupModel = testModel[7] as DynamicFormGroupModel, - model1 = testModel[0], - model2 = nestedFormGroupModel.get(0); - - service.moveFormGroupControl(0, 2, testModel); - - expect(formGroup.controls[model1.id]).toBeTruthy(); - expect(testModel[2] === model1).toBe(true); - - service.moveFormGroupControl(0, 1, nestedFormGroupModel); - - expect( - (formGroup.controls["testFormGroup"] as FormGroup).controls[model2.id] - ).toBeTruthy(); - expect(nestedFormGroupModel.get(1) === model2).toBe(true); - }); - - it("should remove a form control from an existing form group", () => { - let formGroup = service.createFormGroup(testModel), - nestedFormGroup = formGroup.controls["testFormGroup"] as FormGroup, - nestedFormGroupModel = testModel[7] as DynamicFormGroupModel, - length = testModel.length, - size = nestedFormGroupModel.size(), - index = 1, - id1 = testModel[index].id, - id2 = nestedFormGroupModel.get(index).id; - - service.removeFormGroupControl(index, formGroup, testModel); - - expect(Object.keys(formGroup.controls).length).toBe(length - 1); - expect(formGroup.controls[id1]).toBeUndefined(); - - expect(testModel.length).toBe(length - 1); - expect(service.findById(id1, testModel)).toBeNull(); - - service.removeFormGroupControl( - index, - nestedFormGroup, - nestedFormGroupModel - ); - - expect(Object.keys(nestedFormGroup.controls).length).toBe(size - 1); - expect(nestedFormGroup.controls[id2]).toBeUndefined(); - - expect(nestedFormGroupModel.size()).toBe(size - 1); - expect(service.findById(id2, testModel)).toBeNull(); - }); - - it("should create a form array", () => { - let model = service.findById( - "testFormArray", - testModel - ) as DynamicFormArrayModel, - formArray; - - expect(service.createFormArray).toBeTruthy(); - - formArray = service.createFormArray(model); - - expect(formArray instanceof FormArray).toBe(true); - expect(formArray.length).toBe(model.initialCount); - }); - - it("should add a form array group", () => { - let model = service.findById( - "testFormArray", - testModel - ) as DynamicFormArrayModel, - formArray = service.createFormArray(model); - - service.addFormArrayGroup(formArray, model); - - expect(formArray.length).toBe(model.initialCount + 1); - }); - - it("should insert a form array group", () => { - let model = service.findById( - "testFormArray", - testModel - ) as DynamicFormArrayModel, - formArray = service.createFormArray(model); - - service.insertFormArrayGroup(0, formArray, model); - - expect(formArray.length).toBe(model.initialCount + 1); - }); - - it("should move up a form array group", () => { - let model = service.findById( - "testFormArray", - testModel - ) as DynamicFormArrayModel, - formArray = service.createFormArray(model), - index = 3, - step = 1; - - (formArray.at(index) as FormGroup).controls[ - "testFormArrayGroupInput" - ].setValue("next test value 1"); - (formArray.at(index + step) as FormGroup).controls[ - "testFormArrayGroupInput" - ].setValue("next test value 2"); - - (model.get(index).get(0) as DynamicFormValueControlModel).value = - "next test value 1"; - (model.get(index + step).get(0) as DynamicFormValueControlModel< - any - >).value = "next test value 2"; - - service.moveFormArrayGroup(index, step, formArray, model); - - expect(formArray.length).toBe(model.initialCount); - - expect( - (formArray.at(index) as FormGroup).controls["testFormArrayGroupInput"] - .value - ).toEqual("next test value 2"); - expect( - (formArray.at(index + step) as FormGroup).controls[ - "testFormArrayGroupInput" - ].value - ).toEqual("next test value 1"); - - expect( - (model.get(index).get(0) as DynamicFormValueControlModel).value - ).toEqual("next test value 2"); - expect( - (model.get(index + step).get(0) as DynamicFormValueControlModel) - .value - ).toEqual("next test value 1"); - }); - - it("should move down a form array group", () => { - let model = service.findById( - "testFormArray", - testModel - ) as DynamicFormArrayModel, - formArray = service.createFormArray(model), - index = 3, - step = -1; - - (formArray.at(index) as FormGroup).controls[ - "testFormArrayGroupInput" - ].setValue("next test value 1"); - (formArray.at(index + step) as FormGroup).controls[ - "testFormArrayGroupInput" - ].setValue("next test value 2"); - - (model.get(index).get(0) as DynamicFormValueControlModel).value = - "next test value 1"; - (model.get(index + step).get(0) as DynamicFormValueControlModel< - any - >).value = "next test value 2"; - - service.moveFormArrayGroup(index, step, formArray, model); - - expect(formArray.length).toBe(model.initialCount); - - expect( - (formArray.at(index) as FormGroup).controls["testFormArrayGroupInput"] - .value - ).toEqual("next test value 2"); - expect( - (formArray.at(index + step) as FormGroup).controls[ - "testFormArrayGroupInput" - ].value - ).toEqual("next test value 1"); - - expect( - (model.get(index).get(0) as DynamicFormValueControlModel).value - ).toEqual("next test value 2"); - expect( - (model.get(index + step).get(0) as DynamicFormValueControlModel) - .value - ).toEqual("next test value 1"); - }); - - it("should throw when form array group is to be moved out of bounds", () => { - let model = service.findById( - "testFormArray", - testModel - ) as DynamicFormArrayModel, - formArray = service.createFormArray(model); - - expect(() => service.moveFormArrayGroup(2, -5, formArray, model)).toThrow( - new Error( - `form array group cannot be moved due to index or new index being out of bounds` - ) - ); - }); - - it("should remove a form array group", () => { - let model = service.findById( - "testFormArray", - testModel - ) as DynamicFormArrayModel, - formArray = service.createFormArray(model); - - service.removeFormArrayGroup(0, formArray, model); - - const newCount = model.initialCount - 1; - - expect(model.size).toBe(newCount); - expect(formArray.length).toBe(newCount); - }); - - it("should clear a form array", () => { - let model = service.findById( - "testFormArray", - testModel - ) as DynamicFormArrayModel, - formArray = service.createFormArray(model); - - service.clearFormArray(formArray, model); - - expect(model.size === 0).toBe(true); - expect(formArray.length === 0).toBe(true); - }); + + it("should move an existing form control to a different group position", () => { + + let formGroup = service.createFormGroup(testModel), + nestedFormGroupModel = testModel[7] as DynamicFormGroupModel, + model1 = testModel[0], + model2 = nestedFormGroupModel.get(0); + + service.moveFormGroupControl(0, 2, testModel); + + expect(formGroup.controls[model1.id]).toBeTruthy(); + expect(testModel[2] === model1).toBe(true); + + service.moveFormGroupControl(0, 1, nestedFormGroupModel); + + expect((formGroup.controls["testFormGroup"] as FormGroup).controls[model2.id]).toBeTruthy(); + expect(nestedFormGroupModel.get(1) === model2).toBe(true); + }); + + + it("should remove a form control from an existing form group", () => { + + let formGroup = service.createFormGroup(testModel), + nestedFormGroup = formGroup.controls["testFormGroup"] as FormGroup, + nestedFormGroupModel = testModel[7] as DynamicFormGroupModel, + length = testModel.length, + size = nestedFormGroupModel.size(), + index = 1, + id1 = testModel[index].id, + id2 = nestedFormGroupModel.get(index).id; + + service.removeFormGroupControl(index, formGroup, testModel); + + expect(Object.keys(formGroup.controls).length).toBe(length - 1); + expect(formGroup.controls[id1]).toBeUndefined(); + + expect(testModel.length).toBe(length - 1); + expect(service.findById(id1, testModel)).toBeNull(); + + service.removeFormGroupControl(index, nestedFormGroup, nestedFormGroupModel); + + expect(Object.keys(nestedFormGroup.controls).length).toBe(size - 1); + expect(nestedFormGroup.controls[id2]).toBeUndefined(); + + expect(nestedFormGroupModel.size()).toBe(size - 1); + expect(service.findById(id2, testModel)).toBeNull(); + }); + + + it("should create a form array", () => { + + let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, + formArray; + + expect(service.createFormArray).toBeTruthy(); + + formArray = service.createFormArray(model); + + expect(formArray instanceof FormArray).toBe(true); + expect(formArray.length).toBe(model.initialCount); + }); + + + it("should add a form array group", () => { + + let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, + formArray = service.createFormArray(model); + + service.addFormArrayGroup(formArray, model); + + expect(formArray.length).toBe(model.initialCount + 1); + }); + + + it("should insert a form array group", () => { + + let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, + formArray = service.createFormArray(model); + + service.insertFormArrayGroup(0, formArray, model); + + expect(formArray.length).toBe(model.initialCount + 1); + }); + + + it("should move up a form array group", () => { + + let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, + formArray = service.createFormArray(model), + index = 3, + step = 1; + + (formArray.at(index) as FormGroup).controls["testFormArrayGroupInput"].setValue("next test value 1"); + (formArray.at(index + step) as FormGroup).controls["testFormArrayGroupInput"].setValue("next test value 2"); + + (model.get(index).get(0) as DynamicFormValueControlModel).value = "next test value 1"; + (model.get(index + step).get(0) as DynamicFormValueControlModel).value = "next test value 2"; + + service.moveFormArrayGroup(index, step, formArray, model); + + expect(formArray.length).toBe(model.initialCount); + + expect((formArray.at(index) as FormGroup).controls["testFormArrayGroupInput"].value).toEqual("next test value 2"); + expect((formArray.at(index + step) as FormGroup).controls["testFormArrayGroupInput"].value).toEqual("next test value 1"); + + expect((model.get(index).get(0) as DynamicFormValueControlModel).value).toEqual("next test value 2"); + expect((model.get(index + step).get(0) as DynamicFormValueControlModel).value).toEqual("next test value 1"); + }); + + + it("should move down a form array group", () => { + + let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, + formArray = service.createFormArray(model), + index = 3, + step = -1; + + (formArray.at(index) as FormGroup).controls["testFormArrayGroupInput"].setValue("next test value 1"); + (formArray.at(index + step) as FormGroup).controls["testFormArrayGroupInput"].setValue("next test value 2"); + + (model.get(index).get(0) as DynamicFormValueControlModel).value = "next test value 1"; + (model.get(index + step).get(0) as DynamicFormValueControlModel).value = "next test value 2"; + + service.moveFormArrayGroup(index, step, formArray, model); + + expect(formArray.length).toBe(model.initialCount); + + expect((formArray.at(index) as FormGroup).controls["testFormArrayGroupInput"].value).toEqual("next test value 2"); + expect((formArray.at(index + step) as FormGroup).controls["testFormArrayGroupInput"].value).toEqual("next test value 1"); + + expect((model.get(index).get(0) as DynamicFormValueControlModel).value).toEqual("next test value 2"); + expect((model.get(index + step).get(0) as DynamicFormValueControlModel).value).toEqual("next test value 1"); + }); + + + it("should throw when form array group is to be moved out of bounds", () => { + + let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, + formArray = service.createFormArray(model); + + expect(() => service.moveFormArrayGroup(2, -5, formArray, model)) + .toThrow(new Error(`form array group cannot be moved due to index or new index being out of bounds`)); + }); + + + it("should remove a form array group", () => { + + let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, + formArray = service.createFormArray(model); + + service.removeFormArrayGroup(0, formArray, model); + + const newCount = model.initialCount - 1; + + expect(model.size).toBe(newCount); + expect(formArray.length).toBe(newCount); + }); + + + it("should clear a form array", () => { + + let model = service.findById("testFormArray", testModel) as DynamicFormArrayModel, + formArray = service.createFormArray(model); + + service.clearFormArray(formArray, model); + + expect(model.size === 0).toBe(true); + expect(formArray.length === 0).toBe(true); + }); }); 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 6a3322273..85a52dab7 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,582 +1,475 @@ import { Injectable } from "@angular/core"; -import { - AbstractControl, - FormArray, - FormControl, - FormGroup, -} from "@angular/forms"; +import { AbstractControl, FormArray, FormControl, FormGroup } from "@angular/forms"; import { AbstractControlOptions } from "@angular/forms"; import { DynamicFormControlModel } from "../model/dynamic-form-control.model"; import { DynamicFormValueControlModel } from "../model/dynamic-form-value-control.model"; import { - DynamicFormArrayModel, - DYNAMIC_FORM_CONTROL_TYPE_ARRAY, - DynamicFormArrayGroupModel, + DynamicFormArrayModel, + DYNAMIC_FORM_CONTROL_TYPE_ARRAY, + DynamicFormArrayGroupModel } from "../model/form-array/dynamic-form-array.model"; +import { DYNAMIC_FORM_CONTROL_TYPE_GROUP, DynamicFormGroupModel } from "../model/form-group/dynamic-form-group.model"; import { - DYNAMIC_FORM_CONTROL_TYPE_GROUP, - DynamicFormGroupModel, -} from "../model/form-group/dynamic-form-group.model"; -import { - DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP, - DynamicCheckboxGroupModel, + DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP, + DynamicCheckboxGroupModel } from "../model/checkbox/dynamic-checkbox-group.model"; +import { DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX, DynamicCheckboxModel } from "../model/checkbox/dynamic-checkbox.model"; import { - DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX, - DynamicCheckboxModel, -} from "../model/checkbox/dynamic-checkbox.model"; -import { - DYNAMIC_FORM_CONTROL_TYPE_COLORPICKER, - DynamicColorPickerModel, + DYNAMIC_FORM_CONTROL_TYPE_COLORPICKER, + DynamicColorPickerModel } from "../model/colorpicker/dynamic-colorpicker.model"; import { - DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER, - DynamicDatePickerModel, + DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER, + DynamicDatePickerModel } from "../model/datepicker/dynamic-datepicker.model"; +import { DYNAMIC_FORM_CONTROL_TYPE_EDITOR, DynamicEditorModel } from "../model/editor/dynamic-editor.model"; import { - DYNAMIC_FORM_CONTROL_TYPE_EDITOR, - DynamicEditorModel, -} from "../model/editor/dynamic-editor.model"; -import { - DYNAMIC_FORM_CONTROL_TYPE_FILE_UPLOAD, - DynamicFileUploadModel, + DYNAMIC_FORM_CONTROL_TYPE_FILE_UPLOAD, + DynamicFileUploadModel } from "../model/file-upload/dynamic-file-upload.model"; +import { DYNAMIC_FORM_CONTROL_TYPE_INPUT, DynamicInputModel } from "../model/input/dynamic-input.model"; import { - DYNAMIC_FORM_CONTROL_TYPE_INPUT, - DynamicInputModel, -} from "../model/input/dynamic-input.model"; -import { - DYNAMIC_FORM_CONTROL_TYPE_RADIO_GROUP, - DynamicRadioGroupModel, + DYNAMIC_FORM_CONTROL_TYPE_RADIO_GROUP, + DynamicRadioGroupModel } from "../model/radio/dynamic-radio-group.model"; +import { DYNAMIC_FORM_CONTROL_TYPE_RATING, DynamicRatingModel } from "../model/rating/dynamic-rating.model"; +import { DYNAMIC_FORM_CONTROL_TYPE_SELECT, DynamicSelectModel } from "../model/select/dynamic-select.model"; +import { DYNAMIC_FORM_CONTROL_TYPE_SLIDER, DynamicSliderModel } from "../model/slider/dynamic-slider.model"; +import { DYNAMIC_FORM_CONTROL_TYPE_SWITCH, DynamicSwitchModel } from "../model/switch/dynamic-switch.model"; +import { DYNAMIC_FORM_CONTROL_TYPE_TEXTAREA, DynamicTextAreaModel } from "../model/textarea/dynamic-textarea.model"; import { - DYNAMIC_FORM_CONTROL_TYPE_RATING, - DynamicRatingModel, -} from "../model/rating/dynamic-rating.model"; -import { - DYNAMIC_FORM_CONTROL_TYPE_SELECT, - DynamicSelectModel, -} from "../model/select/dynamic-select.model"; -import { - DYNAMIC_FORM_CONTROL_TYPE_SLIDER, - DynamicSliderModel, -} from "../model/slider/dynamic-slider.model"; -import { - DYNAMIC_FORM_CONTROL_TYPE_SWITCH, - DynamicSwitchModel, -} from "../model/switch/dynamic-switch.model"; -import { - DYNAMIC_FORM_CONTROL_TYPE_TEXTAREA, - DynamicTextAreaModel, -} from "../model/textarea/dynamic-textarea.model"; -import { - DYNAMIC_FORM_CONTROL_TYPE_TIMEPICKER, - DynamicTimePickerModel, + DYNAMIC_FORM_CONTROL_TYPE_TIMEPICKER, + DynamicTimePickerModel } from "../model/timepicker/dynamic-timepicker.model"; import { DynamicFormValidationService } from "./dynamic-form-validation.service"; -import { - DynamicFormModel, - DynamicUnionFormModel, -} from "../model/dynamic-form.model"; +import { DynamicFormModel, DynamicUnionFormModel } from "../model/dynamic-form.model"; import { DynamicPathable } from "../model/misc/dynamic-form-control-path.model"; -import { - DynamicFormHook, - DynamicValidatorsConfig, -} from "../model/misc/dynamic-form-control-validation.model"; +import { DynamicFormHook, DynamicValidatorsConfig } from "../model/misc/dynamic-form-control-validation.model"; import { maskFromString, parseReviver } from "../utils/json.utils"; import { isString } from "../utils/core.utils"; import { DynamicFormComponent } from "../component/dynamic-form.component"; import { DynamicFormComponentService } from "./dynamic-form-component.service"; @Injectable({ - providedIn: "root", + providedIn: "root" }) export class DynamicFormService { - constructor( - private componentService: DynamicFormComponentService, - private validationService: DynamicFormValidationService - ) {} - - private createAbstractControlOptions( - validatorsConfig: DynamicValidatorsConfig | null = null, - asyncValidatorsConfig: DynamicValidatorsConfig | null = null, - updateOn: DynamicFormHook | null = null - ): AbstractControlOptions { - return { - asyncValidators: - asyncValidatorsConfig !== null - ? this.validationService.getAsyncValidators(asyncValidatorsConfig) - : null, - validators: - validatorsConfig !== null - ? this.validationService.getValidators(validatorsConfig) - : null, - updateOn: - updateOn !== null && this.validationService.isFormHook(updateOn) - ? updateOn - : DynamicFormHook.Change, - }; - } - - createFormArray(formArrayModel: DynamicFormArrayModel): FormArray { - const controls: AbstractControl[] = []; - const options = this.createAbstractControlOptions( - formArrayModel.validators, - formArrayModel.asyncValidators, - formArrayModel.updateOn - ); - - for (let index = 0; index < formArrayModel.size; index++) { - const groupModel = formArrayModel.get(index); - const groupOptions = this.createAbstractControlOptions( - formArrayModel.groupValidators, - formArrayModel.groupAsyncValidators, - formArrayModel.updateOn - ); - - controls.push( - this.createFormGroup(groupModel.group, groupOptions, groupModel) - ); + + constructor(private componentService: DynamicFormComponentService, + private validationService: DynamicFormValidationService) { } - return new FormArray(controls, options); - } - - createFormGroup( - formModel: DynamicFormModel, - options: AbstractControlOptions | null = null, - parent: DynamicPathable | null = null - ): FormGroup { - const controls: { [controlId: string]: AbstractControl } = {}; - - formModel.forEach(model => { - model.parent = parent; - - switch (model.type) { - case DYNAMIC_FORM_CONTROL_TYPE_ARRAY: - controls[model.id] = this.createFormArray( - model as DynamicFormArrayModel - ); - break; - - case DYNAMIC_FORM_CONTROL_TYPE_GROUP: - case DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP: - const groupModel = model as DynamicFormGroupModel; - const groupOptions = this.createAbstractControlOptions( - groupModel.validators, - groupModel.asyncValidators, - groupModel.updateOn - ); - - controls[model.id] = this.createFormGroup( - groupModel.group, - groupOptions, - groupModel - ); - break; - - default: - const controlModel = model as DynamicFormValueControlModel; - const controlState = { - value: controlModel.value, - disabled: controlModel.disabled, - }; - const controlOptions = this.createAbstractControlOptions( - controlModel.validators, - controlModel.asyncValidators, - controlModel.updateOn - ); - - controls[model.id] = new FormControl(controlState, controlOptions); - } - }); - - return new FormGroup(controls, options); - } - - getPathSegment(model: DynamicPathable): string { - return model instanceof DynamicFormArrayGroupModel - ? model.index.toString() - : (model as DynamicFormControlModel).id; - } - - getPath(model: DynamicPathable, join: boolean = false): string[] | string { - const path = [this.getPathSegment(model)]; - let parent = model.parent; - - while (parent) { - path.unshift(this.getPathSegment(parent)); - parent = parent.parent; + private createAbstractControlOptions(validatorsConfig: DynamicValidatorsConfig | null = null, + asyncValidatorsConfig: DynamicValidatorsConfig | null = null, + updateOn: DynamicFormHook | null = null): AbstractControlOptions { + + return { + asyncValidators: asyncValidatorsConfig !== null ? this.validationService.getAsyncValidators(asyncValidatorsConfig) : null, + validators: validatorsConfig !== null ? this.validationService.getValidators(validatorsConfig) : null, + updateOn: updateOn !== null && this.validationService.isFormHook(updateOn) ? updateOn : DynamicFormHook.Change + }; + } + + createFormArray(formArrayModel: DynamicFormArrayModel): FormArray { + + const controls: AbstractControl[] = []; + const options = this.createAbstractControlOptions(formArrayModel.validators, formArrayModel.asyncValidators, + formArrayModel.updateOn); + + for (let index = 0; index < formArrayModel.size; index++) { + + const groupModel = formArrayModel.get(index); + const groupOptions = this.createAbstractControlOptions(formArrayModel.groupValidators, + formArrayModel.groupAsyncValidators, formArrayModel.updateOn); + + controls.push(this.createFormGroup(groupModel.group, groupOptions, groupModel)); + } + + return new FormArray(controls, options); + } + + createFormGroup(formModel: DynamicFormModel, options: AbstractControlOptions | null = null, + parent: DynamicPathable | null = null): FormGroup { + + const controls: { [controlId: string]: AbstractControl; } = {}; + + formModel.forEach(model => { + + model.parent = parent; + + switch (model.type) { + + case DYNAMIC_FORM_CONTROL_TYPE_ARRAY: + + controls[model.id] = this.createFormArray(model as DynamicFormArrayModel); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_GROUP: + case DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP: + + const groupModel = model as DynamicFormGroupModel; + const groupOptions = this.createAbstractControlOptions(groupModel.validators, + groupModel.asyncValidators, groupModel.updateOn); + + controls[model.id] = this.createFormGroup(groupModel.group, groupOptions, groupModel); + break; + + default: + + const controlModel = model as DynamicFormValueControlModel; + const controlState = {value: controlModel.value, disabled: controlModel.disabled}; + const controlOptions = this.createAbstractControlOptions(controlModel.validators, + controlModel.asyncValidators, controlModel.updateOn); + + controls[model.id] = new FormControl(controlState, controlOptions); + } + }); + + return new FormGroup(controls, options); } - return join ? path.join(".") : path; - } - - addFormGroupControl( - formGroup: FormGroup, - formModel: DynamicUnionFormModel, - ...models: DynamicFormModel - ): void { - if (formModel instanceof DynamicFormGroupModel) { - this.insertFormGroupControl( - formModel.size(), - formGroup, - formModel, - ...models - ); - } else { - const model = formModel as DynamicFormModel; - this.insertFormGroupControl(model.length, formGroup, model, ...models); + getPathSegment(model: DynamicPathable): string { + return model instanceof DynamicFormArrayGroupModel ? model.index.toString() : (model as DynamicFormControlModel).id; } - } - - moveFormGroupControl( - index: number, - step: number, - formModel: DynamicUnionFormModel - ): void { - if (formModel instanceof DynamicFormGroupModel) { - formModel.move(index, step); - } else { - const model = formModel as DynamicFormModel; - model.splice(index + step, 0, ...model.splice(index, 1)); + + getPath(model: DynamicPathable, join: boolean = false): string[] | string { + + const path = [this.getPathSegment(model)]; + let parent = model.parent; + + while (parent) { + + path.unshift(this.getPathSegment(parent)); + parent = parent.parent; + } + + return join ? path.join(".") : path; } - } - - insertFormGroupControl( - index: number, - formGroup: FormGroup, - formModel: DynamicUnionFormModel, - ...models: DynamicFormModel - ): void { - const parent = - formModel instanceof DynamicFormGroupModel ? formModel : null; - const controls = this.createFormGroup(models, null, parent).controls; - - Object.keys(controls).forEach((controlName, idx) => { - const controlModel = models[idx]; - - if (formModel instanceof DynamicFormGroupModel) { - formModel.insert(index, controlModel); - } else { - (formModel as DynamicFormModel).splice(index, 0, controlModel); - } - - formGroup.addControl(controlName, controls[controlName]); - }); - } - - removeFormGroupControl( - index: number, - formGroup: FormGroup, - formModel: DynamicUnionFormModel - ): void { - if (formModel instanceof DynamicFormGroupModel) { - formGroup.removeControl(formModel.get(index).id); - formModel.remove(index); - } else { - formGroup.removeControl(formModel[index].id); - (formModel as DynamicFormModel).splice(index, 1); + + addFormGroupControl(formGroup: FormGroup, formModel: DynamicUnionFormModel, ...models: DynamicFormModel): void { + + if (formModel instanceof DynamicFormGroupModel) { + + this.insertFormGroupControl(formModel.size(), formGroup, formModel, ...models); + + } else { + + const model = formModel as DynamicFormModel; + this.insertFormGroupControl(model.length, formGroup, model, ...models); + } } - } - - addFormArrayGroup( - formArray: FormArray, - formArrayModel: DynamicFormArrayModel - ): void { - const groupModel = formArrayModel.addGroup(); - - formArray.push(this.createFormGroup(groupModel.group, null, groupModel)); - } - - insertFormArrayGroup( - index: number, - formArray: FormArray, - formArrayModel: DynamicFormArrayModel - ): void { - const groupModel = formArrayModel.insertGroup(index); - - formArray.insert( - index, - this.createFormGroup(groupModel.group, null, groupModel) - ); - } - - moveFormArrayGroup( - index: number, - step: number, - formArray: FormArray, - formArrayModel: DynamicFormArrayModel - ): void { - const newIndex = index + step; - const moveUp = step >= 0; - - if ( - index >= 0 && - index < formArrayModel.size && - newIndex >= 0 && - newIndex < formArrayModel.size - ) { - const movingGroups: AbstractControl[] = []; - - for ( - let i = moveUp ? index : newIndex; - i <= (moveUp ? newIndex : index); - i++ - ) { - movingGroups.push(formArray.at(i)); - } - - movingGroups.forEach((formControl, idx) => { - let position; - - if (moveUp) { - position = idx === 0 ? newIndex : index + idx - 1; + + moveFormGroupControl(index: number, step: number, formModel: DynamicUnionFormModel): void { + + if (formModel instanceof DynamicFormGroupModel) { + + formModel.move(index, step); + } else { - position = - idx === movingGroups.length - 1 ? newIndex : newIndex + idx + 1; + + const model = formModel as DynamicFormModel; + model.splice(index + step, 0, ...model.splice(index, 1)); } + } + + insertFormGroupControl(index: number, formGroup: FormGroup, formModel: DynamicUnionFormModel, + ...models: DynamicFormModel): void { + + const parent = formModel instanceof DynamicFormGroupModel ? formModel : null; + const controls = this.createFormGroup(models, null, parent).controls; + + Object.keys(controls).forEach((controlName, idx) => { + + const controlModel = models[idx]; + + if (formModel instanceof DynamicFormGroupModel) { + formModel.insert(index, controlModel); - formArray.setControl(position, formControl); - }); + } else { + (formModel as DynamicFormModel).splice(index, 0, controlModel); + } - formArrayModel.moveGroup(index, step); - } else { - throw new Error( - `form array group cannot be moved due to index or new index being out of bounds` - ); + formGroup.addControl(controlName, controls[controlName]); + }); } - } - - removeFormArrayGroup( - index: number, - formArray: FormArray, - formArrayModel: DynamicFormArrayModel - ): void { - formArray.removeAt(index); - formArrayModel.removeGroup(index); - } - - clearFormArray( - formArray: FormArray, - formArrayModel: DynamicFormArrayModel - ): void { - formArray.clear(); - formArrayModel.clear(); - } - - findById( - id: string, - formModel: DynamicFormModel - ): DynamicFormControlModel | null { - let result = null; - - const findByIdFn = ( - modelId: string, - groupModel: DynamicFormModel - ): void => { - for (const controlModel of groupModel) { - if (controlModel.id === modelId) { - result = controlModel; - break; + + removeFormGroupControl(index: number, formGroup: FormGroup, formModel: DynamicUnionFormModel): void { + + if (formModel instanceof DynamicFormGroupModel) { + + formGroup.removeControl(formModel.get(index).id); + formModel.remove(index); + + } else { + + formGroup.removeControl(formModel[index].id); + (formModel as DynamicFormModel).splice(index, 1); } + } - if (controlModel instanceof DynamicFormGroupModel) { - findByIdFn(modelId, (controlModel as DynamicFormGroupModel).group); + addFormArrayGroup(formArray: FormArray, formArrayModel: DynamicFormArrayModel): void { + + const groupModel = formArrayModel.addGroup(); + + formArray.push(this.createFormGroup(groupModel.group, null, groupModel)); + } + + insertFormArrayGroup(index: number, formArray: FormArray, formArrayModel: DynamicFormArrayModel): void { + + const groupModel = formArrayModel.insertGroup(index); + + formArray.insert(index, this.createFormGroup(groupModel.group, null, groupModel)); + } + + moveFormArrayGroup(index: number, step: number, formArray: FormArray, formArrayModel: DynamicFormArrayModel): void { + + const newIndex = index + step; + const moveUp = step >= 0; + + if ((index >= 0 && index < formArrayModel.size) && (newIndex >= 0 && newIndex < formArrayModel.size)) { + + const movingGroups: AbstractControl[] = []; + + for (let i = moveUp ? index : newIndex; i <= (moveUp ? newIndex : index); i++) { + movingGroups.push(formArray.at(i)); + } + + movingGroups.forEach((formControl, idx) => { + + let position; + + if (moveUp) { + position = idx === 0 ? newIndex : index + idx - 1; + + } else { + position = idx === movingGroups.length - 1 ? newIndex : newIndex + idx + 1; + } + + formArray.setControl(position, formControl); + }); + + formArrayModel.moveGroup(index, step); + + } else { + throw new Error(`form array group cannot be moved due to index or new index being out of bounds`); } - } - }; - - findByIdFn(id, formModel); - - return result; - } - - findByPath = ( - fullPath: string, - formModel: DynamicFormControlModel[] - ): DynamicFormControlModel | null => { - let result = null, - findByPathFn = (path: string, groupModel: any): void => { - let id: string = path.split(".")[0]; - let pathArray: string[] = path.split("."); - pathArray.splice(0, 1); - path = pathArray.join("."); - - for (let controlModel of groupModel) { - if (controlModel.id === id) { - if (path.length === 0) { - result = controlModel; - break; - } else if ( - path.split(".").length == 1 && - Number.isInteger(Number(path.split(".")[0])) && - !controlModel["multiple"] - ) { - result = (controlModel as DynamicFormArrayModel).groups[ - path.split(".")[0] - ]; - break; - } else { - if (controlModel instanceof DynamicFormGroupModel) { - findByPathFn(path, controlModel.group); - } else if (controlModel instanceof DynamicFormArrayModel) { - id = path.split(".")[0]; - pathArray = path.split("."); - pathArray.splice(0, 1); - path = pathArray.join("."); - if ( - controlModel.groups && - controlModel.groups[id] && - controlModel.groups[id].group + } + + removeFormArrayGroup(index: number, formArray: FormArray, formArrayModel: DynamicFormArrayModel): void { + + formArray.removeAt(index); + formArrayModel.removeGroup(index); + } + + clearFormArray(formArray: FormArray, formArrayModel: DynamicFormArrayModel): void { + + formArray.clear(); + formArrayModel.clear(); + } + + findById(id: string, formModel: DynamicFormModel): DynamicFormControlModel | null { + + let result = null; + + const findByIdFn = (modelId: string, groupModel: DynamicFormModel): void => { + + for (const controlModel of groupModel) { + + if (controlModel.id === modelId) { + result = controlModel; + break; + } + + if (controlModel instanceof DynamicFormGroupModel) { + findByIdFn(modelId, (controlModel as DynamicFormGroupModel).group); + } + } + }; + + findByIdFn(id, formModel); + + return result; + } + + findByPath = ( + fullPath: string, + formModel: DynamicFormControlModel[] + ): DynamicFormControlModel | null => { + let result = null, + findByPathFn = (path: string, groupModel: any): void => { + let id: string = path.split(".")[0]; + let pathArray: string[] = path.split("."); + pathArray.splice(0, 1); + path = pathArray.join("."); + + for (let controlModel of groupModel) { + if (controlModel.id === id) { + if (path.length === 0) { + result = controlModel; + break; + } else if ( + path.split(".").length == 1 && + Number.isInteger(Number(path.split(".")[0])) && + !controlModel["multiple"] ) { - findByPathFn(path, controlModel.groups[id].group); + result = (controlModel as DynamicFormArrayModel).groups[ + path.split(".")[0] + ]; + break; + } else { + if (controlModel instanceof DynamicFormGroupModel) { + findByPathFn(path, controlModel.group); + } else if (controlModel instanceof DynamicFormArrayModel) { + id = path.split(".")[0]; + pathArray = path.split("."); + pathArray.splice(0, 1); + path = pathArray.join("."); + if ( + controlModel.groups && + controlModel.groups[id] && + controlModel.groups[id].group + ) { + findByPathFn(path, controlModel.groups[id].group); + } + } } } } - } - } + }; + + findByPathFn(fullPath, formModel); + + return result; }; - findByPathFn(fullPath, formModel); - - return result; - }; - - findModelById( - id: string, - formModel: DynamicFormModel - ): T | null { - return this.findById(id, formModel) as T; - } - - findControlByModel( - model: DynamicFormControlModel, - group: FormGroup - ): T | null { - return group.root.get(this.getPath(model, true)) as T; - } - - detectChanges(formComponent?: DynamicFormComponent): void { - if (formComponent instanceof DynamicFormComponent) { - formComponent.markForCheck(); - formComponent.detectChanges(); - } else { - for (const form of this.componentService.getForms()) { - form.markForCheck(); - form.detectChanges(); - } + findModelById(id: string, formModel: DynamicFormModel): T | null { + return this.findById(id, formModel) as T; } - } - - fromJSON(json: string | object[]): DynamicFormModel | never { - const formModelJSON = isString(json) - ? JSON.parse(json, parseReviver) - : json; - const formModel: DynamicFormModel = []; - - formModelJSON.forEach((model: any) => { - const layout = model.layout ?? null; - - switch (model.type) { - case DYNAMIC_FORM_CONTROL_TYPE_ARRAY: - const formArrayModel = model as DynamicFormArrayModel; - - if (Array.isArray(formArrayModel.groups)) { - formArrayModel.groups.forEach( - (groupModel: DynamicFormArrayGroupModel) => { - groupModel.group = this.fromJSON( - groupModel.group - ) as DynamicFormModel; - } - ); - } - formArrayModel.groupFactory = () => { - return this.fromJSON(formArrayModel.groupPrototype); - }; + findControlByModel(model: DynamicFormControlModel, group: FormGroup): T | null { + return group.root.get(this.getPath(model, true)) as T; + } - formModel.push(new DynamicFormArrayModel(model, layout)); - break; + detectChanges(formComponent?: DynamicFormComponent): void { - case DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX: - formModel.push(new DynamicCheckboxModel(model, layout)); - break; + if (formComponent instanceof DynamicFormComponent) { - case DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP: - model.group = this.fromJSON(model.group) as DynamicCheckboxModel[]; - formModel.push(new DynamicCheckboxGroupModel(model, layout)); - break; + formComponent.markForCheck(); + formComponent.detectChanges(); - case DYNAMIC_FORM_CONTROL_TYPE_COLORPICKER: - formModel.push(new DynamicColorPickerModel(model, layout)); - break; + } else { - case DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER: - formModel.push(new DynamicDatePickerModel(model, layout)); - break; + for (const form of this.componentService.getForms()) { + form.markForCheck(); + form.detectChanges(); + } + } + } - case DYNAMIC_FORM_CONTROL_TYPE_EDITOR: - formModel.push(new DynamicEditorModel(model, layout)); - break; + fromJSON(json: string | object[]): DynamicFormModel | never { - case DYNAMIC_FORM_CONTROL_TYPE_FILE_UPLOAD: - model.value = null; - formModel.push(new DynamicFileUploadModel(model, layout)); - break; + const formModelJSON = isString(json) ? JSON.parse(json, parseReviver) : json; + const formModel: DynamicFormModel = []; - case DYNAMIC_FORM_CONTROL_TYPE_GROUP: - model.group = this.fromJSON(model.group); - formModel.push(new DynamicFormGroupModel(model, layout)); - break; + formModelJSON.forEach((model: any) => { - case DYNAMIC_FORM_CONTROL_TYPE_INPUT: - const inputModel = model as DynamicInputModel; + const layout = model.layout ?? null; - if (inputModel.mask !== null) { - if (!(inputModel.mask instanceof Function)) { - inputModel.mask = maskFromString(inputModel.mask as string); - } - } + switch (model.type) { + + case DYNAMIC_FORM_CONTROL_TYPE_ARRAY: + const formArrayModel = model as DynamicFormArrayModel; + + if (Array.isArray(formArrayModel.groups)) { + + formArrayModel.groups.forEach((groupModel: DynamicFormArrayGroupModel) => { + groupModel.group = this.fromJSON(groupModel.group) as DynamicFormModel; + }); + } + + formArrayModel.groupFactory = () => { + return this.fromJSON(formArrayModel.groupPrototype); + }; + + formModel.push(new DynamicFormArrayModel(model, layout)); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX: + formModel.push(new DynamicCheckboxModel(model, layout)); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP: + model.group = this.fromJSON(model.group) as DynamicCheckboxModel[]; + formModel.push(new DynamicCheckboxGroupModel(model, layout)); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_COLORPICKER: + formModel.push(new DynamicColorPickerModel(model, layout)); + break; - formModel.push(new DynamicInputModel(model, layout)); - break; + case DYNAMIC_FORM_CONTROL_TYPE_DATEPICKER: + formModel.push(new DynamicDatePickerModel(model, layout)); + break; - case DYNAMIC_FORM_CONTROL_TYPE_RADIO_GROUP: - formModel.push(new DynamicRadioGroupModel(model, layout)); - break; + case DYNAMIC_FORM_CONTROL_TYPE_EDITOR: + formModel.push(new DynamicEditorModel(model, layout)); + break; - case DYNAMIC_FORM_CONTROL_TYPE_RATING: - formModel.push(new DynamicRatingModel(model, layout)); - break; + case DYNAMIC_FORM_CONTROL_TYPE_FILE_UPLOAD: + model.value = null; + formModel.push(new DynamicFileUploadModel(model, layout)); + break; - case DYNAMIC_FORM_CONTROL_TYPE_SELECT: - formModel.push(new DynamicSelectModel(model, layout)); - break; + case DYNAMIC_FORM_CONTROL_TYPE_GROUP: + model.group = this.fromJSON(model.group); + formModel.push(new DynamicFormGroupModel(model, layout)); + break; - case DYNAMIC_FORM_CONTROL_TYPE_SLIDER: - formModel.push(new DynamicSliderModel(model, layout)); - break; + case DYNAMIC_FORM_CONTROL_TYPE_INPUT: + const inputModel = model as DynamicInputModel; - case DYNAMIC_FORM_CONTROL_TYPE_SWITCH: - formModel.push(new DynamicSwitchModel(model, layout)); - break; + if (inputModel.mask !== null) { + if (!(inputModel.mask instanceof Function)) { + inputModel.mask = maskFromString(inputModel.mask as string); + } + } - case DYNAMIC_FORM_CONTROL_TYPE_TEXTAREA: - formModel.push(new DynamicTextAreaModel(model, layout)); - break; + formModel.push(new DynamicInputModel(model, layout)); + break; - case DYNAMIC_FORM_CONTROL_TYPE_TIMEPICKER: - formModel.push(new DynamicTimePickerModel(model, layout)); - break; + case DYNAMIC_FORM_CONTROL_TYPE_RADIO_GROUP: + formModel.push(new DynamicRadioGroupModel(model, layout)); + break; - default: - throw new Error( - `unknown form control model type defined on JSON object with id "${model.id}"` - ); - } - }); + case DYNAMIC_FORM_CONTROL_TYPE_RATING: + formModel.push(new DynamicRatingModel(model, layout)); + break; - return formModel; - } + case DYNAMIC_FORM_CONTROL_TYPE_SELECT: + formModel.push(new DynamicSelectModel(model, layout)); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_SLIDER: + formModel.push(new DynamicSliderModel(model, layout)); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_SWITCH: + formModel.push(new DynamicSwitchModel(model, layout)); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_TEXTAREA: + formModel.push(new DynamicTextAreaModel(model, layout)); + break; + + case DYNAMIC_FORM_CONTROL_TYPE_TIMEPICKER: + formModel.push(new DynamicTimePickerModel(model, layout)); + break; + + default: + throw new Error(`unknown form control model type defined on JSON object with id "${model.id}"`); + } + }); + + return formModel; + } }