Skip to content

Commit 3bf7e92

Browse files
authored
Merge pull request #4769 from DSpace/backport-4725-to-dspace-9_x
[Port dspace-9_x] Fix type-bind submission form validation handling
2 parents 31698c1 + 589765c commit 3bf7e92

File tree

4 files changed

+57
-82
lines changed

4 files changed

+57
-82
lines changed

src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.spec.ts

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,9 @@ import {
99
} from '@angular/forms';
1010
import {
1111
DISABLED_MATCHER_PROVIDER,
12-
DynamicFormControlRelation,
1312
DynamicFormRelationService,
1413
HIDDEN_MATCHER,
1514
HIDDEN_MATCHER_PROVIDER,
16-
MATCH_VISIBLE,
17-
OR_OPERATOR,
1815
REQUIRED_MATCHER_PROVIDER,
1916
} from '@ng-dynamic-forms/core';
2017

@@ -26,6 +23,7 @@ import {
2623
import { FormBuilderService } from '../form-builder.service';
2724
import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model';
2825
import { DsDynamicTypeBindRelationService } from './ds-dynamic-type-bind-relation.service';
26+
import { getTypeBindRelations } from './type-bind.utils';
2927

3028
describe('DSDynamicTypeBindRelationService test suite', () => {
3129
let service: DsDynamicTypeBindRelationService;
@@ -85,7 +83,7 @@ describe('DSDynamicTypeBindRelationService test suite', () => {
8583
});
8684
it('Should get 1 related form models for mock relation model data', () => {
8785
const testModel = mockInputWithTypeBindModel;
88-
testModel.typeBindRelations = getTypeBindRelations(['boundType']);
86+
testModel.typeBindRelations = getTypeBindRelations(['boundType'], 'dc.type');
8987
const relatedModels = service.getRelatedFormModel(testModel);
9088
expect(relatedModels).toHaveSize(1);
9189
});
@@ -94,7 +92,7 @@ describe('DSDynamicTypeBindRelationService test suite', () => {
9492
describe('Test matchesCondition method', () => {
9593
it('Should receive one subscription to dc.type type binding"', () => {
9694
const testModel = mockInputWithTypeBindModel;
97-
testModel.typeBindRelations = getTypeBindRelations(['boundType']);
95+
testModel.typeBindRelations = getTypeBindRelations(['boundType'], 'dc.type');
9896
const dcTypeControl = new UntypedFormControl();
9997
dcTypeControl.setValue('boundType');
10098
let subscriptions = service.subscribeRelations(testModel, dcTypeControl);
@@ -103,7 +101,7 @@ describe('DSDynamicTypeBindRelationService test suite', () => {
103101

104102
it('Expect hasMatch to be true (ie. this should be hidden)', () => {
105103
const testModel = mockInputWithTypeBindModel;
106-
testModel.typeBindRelations = getTypeBindRelations(['boundType']);
104+
testModel.typeBindRelations = getTypeBindRelations(['boundType'], 'dc.type');
107105
const dcTypeControl = new UntypedFormControl();
108106
dcTypeControl.setValue('boundType');
109107
testModel.typeBindRelations[0].when[0].value = 'anotherType';
@@ -118,7 +116,7 @@ describe('DSDynamicTypeBindRelationService test suite', () => {
118116

119117
it('Expect hasMatch to be false (ie. this should NOT be hidden)', () => {
120118
const testModel = mockInputWithTypeBindModel;
121-
testModel.typeBindRelations = getTypeBindRelations(['boundType']);
119+
testModel.typeBindRelations = getTypeBindRelations(['boundType'], 'dc.type');
122120
const dcTypeControl = new UntypedFormControl();
123121
dcTypeControl.setValue('boundType');
124122
testModel.typeBindRelations[0].when[0].value = 'boundType';
@@ -134,18 +132,3 @@ describe('DSDynamicTypeBindRelationService test suite', () => {
134132
});
135133

136134
});
137-
138-
function getTypeBindRelations(configuredTypeBindValues: string[]): DynamicFormControlRelation[] {
139-
const bindValues = [];
140-
configuredTypeBindValues.forEach((value) => {
141-
bindValues.push({
142-
id: 'dc.type',
143-
value: value,
144-
});
145-
});
146-
return [{
147-
match: MATCH_VISIBLE,
148-
operator: OR_OPERATOR,
149-
when: bindValues,
150-
}];
151-
}

src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
DynamicFormControlModel,
1414
DynamicFormControlRelation,
1515
DynamicFormRelationService,
16-
MATCH_VISIBLE,
1716
OR_OPERATOR,
1817
} from '@ng-dynamic-forms/core';
1918
import { Subscription } from 'rxjs';
@@ -216,23 +215,4 @@ export class DsDynamicTypeBindRelationService {
216215
return subscriptions;
217216
}
218217

219-
/**
220-
* Helper function to construct a typeBindRelations array
221-
* @param configuredTypeBindValues
222-
*/
223-
public getTypeBindRelations(configuredTypeBindValues: string[]): DynamicFormControlRelation[] {
224-
const bindValues = [];
225-
configuredTypeBindValues.forEach((value) => {
226-
bindValues.push({
227-
id: 'dc.type',
228-
value: value,
229-
});
230-
});
231-
return [{
232-
match: MATCH_VISIBLE,
233-
operator: OR_OPERATOR,
234-
when: bindValues,
235-
}];
236-
}
237-
238218
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import {
2+
DynamicFormControlRelation,
3+
MATCH_ENABLED,
4+
MATCH_VISIBLE,
5+
OR_OPERATOR,
6+
} from '@ng-dynamic-forms/core';
7+
8+
/**
9+
* Get the type bind values from the REST data for a specific field
10+
* The return value is any[] in the method signature but in reality it's
11+
* returning the 'relation' that'll be used for a dynamic matcher when filtering
12+
* fields in type bind, made up of a 'match' outcome (make this field visible), an 'operator'
13+
* (OR) and a 'when' condition (the bindValues array).
14+
* @param configuredTypeBindValues array of types from the submission definition (CONFIG_DATA)
15+
* @param typeField
16+
* @private
17+
* @return DynamicFormControlRelation[] array with one relation in it, for type bind matching to show a field
18+
*/
19+
export function getTypeBindRelations(configuredTypeBindValues: string[], typeField: string): DynamicFormControlRelation[] {
20+
const bindValues = [];
21+
configuredTypeBindValues.forEach((value) => {
22+
bindValues.push({
23+
id: typeField,
24+
value: value,
25+
});
26+
});
27+
// match: MATCH_VISIBLE means that if true, the field / component will be visible
28+
// operator: OR means that all the values in the 'when' condition will be compared with OR, not AND
29+
// when: the list of values to match against, in this case the list of strings from <type-bind>...</type-bind>
30+
// Example: Field [x] will be VISIBLE if item type = book OR item type = book_part
31+
//
32+
// The opposing match value will be the dc.type for the workspace item
33+
//
34+
// MATCH_ENABLED is now also returned, so that hidden type-bound fields that are 'required'
35+
// do not trigger false validation errors
36+
return [
37+
{
38+
match: MATCH_ENABLED,
39+
operator: OR_OPERATOR,
40+
when: bindValues,
41+
},
42+
{
43+
match: MATCH_VISIBLE,
44+
operator: OR_OPERATOR,
45+
when: bindValues,
46+
},
47+
];
48+
}

src/app/shared/form/builder/parsers/field-parser.ts

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,7 @@ import {
22
Inject,
33
InjectionToken,
44
} from '@angular/core';
5-
import {
6-
DynamicFormControlLayout,
7-
DynamicFormControlRelation,
8-
MATCH_VISIBLE,
9-
OR_OPERATOR,
10-
} from '@ng-dynamic-forms/core';
5+
import { DynamicFormControlLayout } from '@ng-dynamic-forms/core';
116
import { TranslateService } from '@ngx-translate/core';
127
import uniqueId from 'lodash/uniqueId';
138

@@ -28,6 +23,7 @@ import {
2823
DynamicRowArrayModel,
2924
DynamicRowArrayModelConfig,
3025
} from '../ds-dynamic-form-ui/models/ds-dynamic-row-array-model';
26+
import { getTypeBindRelations } from '../ds-dynamic-form-ui/type-bind.utils';
3127
import { FormFieldModel } from '../models/form-field.model';
3228
import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model';
3329
import { RelationshipOptions } from '../models/relationship-options.model';
@@ -98,7 +94,7 @@ export abstract class FieldParser {
9894
metadataFields: this.getAllFieldIds(),
9995
hasSelectableMetadata: isNotEmpty(this.configData.selectableMetadata),
10096
isDraggable,
101-
typeBindRelations: isNotEmpty(this.configData.typeBind) ? this.getTypeBindRelations(this.configData.typeBind,
97+
typeBindRelations: isNotEmpty(this.configData.typeBind) ? getTypeBindRelations(this.configData.typeBind,
10298
this.parserOptions.typeField) : null,
10399
groupFactory: () => {
104100
let model;
@@ -327,7 +323,7 @@ export abstract class FieldParser {
327323

328324
// If typeBind is configured
329325
if (isNotEmpty(this.configData.typeBind)) {
330-
(controlModel as DsDynamicInputModel).typeBindRelations = this.getTypeBindRelations(this.configData.typeBind,
326+
(controlModel as DsDynamicInputModel).typeBindRelations = getTypeBindRelations(this.configData.typeBind,
331327
this.parserOptions.typeField);
332328
}
333329

@@ -356,38 +352,6 @@ export abstract class FieldParser {
356352
);
357353
}
358354

359-
/**
360-
* Get the type bind values from the REST data for a specific field
361-
* The return value is any[] in the method signature but in reality it's
362-
* returning the 'relation' that'll be used for a dynamic matcher when filtering
363-
* fields in type bind, made up of a 'match' outcome (make this field visible), an 'operator'
364-
* (OR) and a 'when' condition (the bindValues array).
365-
* @param configuredTypeBindValues array of types from the submission definition (CONFIG_DATA)
366-
* @param typeField
367-
* @private
368-
* @return DynamicFormControlRelation[] array with one relation in it, for type bind matching to show a field
369-
*/
370-
private getTypeBindRelations(configuredTypeBindValues: string[], typeField: string): DynamicFormControlRelation[] {
371-
const bindValues = [];
372-
configuredTypeBindValues.forEach((value) => {
373-
bindValues.push({
374-
id: typeField,
375-
value: value,
376-
});
377-
});
378-
// match: MATCH_VISIBLE means that if true, the field / component will be visible
379-
// operator: OR means that all the values in the 'when' condition will be compared with OR, not AND
380-
// when: the list of values to match against, in this case the list of strings from <type-bind>...</type-bind>
381-
// Example: Field [x] will be VISIBLE if item type = book OR item type = book_part
382-
//
383-
// The opposing match value will be the dc.type for the workspace item
384-
return [{
385-
match: MATCH_VISIBLE,
386-
operator: OR_OPERATOR,
387-
when: bindValues,
388-
}];
389-
}
390-
391355
protected hasRegex() {
392356
return hasValue(this.configData.input.regex);
393357
}

0 commit comments

Comments
 (0)