diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.spec.ts index a54e379cac..883da2295a 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.spec.ts @@ -9,12 +9,9 @@ import { } from '@angular/forms'; import { DISABLED_MATCHER_PROVIDER, - DynamicFormControlRelation, DynamicFormRelationService, HIDDEN_MATCHER, HIDDEN_MATCHER_PROVIDER, - MATCH_VISIBLE, - OR_OPERATOR, REQUIRED_MATCHER_PROVIDER, } from '@ng-dynamic-forms/core'; @@ -26,6 +23,7 @@ import { import { FormBuilderService } from '../form-builder.service'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { DsDynamicTypeBindRelationService } from './ds-dynamic-type-bind-relation.service'; +import { getTypeBindRelations } from './type-bind.utils'; describe('DSDynamicTypeBindRelationService test suite', () => { let service: DsDynamicTypeBindRelationService; @@ -85,7 +83,7 @@ describe('DSDynamicTypeBindRelationService test suite', () => { }); it('Should get 1 related form models for mock relation model data', () => { const testModel = mockInputWithTypeBindModel; - testModel.typeBindRelations = getTypeBindRelations(['boundType']); + testModel.typeBindRelations = getTypeBindRelations(['boundType'], 'dc.type'); const relatedModels = service.getRelatedFormModel(testModel); expect(relatedModels).toHaveSize(1); }); @@ -94,7 +92,7 @@ describe('DSDynamicTypeBindRelationService test suite', () => { describe('Test matchesCondition method', () => { it('Should receive one subscription to dc.type type binding"', () => { const testModel = mockInputWithTypeBindModel; - testModel.typeBindRelations = getTypeBindRelations(['boundType']); + testModel.typeBindRelations = getTypeBindRelations(['boundType'], 'dc.type'); const dcTypeControl = new UntypedFormControl(); dcTypeControl.setValue('boundType'); let subscriptions = service.subscribeRelations(testModel, dcTypeControl); @@ -103,7 +101,7 @@ describe('DSDynamicTypeBindRelationService test suite', () => { it('Expect hasMatch to be true (ie. this should be hidden)', () => { const testModel = mockInputWithTypeBindModel; - testModel.typeBindRelations = getTypeBindRelations(['boundType']); + testModel.typeBindRelations = getTypeBindRelations(['boundType'], 'dc.type'); const dcTypeControl = new UntypedFormControl(); dcTypeControl.setValue('boundType'); testModel.typeBindRelations[0].when[0].value = 'anotherType'; @@ -118,7 +116,7 @@ describe('DSDynamicTypeBindRelationService test suite', () => { it('Expect hasMatch to be false (ie. this should NOT be hidden)', () => { const testModel = mockInputWithTypeBindModel; - testModel.typeBindRelations = getTypeBindRelations(['boundType']); + testModel.typeBindRelations = getTypeBindRelations(['boundType'], 'dc.type'); const dcTypeControl = new UntypedFormControl(); dcTypeControl.setValue('boundType'); testModel.typeBindRelations[0].when[0].value = 'boundType'; @@ -134,18 +132,3 @@ describe('DSDynamicTypeBindRelationService test suite', () => { }); }); - -function getTypeBindRelations(configuredTypeBindValues: string[]): DynamicFormControlRelation[] { - const bindValues = []; - configuredTypeBindValues.forEach((value) => { - bindValues.push({ - id: 'dc.type', - value: value, - }); - }); - return [{ - match: MATCH_VISIBLE, - operator: OR_OPERATOR, - when: bindValues, - }]; -} diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts index 03ca4b26cf..4f8cff747e 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts @@ -13,7 +13,6 @@ import { DynamicFormControlModel, DynamicFormControlRelation, DynamicFormRelationService, - MATCH_VISIBLE, OR_OPERATOR, } from '@ng-dynamic-forms/core'; import { Subscription } from 'rxjs'; @@ -216,23 +215,4 @@ export class DsDynamicTypeBindRelationService { return subscriptions; } - /** - * Helper function to construct a typeBindRelations array - * @param configuredTypeBindValues - */ - public getTypeBindRelations(configuredTypeBindValues: string[]): DynamicFormControlRelation[] { - const bindValues = []; - configuredTypeBindValues.forEach((value) => { - bindValues.push({ - id: 'dc.type', - value: value, - }); - }); - return [{ - match: MATCH_VISIBLE, - operator: OR_OPERATOR, - when: bindValues, - }]; - } - } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/type-bind.utils.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/type-bind.utils.ts new file mode 100644 index 0000000000..1d09e9fafb --- /dev/null +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/type-bind.utils.ts @@ -0,0 +1,48 @@ +import { + DynamicFormControlRelation, + MATCH_ENABLED, + MATCH_VISIBLE, + OR_OPERATOR, +} from '@ng-dynamic-forms/core'; + +/** + * Get the type bind values from the REST data for a specific field + * The return value is any[] in the method signature but in reality it's + * returning the 'relation' that'll be used for a dynamic matcher when filtering + * fields in type bind, made up of a 'match' outcome (make this field visible), an 'operator' + * (OR) and a 'when' condition (the bindValues array). + * @param configuredTypeBindValues array of types from the submission definition (CONFIG_DATA) + * @param typeField + * @private + * @return DynamicFormControlRelation[] array with one relation in it, for type bind matching to show a field + */ +export function getTypeBindRelations(configuredTypeBindValues: string[], typeField: string): DynamicFormControlRelation[] { + const bindValues = []; + configuredTypeBindValues.forEach((value) => { + bindValues.push({ + id: typeField, + value: value, + }); + }); + // match: MATCH_VISIBLE means that if true, the field / component will be visible + // operator: OR means that all the values in the 'when' condition will be compared with OR, not AND + // when: the list of values to match against, in this case the list of strings from ... + // Example: Field [x] will be VISIBLE if item type = book OR item type = book_part + // + // The opposing match value will be the dc.type for the workspace item + // + // MATCH_ENABLED is now also returned, so that hidden type-bound fields that are 'required' + // do not trigger false validation errors + return [ + { + match: MATCH_ENABLED, + operator: OR_OPERATOR, + when: bindValues, + }, + { + match: MATCH_VISIBLE, + operator: OR_OPERATOR, + when: bindValues, + }, + ]; +} diff --git a/src/app/shared/form/builder/parsers/field-parser.ts b/src/app/shared/form/builder/parsers/field-parser.ts index c634c7d876..e4543bcff8 100644 --- a/src/app/shared/form/builder/parsers/field-parser.ts +++ b/src/app/shared/form/builder/parsers/field-parser.ts @@ -2,12 +2,7 @@ import { Inject, InjectionToken, } from '@angular/core'; -import { - DynamicFormControlLayout, - DynamicFormControlRelation, - MATCH_VISIBLE, - OR_OPERATOR, -} from '@ng-dynamic-forms/core'; +import { DynamicFormControlLayout } from '@ng-dynamic-forms/core'; import { TranslateService } from '@ngx-translate/core'; import uniqueId from 'lodash/uniqueId'; @@ -28,6 +23,7 @@ import { DynamicRowArrayModel, DynamicRowArrayModelConfig, } from '../ds-dynamic-form-ui/models/ds-dynamic-row-array-model'; +import { getTypeBindRelations } from '../ds-dynamic-form-ui/type-bind.utils'; import { FormFieldModel } from '../models/form-field.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { RelationshipOptions } from '../models/relationship-options.model'; @@ -98,7 +94,7 @@ export abstract class FieldParser { metadataFields: this.getAllFieldIds(), hasSelectableMetadata: isNotEmpty(this.configData.selectableMetadata), isDraggable, - typeBindRelations: isNotEmpty(this.configData.typeBind) ? this.getTypeBindRelations(this.configData.typeBind, + typeBindRelations: isNotEmpty(this.configData.typeBind) ? getTypeBindRelations(this.configData.typeBind, this.parserOptions.typeField) : null, groupFactory: () => { let model; @@ -327,7 +323,7 @@ export abstract class FieldParser { // If typeBind is configured if (isNotEmpty(this.configData.typeBind)) { - (controlModel as DsDynamicInputModel).typeBindRelations = this.getTypeBindRelations(this.configData.typeBind, + (controlModel as DsDynamicInputModel).typeBindRelations = getTypeBindRelations(this.configData.typeBind, this.parserOptions.typeField); } @@ -356,38 +352,6 @@ export abstract class FieldParser { ); } - /** - * Get the type bind values from the REST data for a specific field - * The return value is any[] in the method signature but in reality it's - * returning the 'relation' that'll be used for a dynamic matcher when filtering - * fields in type bind, made up of a 'match' outcome (make this field visible), an 'operator' - * (OR) and a 'when' condition (the bindValues array). - * @param configuredTypeBindValues array of types from the submission definition (CONFIG_DATA) - * @param typeField - * @private - * @return DynamicFormControlRelation[] array with one relation in it, for type bind matching to show a field - */ - private getTypeBindRelations(configuredTypeBindValues: string[], typeField: string): DynamicFormControlRelation[] { - const bindValues = []; - configuredTypeBindValues.forEach((value) => { - bindValues.push({ - id: typeField, - value: value, - }); - }); - // match: MATCH_VISIBLE means that if true, the field / component will be visible - // operator: OR means that all the values in the 'when' condition will be compared with OR, not AND - // when: the list of values to match against, in this case the list of strings from ... - // Example: Field [x] will be VISIBLE if item type = book OR item type = book_part - // - // The opposing match value will be the dc.type for the workspace item - return [{ - match: MATCH_VISIBLE, - operator: OR_OPERATOR, - when: bindValues, - }]; - } - protected hasRegex() { return hasValue(this.configData.input.regex); }