Skip to content

Commit 5f03a06

Browse files
FrancescoMolinaroAndrea Barbasso
authored andcommitted
Merged in task/dspace-cris-2023_02_x/DSC-2599 (pull request DSpace#3828)
Task/dspace cris 2023 02 x/DSC-2599 Approved-by: Andrea Barbasso
2 parents 2576cb2 + 3202d2c commit 5f03a06

3 files changed

Lines changed: 105 additions & 65 deletions

File tree

src/app/shared/form/form.component.ts

Lines changed: 65 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -194,55 +194,74 @@ export class FormComponent implements OnDestroy, OnInit {
194194
this.formService.getForm(this.formId).pipe(
195195
filter((formState: FormEntry) => !!formState && (isNotEmpty(formState.errors) || isNotEmpty(this.formErrors))),
196196
map((formState) => formState.errors),
197-
distinctUntilChanged())
198-
.subscribe((errors: FormError[]) => {
199-
const { formGroup, formModel } = this;
200-
errors
201-
.filter((error: FormError) => findIndex(this.formErrors, {
202-
fieldId: error.fieldId,
203-
fieldIndex: error.fieldIndex
204-
}) === -1)
205-
.forEach((error: FormError) => {
206-
const { fieldId } = error;
207-
const { fieldIndex } = error;
208-
let field: AbstractControl;
209-
if (!!this.parentFormModel) {
210-
field = this.formBuilderService.getFormControlById(fieldId, formGroup.parent as UntypedFormGroup, formModel, fieldIndex);
211-
} else {
212-
field = this.formBuilderService.getFormControlById(fieldId, formGroup, formModel, fieldIndex);
213-
}
197+
distinctUntilChanged(),
198+
).subscribe((errors: FormError[]) => {
199+
const { formGroup, formModel } = this;
200+
201+
const prevMap = new Map<string, FormError>(
202+
this.formErrors.map(e => [`${e.fieldId}:${e.fieldIndex}`, e]),
203+
);
204+
const nextMap = new Map<string, FormError>(
205+
errors.map(e => [`${e.fieldId}:${e.fieldIndex}`, e]),
206+
);
207+
208+
if (isEqual(prevMap, nextMap)) {
209+
return;
210+
}
214211

215-
if (field) {
216-
const modelArrayIndex = fieldIndex > 0 ? fieldIndex : null;
217-
const model: DynamicFormControlModel = this.formBuilderService.findById(fieldId, formModel, modelArrayIndex);
218-
this.formService.addErrorToField(field, model, error.message);
219-
this.changeDetectorRef.detectChanges();
220-
}
221-
});
222-
223-
this.formErrors
224-
.filter((error: FormError) => findIndex(errors, {
225-
fieldId: error.fieldId,
226-
fieldIndex: error.fieldIndex
227-
}) === -1)
228-
.forEach((error: FormError) => {
229-
const { fieldId } = error;
230-
const { fieldIndex } = error;
231-
let field: AbstractControl;
232-
if (!!this.parentFormModel) {
233-
field = this.formBuilderService.getFormControlById(fieldId, formGroup.parent as UntypedFormGroup, formModel, fieldIndex);
234-
} else {
235-
field = this.formBuilderService.getFormControlById(fieldId, formGroup, formModel, fieldIndex);
212+
const getControl = (err: FormError): AbstractControl | null => {
213+
return this.parentFormModel
214+
? this.formBuilderService.getFormControlById(err.fieldId, formGroup.parent as UntypedFormGroup, formModel, err.fieldIndex)
215+
: this.formBuilderService.getFormControlById(err.fieldId, formGroup, formModel, err.fieldIndex);
216+
};
217+
218+
const getModel = (err: FormError): DynamicFormControlModel => {
219+
const modelArrayIndex = err.fieldIndex > 0 ? err.fieldIndex : null;
220+
return this.formBuilderService.findById(err.fieldId, formModel, modelArrayIndex);
221+
};
222+
// Add or change (including revert) errors
223+
errors.forEach(next => {
224+
const key = `${next.fieldId}:${next.fieldIndex}`;
225+
const prev = prevMap.get(key);
226+
if (!prev || prev.message !== next.message) {
227+
// Remove old message if changed
228+
if (prev) {
229+
const prevControl = getControl(prev);
230+
if (prevControl) {
231+
const prevModel = getModel(prev);
232+
this.formService.removeErrorFromField(prevControl, prevModel, prev.message);
233+
this.formService.removeError(this.formId, prev.fieldId, prev.fieldIndex);
234+
this.formErrors.splice(findIndex(this.formErrors, prev), 1);
236235
}
236+
}
237+
// Add new message
238+
const control = getControl(next);
239+
if (control) {
240+
const model = getModel(next);
241+
this.formService.addErrorToField(control, model, next.message);
242+
this.formErrors.push(next);
243+
}
244+
}
245+
});
246+
247+
const removedErrors: FormError[] = [];
248+
// Remove errors for fields no longer present
249+
this.formErrors.forEach(prev => {
250+
const key = `${prev.fieldId}:${prev.fieldIndex}`;
251+
if (!nextMap.has(key) && prevMap.has(key)) {
252+
const control = getControl(prev);
253+
if (control) {
254+
const model = getModel(prev);
255+
this.formService.removeErrorFromField(control, model, prev.message);
256+
this.formService.removeError(this.formId, prev.fieldId, prev.fieldIndex);
257+
removedErrors.push(prev);
258+
}
259+
}
260+
});
237261

238-
if (field) {
239-
const model: DynamicFormControlModel = this.formBuilderService.findById(fieldId, formModel);
240-
this.formService.removeErrorFromField(field, model, error.message);
241-
}
242-
});
243-
this.formErrors = errors;
244-
this.changeDetectorRef.detectChanges();
245-
})
262+
this.formErrors = this.formErrors.filter(error => !removedErrors.includes(error));
263+
this.changeDetectorRef.detectChanges();
264+
}),
246265
);
247266
}
248267

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

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { AppState } from '../../app.reducer';
1010
import { formReducer } from './form.reducer';
1111
import { getMockFormBuilderService } from '../mocks/form-builder-service.mock';
1212
import { DynamicConcatModel } from './builder/ds-dynamic-form-ui/models/ds-dynamic-concat.model';
13+
import { getMockTranslateService } from '../mocks/translate.service.mock';
1314

1415
describe('FormService test suite', () => {
1516
const config = {
@@ -24,6 +25,7 @@ describe('FormService test suite', () => {
2425
let service: FormService;
2526
let builderService: FormBuilderService;
2627
let formGroup: UntypedFormGroup;
28+
const translateService = getMockTranslateService();
2729

2830
const formModel: DynamicFormControlModel[] = [
2931
new DynamicInputModel({ id: 'author', value: 'test' }),
@@ -129,10 +131,10 @@ describe('FormService test suite', () => {
129131
name_CONCAT_SECOND_INPUT: new FormControl(undefined)
130132
});
131133

132-
formGroup = new UntypedFormGroup({ author, title, date, description, addressLocation, name });
133-
controls = { author, title, date, description , addressLocation, name };
134-
service = new FormService(builderService, store);
135-
})
134+
formGroup = new UntypedFormGroup({ author, title, date, description, addressLocation, name });
135+
controls = { author, title, date, description , addressLocation, name };
136+
service = new FormService(builderService, store, translateService);
137+
}),
136138
)
137139
;
138140

@@ -226,6 +228,17 @@ describe('FormService test suite', () => {
226228

227229
});
228230

231+
it('should show correct error message if a different error is already present in the field and a new is added', () => {
232+
let control = controls.description;
233+
let model = formModel.find((mdl: DynamicFormControlModel) => mdl.id === 'description');
234+
235+
service.addErrorToField(control, model, 'error.test.message');
236+
service.addErrorToField(control, model, 'error.test.newMessage');
237+
service.removeErrorFromField(control, model, 'error.test.message');
238+
239+
expect(control.errors).toEqual({ newMessage: true });
240+
});
241+
229242
it('should remove error from field', () => {
230243
let control = controls.description;
231244
let model = formModel.find((mdl: DynamicFormControlModel) => mdl.id === 'description');
@@ -249,7 +262,7 @@ describe('FormService test suite', () => {
249262

250263
service.removeErrorFromField(control, model, 'error.required');
251264

252-
expect(control.errors).toBeNull();
265+
expect(control.errors).toEqual(null);
253266
});
254267

255268
it('should remove errors from fields of concat group', () => {
@@ -258,8 +271,8 @@ describe('FormService test suite', () => {
258271
let control = controls.name;
259272
let model = formModel.find((mdl: DynamicFormControlModel) => mdl.id === 'name_CONCAT_GROUP');
260273
let errorKeys: string[];
261-
262-
service.addErrorToField(control, model, 'Test error message');
274+
const messageKey = 'Test error message';
275+
service.addErrorToField(control, model, messageKey);
263276
errorKeys = Object.keys(control.errors);
264277

265278
service.removeErrorFromField(control, model, errorKeys[0]);
@@ -271,7 +284,7 @@ describe('FormService test suite', () => {
271284

272285
// the group's inputs should no longer have an error
273286
Object.values(control.controls).forEach((subControl: AbstractControl) => {
274-
expect(control.errors).toBeNull();
287+
expect(control.errors).toEqual(null);
275288
});
276289
});
277290

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

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { AppState } from '../../app.reducer';
88
import { formObjectFromIdSelector } from './selectors';
99
import { FormBuilderService } from './builder/form-builder.service';
1010
import { DynamicFormControlEvent, DynamicFormControlModel, DynamicFormGroupModel } from '@ng-dynamic-forms/core';
11-
import { isEmpty, isNotUndefined } from '../empty.util';
11+
import { hasValue, isEmpty, isNotUndefined } from '../empty.util';
1212
import uniqueId from 'lodash/uniqueId';
1313
import {
1414
FormAddError,
@@ -22,13 +22,15 @@ import {
2222
import { FormEntry, FormError, FormTouchedState } from './form.reducer';
2323
import { environment } from '../../../environments/environment';
2424
import { DynamicLinkModel } from './builder/ds-dynamic-form-ui/models/ds-dynamic-link.model';
25+
import { TranslateService } from '@ngx-translate/core';
2526

2627
@Injectable()
2728
export class FormService {
2829

2930
constructor(
3031
private formBuilderService: FormBuilderService,
31-
private store: Store<AppState>) {
32+
private store: Store<AppState>,
33+
private translateService: TranslateService) {
3234
}
3335

3436
/**
@@ -136,15 +138,19 @@ export class FormService {
136138
}
137139
const errors: string[] = Object.keys(field.errors)
138140
.filter((errorKey) => field.errors[errorKey] === true)
139-
.map((errorKey) => `error.validation.${errorKey}`);
141+
.map((errorKey) => {
142+
const defaultErrorKey = `error.validation.${errorKey}`;
143+
const customErrorKey = `error.validation.${formId}.${errorKey}`;
144+
const hasDefaultLabel = this.translateService.instant(defaultErrorKey) !== defaultErrorKey;
145+
return hasDefaultLabel ? defaultErrorKey : customErrorKey;
146+
});
140147
errors.forEach((error) => this.addError(formId, fieldId, fieldIndex, error));
141148
}
142149

143150
public addErrorToField(field: AbstractControl, model: DynamicFormControlModel, message: string) {
144151

145152
const error = {}; // create the error object
146153
const errorKey = this.getValidatorNameFromMap(message);
147-
let errorMsg = message;
148154

149155
// if form control model has no errorMessages object, create it
150156
if (!model.errorMessages) {
@@ -155,11 +161,7 @@ export class FormService {
155161
if (isEmpty(model.errorMessages[errorKey])) {
156162
// put the error message in the form control model
157163
model.errorMessages[errorKey] = message;
158-
} else {
159-
// Use correct error messages from the model
160-
errorMsg = model.errorMessages[errorKey];
161164
}
162-
163165
if (!field.hasError(errorKey)) {
164166
error[errorKey] = true;
165167
// add the error in the form control
@@ -182,10 +184,10 @@ export class FormService {
182184
public removeErrorFromField(field: AbstractControl, model: DynamicFormControlModel, messageKey: string) {
183185
const error = {};
184186
const errorKey = this.getValidatorNameFromMap(messageKey);
185-
186187
if (field.hasError(errorKey)) {
187188
error[errorKey] = null;
188-
field.setErrors(error);
189+
const updatedError = { ...field.errors, ...error };
190+
field.setErrors(updatedError);
189191
field.clearValidators();
190192
field.updateValueAndValidity();
191193
}
@@ -199,7 +201,13 @@ export class FormService {
199201
});
200202
}
201203

202-
field.markAsUntouched();
204+
const currentErrors = field.errors;
205+
const hasDifferentErrors = hasValue(currentErrors) && Object.keys(currentErrors).filter((key) => currentErrors[key]).length > 0;
206+
207+
if (!hasDifferentErrors) {
208+
field.markAsUntouched();
209+
}
210+
203211
}
204212

205213
public resetForm(formGroup: UntypedFormGroup, groupModel: DynamicFormControlModel[], formId: string) {

0 commit comments

Comments
 (0)