Skip to content

Commit afc1475

Browse files
authored
Merge pull request DSpace#2133 from alexandrevryghem/fix-missing-hints-and-required-attributes-main
Fix missing hints and error messages for input-type `list` & `tag`
2 parents 6892489 + 441eac2 commit afc1475

8 files changed

Lines changed: 45 additions & 15 deletions

src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<ng-container #componentViewContainer></ng-container>
1717
</div>
1818

19-
<small *ngIf="hasHint && ((!model.repeatable && (isRelationship === false || value?.value === null)) || (model.repeatable === true && context?.index === context?.context?.groups?.length - 1)) && (!showErrorMessages || errorMessages.length === 0)"
19+
<small *ngIf="hasHint && (formBuilderService.hasArrayGroupValue(model) || (!model.repeatable && (isRelationship === false || value?.value === null)) || (model.repeatable === true && context?.index === context?.context?.groups?.length - 1)) && (!showErrorMessages || errorMessages.length === 0)"
2020
class="text-muted ds-hint" [innerHTML]="model.hint | translate" [ngClass]="getClass('element', 'hint')"></small>
2121
<!-- In case of repeatable fields show empty space for all elements except the first -->
2222
<div *ngIf="context?.index !== null

src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.spec.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,14 @@ describe('DsDynamicFormControlContainerComponent test suite', () => {
147147
new DynamicListCheckboxGroupModel({
148148
id: 'checkboxList',
149149
vocabularyOptions: vocabularyOptions,
150-
repeatable: true
150+
repeatable: true,
151+
required: false,
151152
}),
152153
new DynamicListRadioGroupModel({
153154
id: 'radioList',
154155
vocabularyOptions: vocabularyOptions,
155-
repeatable: false
156+
repeatable: false,
157+
required: false,
156158
}),
157159
new DynamicRelationGroupModel({
158160
submissionId: '1234',

src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
259259
private submissionObjectService: SubmissionObjectDataService,
260260
private ref: ChangeDetectorRef,
261261
private formService: FormService,
262-
private formBuilderService: FormBuilderService,
262+
public formBuilderService: FormBuilderService,
263263
private submissionService: SubmissionService,
264264
@Inject(APP_CONFIG) protected appConfig: AppConfig,
265265
) {

src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-checkbox-group.model.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ export interface DynamicListCheckboxGroupModelConfig extends DynamicFormGroupMod
1515
vocabularyOptions: VocabularyOptions;
1616
groupLength?: number;
1717
repeatable: boolean;
18-
value?: any;
18+
value?: VocabularyEntry[];
1919
typeBindRelations?: DynamicFormControlRelation[];
20+
required: boolean;
21+
hint?: string;
2022
}
2123

2224
export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel {
@@ -26,6 +28,8 @@ export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel {
2628
@serializable() groupLength: number;
2729
@serializable() _value: VocabularyEntry[];
2830
@serializable() typeBindRelations: DynamicFormControlRelation[];
31+
@serializable() required: boolean;
32+
@serializable() hint: string;
2933
isListGroup = true;
3034
valueUpdates: Subject<any>;
3135

@@ -36,6 +40,8 @@ export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel {
3640
this.groupLength = config.groupLength || 5;
3741
this._value = [];
3842
this.repeatable = config.repeatable;
43+
this.required = config.required;
44+
this.hint = config.hint;
3945

4046
this.valueUpdates = new Subject<any>();
4147
this.valueUpdates.subscribe((value: VocabularyEntry | VocabularyEntry[]) => this.value = value);
@@ -56,9 +62,8 @@ export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel {
5662
if (Array.isArray(value)) {
5763
this._value = value;
5864
} else {
59-
// _value is non extendible so assign it a new array
60-
const newValue = (this.value as VocabularyEntry[]).concat([value]);
61-
this._value = newValue;
65+
// _value is non-extendable so assign it a new array
66+
this._value = (this.value as VocabularyEntry[]).concat([value]);
6267
}
6368
}
6469
}

src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,24 @@ import {
66
} from '@ng-dynamic-forms/core';
77
import { VocabularyOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-options.model';
88
import { hasValue } from '../../../../../empty.util';
9+
import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model';
910

1011
export interface DynamicListModelConfig extends DynamicRadioGroupModelConfig<any> {
1112
vocabularyOptions: VocabularyOptions;
1213
groupLength?: number;
1314
repeatable: boolean;
14-
value?: any;
15+
value?: VocabularyEntry[];
16+
required: boolean;
17+
hint?: string;
1518
}
1619

1720
export class DynamicListRadioGroupModel extends DynamicRadioGroupModel<any> {
1821

1922
@serializable() vocabularyOptions: VocabularyOptions;
2023
@serializable() repeatable: boolean;
2124
@serializable() groupLength: number;
25+
@serializable() required: boolean;
26+
@serializable() hint: string;
2227
isListGroup = true;
2328

2429
constructor(config: DynamicListModelConfig, layout?: DynamicFormControlLayout) {
@@ -27,6 +32,8 @@ export class DynamicListRadioGroupModel extends DynamicRadioGroupModel<any> {
2732
this.vocabularyOptions = config.vocabularyOptions;
2833
this.groupLength = config.groupLength || 5;
2934
this.repeatable = config.repeatable;
35+
this.required = config.required;
36+
this.hint = config.hint;
3037
this.value = config.value;
3138
}
3239

src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
[id]="item.id"
1818
[formControlName]="item.id"
1919
[name]="model.name"
20-
[required]="model.required"
2120
[value]="item.value"
2221
(blur)="onBlur($event)"
2322
(change)="onChange($event)"

src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
2-
import { FormGroup } from '@angular/forms';
3-
2+
import { FormGroup, ValidatorFn, ValidationErrors, AbstractControl } from '@angular/forms';
43
import {
54
DynamicCheckboxModel,
65
DynamicFormControlComponent,
@@ -110,6 +109,9 @@ export class DsDynamicListComponent extends DynamicFormControlComponent implemen
110109
protected setOptionsFromVocabulary() {
111110
if (this.model.vocabularyOptions.name && this.model.vocabularyOptions.name.length > 0) {
112111
const listGroup = this.group.controls[this.model.id] as FormGroup;
112+
if (this.model.repeatable && this.model.required) {
113+
listGroup.addValidators(this.hasAtLeastOneVocabularyEntry());
114+
}
113115
const pageInfo: PageInfo = new PageInfo({
114116
elementsPerPage: 9999, currentPage: 1
115117
} as PageInfo);
@@ -121,7 +123,7 @@ export class DsDynamicListComponent extends DynamicFormControlComponent implemen
121123
let tempList: ListItem[] = [];
122124
this.optionsList = entries.page;
123125
// Make a list of available options (checkbox/radio) and split in groups of 'model.groupLength'
124-
entries.page.forEach((option, key) => {
126+
entries.page.forEach((option: VocabularyEntry, key: number) => {
125127
const value = option.authority || option.value;
126128
const checked: boolean = isNotEmpty(findKey(
127129
this.model.value,
@@ -156,4 +158,13 @@ export class DsDynamicListComponent extends DynamicFormControlComponent implemen
156158
}
157159
}
158160

161+
/**
162+
* Checks if at least one {@link VocabularyEntry} has been selected.
163+
*/
164+
hasAtLeastOneVocabularyEntry(): ValidatorFn {
165+
return (control: AbstractControl): ValidationErrors | null => {
166+
return control && control.value && Object.values(control.value).find((checked: boolean) => checked === true) ? null : this.model.errorMessages;
167+
};
168+
}
169+
159170
}

src/app/shared/form/builder/form-builder.service.spec.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,10 +235,16 @@ describe('FormBuilderService test suite', () => {
235235
new DynamicListCheckboxGroupModel({
236236
id: 'testCheckboxList',
237237
vocabularyOptions: vocabularyOptions,
238-
repeatable: true
238+
repeatable: true,
239+
required: false,
239240
}),
240241

241-
new DynamicListRadioGroupModel({ id: 'testRadioList', vocabularyOptions: vocabularyOptions, repeatable: false }),
242+
new DynamicListRadioGroupModel({
243+
id: 'testRadioList',
244+
vocabularyOptions: vocabularyOptions,
245+
repeatable: false,
246+
required: false,
247+
}),
242248

243249
new DynamicRelationGroupModel({
244250
submissionId,

0 commit comments

Comments
 (0)