import { Injectable } from '@angular/core'; import { AbstractControl, FormGroup } from '@angular/forms'; import { DYNAMIC_FORM_CONTROL_TYPE_ARRAY, DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP, DYNAMIC_FORM_CONTROL_TYPE_GROUP, DYNAMIC_FORM_CONTROL_TYPE_INPUT, DYNAMIC_FORM_CONTROL_TYPE_RADIO_GROUP, DynamicFormArrayModel, DynamicFormControlEvent, DynamicFormControlModel, DynamicFormGroupModel, DynamicFormService, DynamicFormValidationService, DynamicPathable, parseReviver, } from '@ng-dynamic-forms/core'; import { isObject, isString, mergeWith } from 'lodash'; import { hasValue, isEmpty, isNotEmpty, isNotNull, isNotUndefined, isNull } from '../../empty.util'; import { DynamicQualdropModel } from './ds-dynamic-form-ui/models/ds-dynamic-qualdrop.model'; import { SubmissionFormsModel } from '../../../core/config/models/config-submission-forms.model'; import { DYNAMIC_FORM_CONTROL_TYPE_TAG } from './ds-dynamic-form-ui/models/tag/dynamic-tag.model'; import { RowParser } from './parsers/row-parser'; import { DynamicRelationGroupModel } from './ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model'; import { DynamicRowArrayModel } from './ds-dynamic-form-ui/models/ds-dynamic-row-array-model'; import { DsDynamicInputModel } from './ds-dynamic-form-ui/models/ds-dynamic-input.model'; import { FormFieldMetadataValueObject } from './models/form-field-metadata-value.model'; import { isNgbDateStruct } from '../../date.util'; import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './ds-dynamic-form-ui/ds-dynamic-form-constants'; import { CONCAT_GROUP_SUFFIX, DynamicConcatModel } from './ds-dynamic-form-ui/models/ds-dynamic-concat.model'; @Injectable() export class FormBuilderService extends DynamicFormService { constructor( validationService: DynamicFormValidationService, protected rowParser: RowParser ) { super(validationService); } findById(id: string, groupModel: DynamicFormControlModel[], arrayIndex = null): DynamicFormControlModel | null { let result = null; const findByIdFn = (findId: string, findGroupModel: DynamicFormControlModel[], findArrayIndex): void => { for (const controlModel of findGroupModel) { if (controlModel.id === findId) { if (this.isArrayGroup(controlModel) && isNotNull(findArrayIndex)) { result = (controlModel as DynamicFormArrayModel).get(findArrayIndex); } else { result = controlModel; } break; } if (this.isConcatGroup(controlModel)) { const concatGroupId = controlModel.id.replace(CONCAT_GROUP_SUFFIX, ''); // if (concatGroupId === findId) { if (concatGroupId.includes(findId)) { result = (controlModel as DynamicConcatModel).group[0]; break; } } if (this.isGroup(controlModel)) { findByIdFn(findId, (controlModel as DynamicFormGroupModel).group, findArrayIndex); } if (this.isArrayGroup(controlModel) && (isNull(findArrayIndex) || (controlModel as DynamicFormArrayModel).size > (findArrayIndex))) { const index = (isNull(findArrayIndex)) ? 0 : findArrayIndex; findByIdFn(findId, (controlModel as DynamicFormArrayModel).get(index).group, index); } } }; findByIdFn(id, groupModel, arrayIndex); return result; } clearAllModelsValue(groupModel: DynamicFormControlModel[]): void { const iterateControlModels = (findGroupModel: DynamicFormControlModel[]): void => { for (const controlModel of findGroupModel) { if (this.isGroup(controlModel)) { iterateControlModels((controlModel as DynamicFormGroupModel).group); continue; } if (this.isArrayGroup(controlModel)) { iterateControlModels((controlModel as DynamicFormArrayModel).groupFactory()); continue; } if (controlModel.hasOwnProperty('valueUpdates')) { (controlModel as any).valueUpdates.next(undefined); } } }; iterateControlModels(groupModel); } getValueFromModel(groupModel: DynamicFormControlModel[]): void { let result = Object.create({}); const customizer = (objValue, srcValue) => { if (Array.isArray(objValue)) { return objValue.concat(srcValue); } }; const normalizeValue = (controlModel, controlValue, controlModelIndex) => { const controlLanguage = (controlModel as DsDynamicInputModel).hasLanguages ? (controlModel as DsDynamicInputModel).language : null; if (isString(controlValue)) { return new FormFieldMetadataValueObject(controlValue, controlLanguage, null, null, controlModelIndex); } else if (isObject(controlValue)) { const authority = (controlValue as any).authority || (controlValue as any).id || null; const place = controlModelIndex || (controlValue as any).place; if (isNgbDateStruct(controlValue)) { return new FormFieldMetadataValueObject(controlValue, controlLanguage, authority, controlValue as any, place); } else { return new FormFieldMetadataValueObject((controlValue as any).value, controlLanguage, authority, (controlValue as any).display, place, (controlValue as any).confidence); } } }; const iterateControlModels = (findGroupModel: DynamicFormControlModel[], controlModelIndex: number = 0): void => { let iterateResult = Object.create({}); // Iterate over all group's controls for (const controlModel of findGroupModel) { if (this.isRowGroup(controlModel) && !this.isCustomOrListGroup(controlModel)) { iterateResult = mergeWith(iterateResult, iterateControlModels((controlModel as DynamicFormGroupModel).group), customizer); continue; } if (this.isGroup(controlModel) && !this.isCustomOrListGroup(controlModel)) { iterateResult[controlModel.name] = iterateControlModels((controlModel as DynamicFormGroupModel).group); continue; } if (this.isRowArrayGroup(controlModel)) { for (const arrayItemModel of (controlModel as DynamicRowArrayModel).groups) { iterateResult = mergeWith(iterateResult, iterateControlModels(arrayItemModel.group, arrayItemModel.index), customizer); } continue; } if (this.isArrayGroup(controlModel)) { iterateResult[controlModel.name] = []; for (const arrayItemModel of (controlModel as DynamicFormArrayModel).groups) { iterateResult[controlModel.name].push(iterateControlModels(arrayItemModel.group, arrayItemModel.index)); } continue; } let controlId; // Get the field's name if (this.isQualdropGroup(controlModel)) { // If is instance of DynamicQualdropModel take the qualdrop id as field's name controlId = (controlModel as DynamicQualdropModel).qualdropId; } else { controlId = controlModel.name; } if (this.isRelationGroup(controlModel)) { const values = (controlModel as DynamicRelationGroupModel).getGroupValue(); values.forEach((groupValue, groupIndex) => { const newGroupValue = Object.create({}); Object.keys(groupValue) .forEach((key) => { const normValue = normalizeValue(controlModel, groupValue[key], groupIndex); if (isNotEmpty(normValue) && normValue.hasValue()) { if (iterateResult.hasOwnProperty(key)) { iterateResult[key].push(normValue); } else { iterateResult[key] = [normValue]; } } }); }); } else if (isNotUndefined((controlModel as any).value) && isNotEmpty((controlModel as any).value)) { const controlArrayValue = []; // Normalize control value as an array of FormFieldMetadataValueObject const values = Array.isArray((controlModel as any).value) ? (controlModel as any).value : [(controlModel as any).value]; values.forEach((controlValue) => { controlArrayValue.push(normalizeValue(controlModel, controlValue, controlModelIndex)); }); if (controlId && iterateResult.hasOwnProperty(controlId) && isNotNull(iterateResult[controlId])) { iterateResult[controlId] = iterateResult[controlId].concat(controlArrayValue); } else { iterateResult[controlId] = isNotEmpty(controlArrayValue) ? controlArrayValue : null; } } } return iterateResult; }; result = iterateControlModels(groupModel); return result; } modelFromConfiguration(submissionId: string, json: string | SubmissionFormsModel, scopeUUID: string, sectionData: any = {}, submissionScope?: string, readOnly = false): DynamicFormControlModel[] | never { let rows: DynamicFormControlModel[] = []; const rawData = typeof json === 'string' ? JSON.parse(json, parseReviver) : json; if (rawData.rows && !isEmpty(rawData.rows)) { rawData.rows.forEach((currentRow) => { const rowParsed = this.rowParser.parse(submissionId, currentRow, scopeUUID, sectionData, submissionScope, readOnly); if (isNotNull(rowParsed)) { if (Array.isArray(rowParsed)) { rows = rows.concat(rowParsed); } else { rows.push(rowParsed); } } }); } return rows; } isModelInCustomGroup(model: DynamicFormControlModel): boolean { return this.isCustomGroup((model as any).parent); } hasArrayGroupValue(model: DynamicFormControlModel): boolean { return model && (this.isListGroup(model) || model.type === DYNAMIC_FORM_CONTROL_TYPE_TAG || model.type === DYNAMIC_FORM_CONTROL_TYPE_ARRAY); } hasMappedGroupValue(model: DynamicFormControlModel): boolean { return (this.isQualdropGroup((model as any).parent) || this.isRelationGroup((model as any).parent)); } isGroup(model: DynamicFormControlModel): boolean { return model && (model.type === DYNAMIC_FORM_CONTROL_TYPE_GROUP || model.type === DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP); } isQualdropGroup(model: DynamicFormControlModel): boolean { return (model && model.type === DYNAMIC_FORM_CONTROL_TYPE_GROUP && hasValue((model as any).qualdropId)); } isCustomGroup(model: DynamicFormControlModel): boolean { return model && ((model as any).type === DYNAMIC_FORM_CONTROL_TYPE_GROUP && (model as any).isCustomGroup === true); } isConcatGroup(model: DynamicFormControlModel): boolean { return this.isCustomGroup(model) && (model.id.indexOf(CONCAT_GROUP_SUFFIX) !== -1); } isRowGroup(model: DynamicFormControlModel): boolean { return model && ((model as any).type === DYNAMIC_FORM_CONTROL_TYPE_GROUP && (model as any).isRowGroup === true); } isCustomOrListGroup(model: DynamicFormControlModel): boolean { return model && (this.isCustomGroup(model) || this.isListGroup(model)); } isListGroup(model: DynamicFormControlModel): boolean { return model && ((model.type === DYNAMIC_FORM_CONTROL_TYPE_CHECKBOX_GROUP && (model as any).isListGroup === true) || (model.type === DYNAMIC_FORM_CONTROL_TYPE_RADIO_GROUP && (model as any).isListGroup === true)); } isRelationGroup(model: DynamicFormControlModel): boolean { return model && model.type === DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP; } isRowArrayGroup(model: DynamicFormControlModel): boolean { return model.type === DYNAMIC_FORM_CONTROL_TYPE_ARRAY && (model as any).isRowArray === true; } isArrayGroup(model: DynamicFormControlModel): boolean { return model.type === DYNAMIC_FORM_CONTROL_TYPE_ARRAY; } isInputModel(model: DynamicFormControlModel): boolean { return model.type === DYNAMIC_FORM_CONTROL_TYPE_INPUT; } getFormControlById(id: string, formGroup: FormGroup, groupModel: DynamicFormControlModel[], index = 0): AbstractControl { const fieldModel = this.findById(id, groupModel, index); return isNotEmpty(fieldModel) ? formGroup.get(this.getPath(fieldModel)) : null; } /** * Note (discovered while debugging) this is not the ID as used in the form, * but the first part of the path needed in a patch operation: * e.g. add foo/0 -> the id is 'foo' */ getId(model: DynamicPathable): string { let tempModel: DynamicFormControlModel; if (this.isArrayGroup(model as DynamicFormControlModel)) { return hasValue((model as any).metadataKey) ? (model as any).metadataKey : model.index.toString(); } else if (this.isModelInCustomGroup(model as DynamicFormControlModel)) { tempModel = (model as any).parent; } else { tempModel = (model as any); } return (tempModel.id !== tempModel.name) ? tempModel.name : tempModel.id; } /** * Calculate the metadata list related to the event. * @param event */ getMetadataIdsFromEvent(event: DynamicFormControlEvent): string[] { let model = event.model; while (model.parent) { model = model.parent as any; } const iterateControlModels = (findGroupModel: DynamicFormControlModel[], controlModelIndex: number = 0): string[] => { let iterateResult = Object.create({}); // Iterate over all group's controls for (const controlModel of findGroupModel) { if (this.isRowGroup(controlModel) && !this.isCustomOrListGroup(controlModel)) { iterateResult = mergeWith(iterateResult, iterateControlModels((controlModel as DynamicFormGroupModel).group)); continue; } if (this.isGroup(controlModel) && !this.isCustomOrListGroup(controlModel)) { iterateResult[controlModel.name] = iterateControlModels((controlModel as DynamicFormGroupModel).group); continue; } if (this.isRowArrayGroup(controlModel)) { for (const arrayItemModel of (controlModel as DynamicRowArrayModel).groups) { iterateResult = mergeWith(iterateResult, iterateControlModels(arrayItemModel.group, arrayItemModel.index)); } continue; } if (this.isArrayGroup(controlModel)) { iterateResult[controlModel.name] = []; for (const arrayItemModel of (controlModel as DynamicFormArrayModel).groups) { iterateResult[controlModel.name].push(iterateControlModels(arrayItemModel.group, arrayItemModel.index)); } continue; } let controlId; // Get the field's name if (this.isQualdropGroup(controlModel)) { // If is instance of DynamicQualdropModel take the qualdrop id as field's name controlId = (controlModel as DynamicQualdropModel).qualdropId; } else { controlId = controlModel.name; } if (this.isRelationGroup(controlModel)) { const values = (controlModel as DynamicRelationGroupModel).getGroupValue(); values.forEach((groupValue, groupIndex) => { Object.keys(groupValue).forEach((key) => { iterateResult[key] = true; }); }); } else { iterateResult[controlId] = true; } } return iterateResult; }; const result = iterateControlModels([model]); return result; } }