Merge pull request #2133 from alexandrevryghem/fix-missing-hints-and-required-attributes-main

Fix missing hints and error messages for input-type `list` & `tag`
This commit is contained in:
Tim Donohue
2023-04-03 16:31:36 -05:00
committed by GitHub
8 changed files with 45 additions and 15 deletions

View File

@@ -16,7 +16,7 @@
<ng-container #componentViewContainer></ng-container>
</div>
<small *ngIf="hasHint && ((!model.repeatable && (isRelationship === false || value?.value === null)) || (model.repeatable === true && context?.index === context?.context?.groups?.length - 1)) && (!showErrorMessages || errorMessages.length === 0)"
<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)"
class="text-muted ds-hint" [innerHTML]="model.hint | translate" [ngClass]="getClass('element', 'hint')"></small>
<!-- In case of repeatable fields show empty space for all elements except the first -->
<div *ngIf="context?.index !== null

View File

@@ -147,12 +147,14 @@ describe('DsDynamicFormControlContainerComponent test suite', () => {
new DynamicListCheckboxGroupModel({
id: 'checkboxList',
vocabularyOptions: vocabularyOptions,
repeatable: true
repeatable: true,
required: false,
}),
new DynamicListRadioGroupModel({
id: 'radioList',
vocabularyOptions: vocabularyOptions,
repeatable: false
repeatable: false,
required: false,
}),
new DynamicRelationGroupModel({
submissionId: '1234',

View File

@@ -259,7 +259,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo
private submissionObjectService: SubmissionObjectDataService,
private ref: ChangeDetectorRef,
private formService: FormService,
private formBuilderService: FormBuilderService,
public formBuilderService: FormBuilderService,
private submissionService: SubmissionService,
@Inject(APP_CONFIG) protected appConfig: AppConfig,
) {

View File

@@ -15,8 +15,10 @@ export interface DynamicListCheckboxGroupModelConfig extends DynamicFormGroupMod
vocabularyOptions: VocabularyOptions;
groupLength?: number;
repeatable: boolean;
value?: any;
value?: VocabularyEntry[];
typeBindRelations?: DynamicFormControlRelation[];
required: boolean;
hint?: string;
}
export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel {
@@ -26,6 +28,8 @@ export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel {
@serializable() groupLength: number;
@serializable() _value: VocabularyEntry[];
@serializable() typeBindRelations: DynamicFormControlRelation[];
@serializable() required: boolean;
@serializable() hint: string;
isListGroup = true;
valueUpdates: Subject<any>;
@@ -36,6 +40,8 @@ export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel {
this.groupLength = config.groupLength || 5;
this._value = [];
this.repeatable = config.repeatable;
this.required = config.required;
this.hint = config.hint;
this.valueUpdates = new Subject<any>();
this.valueUpdates.subscribe((value: VocabularyEntry | VocabularyEntry[]) => this.value = value);
@@ -56,9 +62,8 @@ export class DynamicListCheckboxGroupModel extends DynamicCheckboxGroupModel {
if (Array.isArray(value)) {
this._value = value;
} else {
// _value is non extendible so assign it a new array
const newValue = (this.value as VocabularyEntry[]).concat([value]);
this._value = newValue;
// _value is non-extendable so assign it a new array
this._value = (this.value as VocabularyEntry[]).concat([value]);
}
}
}

View File

@@ -6,12 +6,15 @@ import {
} from '@ng-dynamic-forms/core';
import { VocabularyOptions } from '../../../../../../core/submission/vocabularies/models/vocabulary-options.model';
import { hasValue } from '../../../../../empty.util';
import { VocabularyEntry } from '../../../../../../core/submission/vocabularies/models/vocabulary-entry.model';
export interface DynamicListModelConfig extends DynamicRadioGroupModelConfig<any> {
vocabularyOptions: VocabularyOptions;
groupLength?: number;
repeatable: boolean;
value?: any;
value?: VocabularyEntry[];
required: boolean;
hint?: string;
}
export class DynamicListRadioGroupModel extends DynamicRadioGroupModel<any> {
@@ -19,6 +22,8 @@ export class DynamicListRadioGroupModel extends DynamicRadioGroupModel<any> {
@serializable() vocabularyOptions: VocabularyOptions;
@serializable() repeatable: boolean;
@serializable() groupLength: number;
@serializable() required: boolean;
@serializable() hint: string;
isListGroup = true;
constructor(config: DynamicListModelConfig, layout?: DynamicFormControlLayout) {
@@ -27,6 +32,8 @@ export class DynamicListRadioGroupModel extends DynamicRadioGroupModel<any> {
this.vocabularyOptions = config.vocabularyOptions;
this.groupLength = config.groupLength || 5;
this.repeatable = config.repeatable;
this.required = config.required;
this.hint = config.hint;
this.value = config.value;
}

View File

@@ -17,7 +17,6 @@
[id]="item.id"
[formControlName]="item.id"
[name]="model.name"
[required]="model.required"
[value]="item.value"
(blur)="onBlur($event)"
(change)="onChange($event)"

View File

@@ -1,6 +1,5 @@
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { FormGroup, ValidatorFn, ValidationErrors, AbstractControl } from '@angular/forms';
import {
DynamicCheckboxModel,
DynamicFormControlComponent,
@@ -110,6 +109,9 @@ export class DsDynamicListComponent extends DynamicFormControlComponent implemen
protected setOptionsFromVocabulary() {
if (this.model.vocabularyOptions.name && this.model.vocabularyOptions.name.length > 0) {
const listGroup = this.group.controls[this.model.id] as FormGroup;
if (this.model.repeatable && this.model.required) {
listGroup.addValidators(this.hasAtLeastOneVocabularyEntry());
}
const pageInfo: PageInfo = new PageInfo({
elementsPerPage: 9999, currentPage: 1
} as PageInfo);
@@ -121,7 +123,7 @@ export class DsDynamicListComponent extends DynamicFormControlComponent implemen
let tempList: ListItem[] = [];
this.optionsList = entries.page;
// Make a list of available options (checkbox/radio) and split in groups of 'model.groupLength'
entries.page.forEach((option, key) => {
entries.page.forEach((option: VocabularyEntry, key: number) => {
const value = option.authority || option.value;
const checked: boolean = isNotEmpty(findKey(
this.model.value,
@@ -156,4 +158,13 @@ export class DsDynamicListComponent extends DynamicFormControlComponent implemen
}
}
/**
* Checks if at least one {@link VocabularyEntry} has been selected.
*/
hasAtLeastOneVocabularyEntry(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
return control && control.value && Object.values(control.value).find((checked: boolean) => checked === true) ? null : this.model.errorMessages;
};
}
}

View File

@@ -235,10 +235,16 @@ describe('FormBuilderService test suite', () => {
new DynamicListCheckboxGroupModel({
id: 'testCheckboxList',
vocabularyOptions: vocabularyOptions,
repeatable: true
repeatable: true,
required: false,
}),
new DynamicListRadioGroupModel({ id: 'testRadioList', vocabularyOptions: vocabularyOptions, repeatable: false }),
new DynamicListRadioGroupModel({
id: 'testRadioList',
vocabularyOptions: vocabularyOptions,
repeatable: false,
required: false,
}),
new DynamicRelationGroupModel({
submissionId,