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..2cfaf0ce8d 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'; @@ -85,7 +82,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 = DsDynamicTypeBindRelationService.getTypeBindRelations(['boundType'], 'dc.type'); const relatedModels = service.getRelatedFormModel(testModel); expect(relatedModels).toHaveSize(1); }); @@ -94,7 +91,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 = DsDynamicTypeBindRelationService.getTypeBindRelations(['boundType'], 'dc.type'); const dcTypeControl = new UntypedFormControl(); dcTypeControl.setValue('boundType'); let subscriptions = service.subscribeRelations(testModel, dcTypeControl); @@ -103,7 +100,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 = DsDynamicTypeBindRelationService.getTypeBindRelations(['boundType'], 'dc.type'); const dcTypeControl = new UntypedFormControl(); dcTypeControl.setValue('boundType'); testModel.typeBindRelations[0].when[0].value = 'anotherType'; @@ -118,7 +115,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 = DsDynamicTypeBindRelationService.getTypeBindRelations(['boundType'], 'dc.type'); const dcTypeControl = new UntypedFormControl(); dcTypeControl.setValue('boundType'); testModel.typeBindRelations[0].when[0].value = 'boundType'; @@ -134,18 +131,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 fd80e7ba5a..7441dad55d 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_DISABLED, MATCH_ENABLED, MATCH_VISIBLE, OR_OPERATOR, @@ -36,6 +35,48 @@ import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './ds-dynamic-form-cons @Injectable({ providedIn: 'root' }) export class DsDynamicTypeBindRelationService { + /** + * 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 + */ + public static 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, + }, + ]; + } + constructor(@Optional() @Inject(DYNAMIC_MATCHERS) private dynamicMatchers: DynamicFormControlMatcher[], protected dynamicFormRelationService: DynamicFormRelationService, protected formBuilderService: FormBuilderService, @@ -218,30 +259,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_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 3ea371083d..17d3a0c564 100644 --- a/src/app/shared/form/builder/parsers/field-parser.ts +++ b/src/app/shared/form/builder/parsers/field-parser.ts @@ -2,14 +2,7 @@ import { Inject, InjectionToken, } from '@angular/core'; -import { - DynamicFormControlLayout, - DynamicFormControlRelation, - MATCH_DISABLED, - MATCH_ENABLED, - 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'; @@ -22,6 +15,7 @@ import { isNotNull, isNotUndefined, } from '../../../empty.util'; +import { DsDynamicTypeBindRelationService } from '../ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service'; import { DsDynamicInputModel, DsDynamicInputModelConfig, @@ -100,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) ? DsDynamicTypeBindRelationService.getTypeBindRelations(this.configData.typeBind, this.parserOptions.typeField) : null, groupFactory: () => { let model; @@ -329,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 = DsDynamicTypeBindRelationService.getTypeBindRelations(this.configData.typeBind, this.parserOptions.typeField); } @@ -358,45 +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_ENABLED, - operator: OR_OPERATOR, - when: bindValues, - }, - { - match: MATCH_VISIBLE, - operator: OR_OPERATOR, - when: bindValues, - }, - ]; - } - protected hasRegex() { return hasValue(this.configData.input.regex); }