Skip to content

Commit 458df45

Browse files
authored
Merge pull request #1812 from atmire/w2p-94301_Empty-required-name-input-not-displaying-error-message-after-saving
Fix error validation for required `name` fields in submission
2 parents 445255e + a370f42 commit 458df45

8 files changed

Lines changed: 105 additions & 16 deletions

File tree

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
@@ -22,7 +22,7 @@
2222
<div *ngIf="context?.index !== null
2323
&& (!showErrorMessages || errorMessages.length === 0)" class="clearfix w-100 mb-2"></div>
2424

25-
<div *ngIf="showErrorMessages" [id]="id + '_errors'"
25+
<div *ngIf="!model.hideErrorMessages && showErrorMessages" [id]="id + '_errors'"
2626
[ngClass]="[getClass('element', 'errors'), getClass('grid', 'errors')]">
2727
<small *ngFor="let message of errorMessages" class="invalid-feedback d-block">{{ message | translate: model.validators }}</small>
2828
</div>

src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export interface DsDynamicInputModelConfig extends DynamicInputModelConfig {
2727
hasSelectableMetadata: boolean;
2828
metadataValue?: FormFieldMetadataValueObject;
2929
isModelOfInnerForm?: boolean;
30-
30+
hideErrorMessages?: boolean;
3131
}
3232

3333
export class DsDynamicInputModel extends DynamicInputModel {
@@ -46,6 +46,7 @@ export class DsDynamicInputModel extends DynamicInputModel {
4646
@serializable() hasSelectableMetadata: boolean;
4747
@serializable() metadataValue: FormFieldMetadataValueObject;
4848
@serializable() isModelOfInnerForm: boolean;
49+
@serializable() hideErrorMessages?: boolean;
4950

5051

5152
constructor(config: DsDynamicInputModelConfig, layout?: DynamicFormControlLayout) {
@@ -61,6 +62,7 @@ export class DsDynamicInputModel extends DynamicInputModel {
6162
this.metadataValue = config.metadataValue;
6263
this.place = config.place;
6364
this.isModelOfInnerForm = (hasValue(config.isModelOfInnerForm) ? config.isModelOfInnerForm : false);
65+
this.hideErrorMessages = config.hideErrorMessages;
6466

6567
this.language = config.language;
6668
if (!this.language) {

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,14 @@ describe('FormBuilderService test suite', () => {
325325
typeBindRelations: [{ match: 'VISIBLE', operator: 'OR', when: [{id: 'dc.type', value: 'Book' }]}]
326326
},
327327
),
328+
329+
new DynamicConcatModel({
330+
id: 'testConcatGroup_CONCAT_GROUP',
331+
group: [
332+
new DynamicInputModel({ id: 'testConcatGroup_CONCAT_FIRST_INPUT' }),
333+
new DynamicInputModel({ id: 'testConcatGroup_CONCAT_SECOND_INPUT' }),
334+
]
335+
} as any)
328336
];
329337

330338
testFormConfiguration = {
@@ -463,6 +471,7 @@ describe('FormBuilderService test suite', () => {
463471
expect(service.findById('testTimePicker', testModel) instanceof DynamicTimePickerModel).toBe(true);
464472
expect(service.findById('testRating', testModel) instanceof DynamicRatingModel).toBe(true);
465473
expect(service.findById('testColorPicker', testModel) instanceof DynamicColorPickerModel).toBe(true);
474+
expect(service.findById('testConcatGroup', testModel) instanceof DynamicConcatModel).toBe(true);
466475
});
467476

468477
it('should find a nested dynamic form control model by id', () => {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ export class FormBuilderService extends DynamicFormService {
116116
}
117117

118118
if (this.isConcatGroup(controlModel)) {
119-
if (controlModel.id.match(new RegExp(findId + CONCAT_GROUP_SUFFIX + `_\\d+$`))) {
120-
result = (controlModel as DynamicConcatModel).group[0];
119+
if (controlModel.id.match(new RegExp(findId + CONCAT_GROUP_SUFFIX))) {
120+
result = (controlModel as DynamicConcatModel);
121121
break;
122122
}
123123
}

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import {Inject} from '@angular/core';
22
import { FormFieldModel } from '../models/form-field.model';
33
import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model';
4-
import {
5-
DynamicFormControlLayout,
6-
DynamicInputModel,
7-
DynamicInputModelConfig
8-
} from '@ng-dynamic-forms/core';
4+
import { DynamicFormControlLayout, } from '@ng-dynamic-forms/core';
95
import {
106
CONCAT_FIRST_INPUT_SUFFIX,
117
CONCAT_GROUP_SUFFIX,
@@ -22,6 +18,7 @@ import {
2218
PARSER_OPTIONS,
2319
SUBMISSION_ID
2420
} from './field-parser';
21+
import { DsDynamicInputModel, DsDynamicInputModelConfig } from '../ds-dynamic-form-ui/models/ds-dynamic-input.model';
2522

2623
export class ConcatFieldParser extends FieldParser {
2724

@@ -58,21 +55,24 @@ export class ConcatFieldParser extends FieldParser {
5855
concatGroup.group = [];
5956
concatGroup.separator = this.separator;
6057

61-
const input1ModelConfig: DynamicInputModelConfig = this.initModel(
58+
const input1ModelConfig: DsDynamicInputModelConfig = this.initModel(
6259
id + CONCAT_FIRST_INPUT_SUFFIX,
6360
false,
6461
true,
6562
true,
6663
false
6764
);
68-
const input2ModelConfig: DynamicInputModelConfig = this.initModel(
65+
const input2ModelConfig: DsDynamicInputModelConfig = this.initModel(
6966
id + CONCAT_SECOND_INPUT_SUFFIX,
7067
false,
7168
true,
7269
true,
7370
false
7471
);
7572

73+
input1ModelConfig.hideErrorMessages = true;
74+
input2ModelConfig.hideErrorMessages = true;
75+
7676
if (hasNoValue(concatGroup.hint) && hasValue(input1ModelConfig.hint) && hasNoValue(input2ModelConfig.hint)) {
7777
concatGroup.hint = input1ModelConfig.hint;
7878
input1ModelConfig.hint = undefined;
@@ -98,8 +98,8 @@ export class ConcatFieldParser extends FieldParser {
9898
input2ModelConfig.placeholder = placeholder[1];
9999
}
100100

101-
const model1 = new DynamicInputModel(input1ModelConfig, clsInput);
102-
const model2 = new DynamicInputModel(input2ModelConfig, clsInput);
101+
const model1 = new DsDynamicInputModel(input1ModelConfig, clsInput);
102+
const model2 = new DsDynamicInputModel(input2ModelConfig, clsInput);
103103
concatGroup.group.push(model1);
104104
concatGroup.group.push(model2);
105105

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

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,15 @@ describe('FormService test suite', () => {
108108
const title: AbstractControl = new FormControl(undefined, Validators.required);
109109
const date: AbstractControl = new FormControl(undefined);
110110
const description: AbstractControl = new FormControl(undefined);
111-
formGroup = new FormGroup({ author, title, date, description });
112-
controls = { author, title, date, description };
111+
112+
const addressLocation: FormGroup = new FormGroup({
113+
zipCode: new FormControl(undefined),
114+
state: new FormControl(undefined),
115+
city: new FormControl(undefined),
116+
});
117+
118+
formGroup = new FormGroup({ author, title, date, description, addressLocation });
119+
controls = { author, title, date, description , addressLocation };
113120
service = new FormService(builderService, store);
114121
})
115122
)
@@ -179,6 +186,32 @@ describe('FormService test suite', () => {
179186
expect(formGroup.controls.description.touched).toBe(true);
180187
});
181188

189+
it('should add errors to fields of concat group', () => {
190+
(builderService as any).isConcatGroup.and.returnValue(true);
191+
192+
let control = controls.addressLocation;
193+
let model = formModel.find((mdl: DynamicFormControlModel) => mdl.id === 'addressLocation');
194+
let errorKeys: string[];
195+
196+
service.addErrorToField(control, model, 'Test error message');
197+
198+
// the group itself should get an error
199+
errorKeys = Object.keys(control.errors);
200+
expect(errorKeys.length).toBe(1);
201+
expect(control.hasError(errorKeys[0])).toBe(true);
202+
203+
expect(control.touched).toBe(true);
204+
205+
// the group's inputs should get an error
206+
Object.values(control.controls).forEach((subControl: AbstractControl) => {
207+
errorKeys = Object.keys(subControl.errors);
208+
expect(errorKeys.length).toBe(1);
209+
expect(subControl.hasError(errorKeys[0])).toBe(true);
210+
expect(subControl.touched).toBe(true);
211+
});
212+
213+
});
214+
182215
it('should remove error from field', () => {
183216
let control = controls.description;
184217
let model = formModel.find((mdl: DynamicFormControlModel) => mdl.id === 'description');
@@ -209,6 +242,32 @@ describe('FormService test suite', () => {
209242
expect(formGroup.controls.description.touched).toBe(false);
210243
});
211244

245+
it('should remove errors from fields of concat group', () => {
246+
(builderService as any).isConcatGroup.and.returnValue(true);
247+
248+
let control = controls.addressLocation;
249+
let model = formModel.find((mdl: DynamicFormControlModel) => mdl.id === 'addressLocation');
250+
let errorKeys: string[];
251+
252+
service.addErrorToField(control, model, 'Test error message');
253+
errorKeys = Object.keys(control.errors);
254+
255+
service.removeErrorFromField(control, model, errorKeys[0]);
256+
257+
// the group itself should no longer have an error
258+
expect(errorKeys.length).toBe(1);
259+
expect(control.hasError(errorKeys[0])).toBe(false);
260+
expect(control.touched).toBe(false);
261+
262+
// the group's inputs should no longer have an error
263+
Object.values(control.controls).forEach((subControl: AbstractControl) => {
264+
errorKeys = Object.keys(subControl.errors);
265+
expect(errorKeys.length).toBe(1);
266+
expect(subControl.hasError(errorKeys[0])).toBe(false);
267+
expect(subControl.touched).toBe(false);
268+
});
269+
});
270+
212271
it('should reset form group', () => {
213272
const control = controls.author;
214273

src/app/shared/form/form.service.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { select, Store } from '@ngrx/store';
77
import { AppState } from '../../app.reducer';
88
import { formObjectFromIdSelector } from './selectors';
99
import { FormBuilderService } from './builder/form-builder.service';
10-
import { DynamicFormControlEvent, DynamicFormControlModel } from '@ng-dynamic-forms/core';
10+
import { DynamicFormControlEvent, DynamicFormControlModel, DynamicFormGroupModel } from '@ng-dynamic-forms/core';
1111
import { isEmpty, isNotUndefined } from '../empty.util';
1212
import { uniqueId } from 'lodash';
1313
import {
@@ -161,6 +161,15 @@ export class FormService {
161161
field.setErrors(error);
162162
}
163163

164+
// if the field in question is a concat group, pass down the error to its fields
165+
if (field instanceof FormGroup && model instanceof DynamicFormGroupModel && this.formBuilderService.isConcatGroup(model)) {
166+
model.group.forEach((subModel) => {
167+
const subField = field.controls[subModel.id];
168+
169+
this.addErrorToField(subField, subModel, message);
170+
});
171+
}
172+
164173
field.markAsTouched();
165174
}
166175

@@ -173,6 +182,15 @@ export class FormService {
173182
field.setErrors(error);
174183
}
175184

185+
// if the field in question is a concat group, clear the error from its fields
186+
if (field instanceof FormGroup && model instanceof DynamicFormGroupModel && this.formBuilderService.isConcatGroup(model)) {
187+
model.group.forEach((subModel) => {
188+
const subField = field.controls[subModel.id];
189+
190+
this.removeErrorFromField(subField, subModel, messageKey);
191+
});
192+
}
193+
176194
field.markAsUntouched();
177195
}
178196

src/app/shared/mocks/form-builder-service.mock.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export function getMockFormBuilderService(): FormBuilderService {
1919
isQualdropGroup: false,
2020
isModelInCustomGroup: true,
2121
isRelationGroup: true,
22+
isConcatGroup: false,
2223
hasArrayGroupValue: true,
2324
getTypeBindModel: new DsDynamicInputModel({
2425
name: 'dc.type',

0 commit comments

Comments
 (0)