|
@@ -22,7 +22,7 @@
|
|
|
*
|
|
|
*/
|
|
|
|
|
|
-import { FormBuilder, FormGroup, FormArray, FormControl, AbstractControlOptions } from '@angular/forms';
|
|
|
+import { FormBuilder, FormGroup, FormArray, FormControl, AbstractControl, AbstractControlOptions } from '@angular/forms';
|
|
|
import { cloneDeep, omit, reduce } from 'lodash/fp';
|
|
|
import * as fmdModels from '../models/field.model';
|
|
|
|
|
@@ -93,7 +93,7 @@ const combineExtraMeta = (metaG, extraMeta, createFromExtra = false, containerSe
|
|
|
)
|
|
|
};
|
|
|
combinedMeta[key] = combineMetaForField(metaFoG, {}, extra);
|
|
|
-
|
|
|
+
|
|
|
|
|
|
combinedMeta[key].__containerTemplate = combineExtraMeta(
|
|
|
cloneDeep(baseObjWithAllKeys),
|
|
@@ -138,7 +138,7 @@ const generateRepeatedGroup = (metaFoG, extraMeta, baseObjWithAllKeys): StringMa
|
|
|
metaFoG.meta = metaFoG.meta.map( rcMem => ({ ...rcMem, meta: { ...baseObjWithAllKeys, ...rcMem.meta } }) );
|
|
|
|
|
|
|
|
|
- const repeatedGroup = repeatInAutoMeta ?
|
|
|
+ const repeatedGroup = repeatInAutoMeta ?
|
|
|
[ ...metaFoG.meta, ...Array(repeat - repeatInAutoMeta).fill({ meta: baseObjWithAllKeys }) ] :
|
|
|
Array(repeat).fill({ meta: baseObjWithAllKeys });
|
|
|
const fullyNamedRepeatedGroup = repeatedGroup.map((rgMem, i) => rgMem.name ? rgMem : { name: `group${i + 1}`, ...rgMem });
|
|
@@ -179,7 +179,7 @@ const buildFieldSpecificMetaInClosure = (metaG, context) => {
|
|
|
}
|
|
|
return 'text';
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
const buildFieldClassName = (t: string): string => {
|
|
|
const start = t[0].toUpperCase() + t.slice(1);
|
|
|
if (start === 'Container' || start === 'RepeatingContainer' || start === 'Heading' || t.slice(-5) === 'Group') {
|
|
@@ -187,7 +187,7 @@ const buildFieldSpecificMetaInClosure = (metaG, context) => {
|
|
|
}
|
|
|
return start + 'Field';
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
const buildModeledField = (metaFoG) => {
|
|
|
const type = resolveType(metaFoG);
|
|
|
const className = buildFieldClassName(type);
|
|
@@ -196,7 +196,7 @@ const buildFieldSpecificMetaInClosure = (metaG, context) => {
|
|
|
}
|
|
|
return new fmdModels[className](metaFoG, context);
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
|
|
|
const buildModeledFieldGroupMember = (metaFoG) => {
|
|
|
const modeledGroupMember = buildModeledField(metaFoG);
|
|
@@ -213,11 +213,11 @@ const buildFieldSpecificMetaInClosure = (metaG, context) => {
|
|
|
}
|
|
|
return modeledGroupMember;
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
|
|
|
const buildModeledFieldGroupReducerIteree = (res, metaFoG) => ({ ...res, [metaFoG.name]: buildModeledFieldGroupMember(metaFoG) });
|
|
|
const _buildFieldSpecificMeta = metaG => isRepeatingContainer(metaG) ?
|
|
|
- metaG.map(rcMem => _buildFieldSpecificMeta(rcMem)) :
|
|
|
+ metaG.map(rcMem => _buildFieldSpecificMeta(rcMem)) :
|
|
|
reduce(buildModeledFieldGroupReducerIteree, {}, metaG);
|
|
|
const buildFieldSpecificMeta = metaG => _buildFieldSpecificMeta(addMissingNames(metaG));
|
|
|
|
|
@@ -355,36 +355,51 @@ const extractFieldMappings = (metaG, parentPath = '') => Object.entries(metaG)
|
|
|
|
|
|
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
const buildFormGroupFunctionFactory = (fb: FormBuilder): (meta) => FormGroup => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const buildControlState = metaF => ({ value: metaF.value || metaF.default, disabled: metaF.disabled });
|
|
|
- const buildValidators = (metaF): AbstractControlOptions => ({
|
|
|
- validators: metaF.validators,
|
|
|
- asyncValidators: metaF.asyncValidators,
|
|
|
+ const buildValidators = (metaFoG): AbstractControlOptions => ({
|
|
|
+ validators: metaFoG.validators,
|
|
|
+ asyncValidators: metaFoG.asyncValidators,
|
|
|
|
|
|
- updateOn: metaF.type === 'text' || metaF.type === 'textarea' ? 'blur' : 'change'
|
|
|
+ updateOn: getUpdateOn(metaFoG.type)
|
|
|
});
|
|
|
+ const BVAL = metaFoG => {
|
|
|
+ if (!metaFoG || !(metaFoG.validators || metaFoG.asyncValidators)) {
|
|
|
+ return undefined;
|
|
|
+ }
|
|
|
+
|
|
|
+ const res = buildValidators(metaFoG);
|
|
|
+
|
|
|
+ return res;
|
|
|
+ }
|
|
|
const buildFormControl = metaF => new FormControl(buildControlState(metaF), buildValidators(metaF));
|
|
|
|
|
|
|
|
|
- const buildFormArray = (metaG): FormArray => {
|
|
|
+ const buildFormArray = (metaG, grMeta?): FormArray => {
|
|
|
return fb.array(
|
|
|
- metaG.map(m => isContainer(m) ? _buildFormGroup(m.meta) : buildFormControl(m))
|
|
|
+ metaG.map(m => isContainer(m) ? _buildFormGroup(m.meta) : buildFormControl(m)),
|
|
|
+ buildValidators(grMeta)
|
|
|
);
|
|
|
};
|
|
|
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
const buildFormGroupMember = metaFoG => isGroup(metaFoG) ?
|
|
|
- (isArray(metaFoG.meta) ? buildFormArray(metaFoG.meta) : _buildFormGroup(metaFoG.meta)) :
|
|
|
+ (isArray(metaFoG.meta) ? buildFormArray(metaFoG.meta, metaFoG) : _buildFormGroup(metaFoG.meta, metaFoG)) :
|
|
|
buildFormControl(metaFoG);
|
|
|
|
|
|
const buildFormGroupReducerIteree = (res, metaFoG) => {
|
|
|
return metaFoG.noFormControls ? res : { ...res, [metaFoG.name]: buildFormGroupMember(metaFoG) };
|
|
|
};
|
|
|
- const _buildFormGroup = _metaG => fb.group(reduce(buildFormGroupReducerIteree, {}, _metaG));
|
|
|
+ const _buildFormGroup = (_metaG, grMeta?) => fb.group(reduce(buildFormGroupReducerIteree, {}, _metaG), BVAL(grMeta));
|
|
|
|
|
|
|
|
|
const buildFormGroup = metaG => {
|
|
@@ -398,6 +413,72 @@ const buildFormGroupFunctionFactory = (fb: FormBuilder): (meta) => FormGroup =>
|
|
|
return buildFormGroup;
|
|
|
};
|
|
|
|
|
|
+
|
|
|
+const getUpdateOn = (type: string): 'blur'|'change'|'submit' => {
|
|
|
+ const t = type.toLowerCase();
|
|
|
+ let res;
|
|
|
+ if (t === 'text' || t === 'textarea' || t === 'password') {
|
|
|
+ res = 'blur';
|
|
|
+ } else {
|
|
|
+ res = 'change';
|
|
|
+ }
|
|
|
+ return res;
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+const touchAndUpdateValidityRecursive = (group: FormGroup | FormArray): void => {
|
|
|
+ group.markAsTouched();
|
|
|
+ group.updateValueAndValidity();
|
|
|
+ for (const key in group.controls) {
|
|
|
+ if (group.controls[key] instanceof FormControl) {
|
|
|
+ group.controls[key].markAsTouched();
|
|
|
+ group.controls[key].updateValueAndValidity();
|
|
|
+ } else {
|
|
|
+ touchAndUpdateValidityRecursive(group.controls[key]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const resetValidityRecursive = (group: FormGroup | FormArray): void => {
|
|
|
+ group.markAsUntouched();
|
|
|
+ group.updateValueAndValidity();
|
|
|
+ for (const key in group.controls) {
|
|
|
+ if (group.controls[key] instanceof FormControl) {
|
|
|
+ group.controls[key].markAsUntouched();
|
|
|
+ group.controls[key].updateValueAndValidity();
|
|
|
+ } else {
|
|
|
+ resetValidityRecursive(group.controls[key]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const advanceUpdateStategyOfInvalidControlsRecursive = (group: FormGroup | FormArray): void => {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ for (const key in group.controls) {
|
|
|
+ if (group.controls[key] instanceof FormControl) {
|
|
|
+ console.log(key, group.controls[key].touched, group.controls[key].invalid);
|
|
|
+ if (group.controls[key].touched && group.controls[key].invalid) {
|
|
|
+ console.log('Replacing control', key);
|
|
|
+ const newControl = new FormControl(
|
|
|
+ group.controls[key].value,
|
|
|
+ { updateOn: 'change', validators: group.controls[key].validator },
|
|
|
+ group.controls[key].asyncValidator
|
|
|
+ );
|
|
|
+ (group as any).setControl(key as any, newControl);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ advanceUpdateStategyOfInvalidControlsRecursive(group.controls[key]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
@@ -463,16 +544,51 @@ const generateNewModel = (originalModel, updates) => {
|
|
|
return updateObject(originalModel, updates);
|
|
|
};
|
|
|
|
|
|
+const updateMeta = (newMeta: StringMap<any>, path: string, meta: StringMap<any>): StringMap<any> => {
|
|
|
+
|
|
|
+ if (path === '/') {
|
|
|
+ const updatedMeta = updateObject(meta || this.meta, newMeta, true);
|
|
|
+ return updatedMeta;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ console.log(path);
|
|
|
+ const segments = path.split('.');
|
|
|
+ console.log(segments);
|
|
|
+ let branchMeta = meta;
|
|
|
+ while(isContainer(branchMeta)) {
|
|
|
+ const s = segments.shift();
|
|
|
+ console.log(s, branchMeta[s].meta);
|
|
|
+
|
|
|
+ branchMeta = branchMeta[s].meta;
|
|
|
+ }
|
|
|
+ while(segments.length > 1) {
|
|
|
+ const s = segments.shift();
|
|
|
+ console.log(s, branchMeta[s]);
|
|
|
+
|
|
|
+ branchMeta = branchMeta[s];
|
|
|
+ }
|
|
|
+ branchMeta = branchMeta[segments[0]];
|
|
|
+ console.log(segments[0], branchMeta);
|
|
|
+
|
|
|
+
|
|
|
+ const updatedMeta = updateObject(branchMeta, newMeta, true);
|
|
|
+ branchMeta = updatedMeta;
|
|
|
+ console.log(branchMeta);
|
|
|
+ return meta;
|
|
|
+}
|
|
|
+
|
|
|
const updateObject = (obj, updates, createAdditionalKeys = false) => {
|
|
|
|
|
|
if (!isRealObject(obj)) {
|
|
|
obj = {};
|
|
|
}
|
|
|
- console.log('obj is', obj, typeof obj);
|
|
|
if (Object.keys(obj).length === 0) {
|
|
|
createAdditionalKeys = true;
|
|
|
}
|
|
|
- const shallowClone = { ...obj };
|
|
|
+ const shallowClone = { ...obj };
|
|
|
Object.entries(updates).forEach(([key, val]) => safeSet(shallowClone, key, val, createAdditionalKeys));
|
|
|
return shallowClone;
|
|
|
};
|
|
@@ -488,7 +604,7 @@ const safeSet = (obj, key, val, createAdditionalKeys = false) => {
|
|
|
}
|
|
|
|
|
|
if (undefinedNullOrScalar(currentVal)) {
|
|
|
- console.log('safeSet undefinedNullOrScalar', key, val);
|
|
|
+
|
|
|
obj[key] = val;
|
|
|
} else {
|
|
|
if (Array.isArray(currentVal)) {
|
|
@@ -576,6 +692,8 @@ const addMissingFieldSpecificMeta = metaG => Object.entries(metaG)
|
|
|
|
|
|
export {
|
|
|
autoMeta, combineModelWithMeta, combineExtraMeta, execMetaReorderingInstructions,
|
|
|
- buildFieldSpecificMetaInClosure, extractFieldMappings, buildFormGroupFunctionFactory,
|
|
|
- generateNewModel
|
|
|
+ buildFieldSpecificMetaInClosure, extractFieldMappings,
|
|
|
+ buildFormGroupFunctionFactory,
|
|
|
+ touchAndUpdateValidityRecursive, resetValidityRecursive, advanceUpdateStategyOfInvalidControlsRecursive,
|
|
|
+ generateNewModel, updateMeta,
|
|
|
};
|