Skip to content

Commit bfb370f

Browse files
committed
Feature - Add global error message support, add wildcard fallback message support, add custom error message function support
1 parent 4ea5ace commit bfb370f

File tree

3 files changed

+128
-14
lines changed

3 files changed

+128
-14
lines changed

projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validation.service.spec.ts

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { TestBed, inject } from "@angular/core/testing";
22
import { ReactiveFormsModule, FormControl, NG_VALIDATORS, NG_ASYNC_VALIDATORS, ValidationErrors, Validators } from "@angular/forms";
33
import { DynamicFormValidationService } from "./dynamic-form-validation.service";
4-
import { DYNAMIC_VALIDATORS, Validator, ValidatorFactory } from "./dynamic-form-validators";
4+
import {
5+
DYNAMIC_GLOBAL_ERROR_MESSAGES, DYNAMIC_VALIDATORS,
6+
Validator, ValidatorErrorMessageFn,
7+
ValidatorFactory,
8+
} from "./dynamic-form-validators";
59
import { DynamicFormControlModel } from "../model/dynamic-form-control.model";
610
import { DynamicInputModel } from "../model/input/dynamic-input.model";
711
import { isFunction } from "../utils/core.utils";
@@ -45,6 +49,14 @@ describe("DynamicFormValidationService test suite", () => {
4549
useValue: new Map<string, Validator | ValidatorFactory>([
4650
["testValidatorFactory", testValidatorFactory]
4751
])
52+
},
53+
{
54+
provide: DYNAMIC_GLOBAL_ERROR_MESSAGES,
55+
useValue: new Map<string, string | ValidatorErrorMessageFn>([
56+
['testDynamicError', 'this is a test'],
57+
['testFunc', (model: DynamicFormControlModel, error: string) => error],
58+
['*', 'this is a catch-all'],
59+
]),
4860
}
4961
]
5062
});
@@ -163,7 +175,9 @@ describe("DynamicFormValidationService test suite", () => {
163175
required: "Field is required",
164176
minLength: "Field must contain at least {{ minLength }} characters",
165177
custom1: "Field {{ id }} has a custom error",
166-
custom2: "Field has a custom error: {{ validator.param }}"
178+
custom2: "Field has a custom error: {{ validator.param }}",
179+
customFunc: (model: DynamicFormControlModel, error: string) => error,
180+
'*': 'catch-all',
167181
}
168182
});
169183

@@ -190,6 +204,18 @@ describe("DynamicFormValidationService test suite", () => {
190204
errorMessages = service.createErrorMessages(testControl, testModel);
191205
expect(errorMessages.length).toBe(1);
192206
expect(errorMessages[0]).toEqual("Field has a custom error: 42");
207+
208+
testControl.setErrors({customFunc: 'error message'});
209+
210+
errorMessages = service.createErrorMessages(testControl, testModel);
211+
expect(errorMessages.length).toBe(1);
212+
expect(errorMessages[0]).toEqual("error message");
213+
214+
testControl.setErrors({unknownToken: true});
215+
216+
errorMessages = service.createErrorMessages(testControl, testModel);
217+
expect(errorMessages.length).toBe(1);
218+
expect(errorMessages[0]).toEqual("catch-all");
193219
});
194220

195221

@@ -223,4 +249,70 @@ describe("DynamicFormValidationService test suite", () => {
223249
expect(service.isFormHook("change")).toBe(true);
224250
expect(service.isFormHook("submit")).toBe(true);
225251
});
252+
253+
it("can create global error messages", () => {
254+
inject([DynamicFormValidationService],
255+
(validationService: DynamicFormValidationService) => {
256+
const testControl: FormControl = new FormControl();
257+
const testModel: DynamicFormControlModel = new DynamicInputModel({
258+
id: "testModel",
259+
minLength: 5,
260+
});
261+
262+
let errorMessages;
263+
264+
errorMessages = validationService.createErrorMessages(testControl, testModel);
265+
expect(errorMessages.length).toBe(0);
266+
267+
testControl.setErrors({testDynamicError: true});
268+
269+
errorMessages = validationService.createErrorMessages(testControl, testModel);
270+
expect(errorMessages.length).toBe(1);
271+
expect(errorMessages[0]).toEqual("this is a test");
272+
});
273+
});
274+
275+
it("error messages can be functions", () => {
276+
inject([DynamicFormValidationService],
277+
(validationService: DynamicFormValidationService) => {
278+
const testControl: FormControl = new FormControl();
279+
const testModel: DynamicFormControlModel = new DynamicInputModel({
280+
id: "testModel",
281+
minLength: 5,
282+
});
283+
284+
let errorMessages;
285+
286+
errorMessages = validationService.createErrorMessages(testControl, testModel);
287+
expect(errorMessages.length).toBe(0);
288+
289+
testControl.setErrors({testFunc: 'this should echo'});
290+
291+
errorMessages = validationService.createErrorMessages(testControl, testModel);
292+
expect(errorMessages.length).toBe(1);
293+
expect(errorMessages[0]).toEqual("this should echo");
294+
});
295+
});
296+
297+
it("error messages can be catch-alls", () => {
298+
inject([DynamicFormValidationService],
299+
(validationService: DynamicFormValidationService) => {
300+
const testControl: FormControl = new FormControl();
301+
const testModel: DynamicFormControlModel = new DynamicInputModel({
302+
id: "testModel",
303+
minLength: 5,
304+
});
305+
306+
let errorMessages;
307+
308+
errorMessages = service.createErrorMessages(testControl, testModel);
309+
expect(errorMessages.length).toBe(0);
310+
311+
testControl.setErrors({unknown: 'this should not echo'});
312+
313+
errorMessages = service.createErrorMessages(testControl, testModel);
314+
expect(errorMessages.length).toBe(1);
315+
expect(errorMessages[0]).toEqual("this is a catch-all");
316+
});
317+
});
226318
});

projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validation.service.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,27 @@ import {
55
ValidatorFn,
66
Validators,
77
NG_VALIDATORS,
8-
NG_ASYNC_VALIDATORS
8+
NG_ASYNC_VALIDATORS,
99
} from "@angular/forms";
1010
import { DynamicFormControlModel } from "../model/dynamic-form-control.model";
1111
import {
1212
DynamicFormHook,
1313
DynamicValidatorDescriptor,
14-
DynamicValidatorsConfig
14+
DynamicValidatorsConfig,
1515
} from "../model/misc/dynamic-form-control-validation.model";
1616
import { isObject, isString } from "../utils/core.utils";
17-
import { DYNAMIC_VALIDATORS, Validator, ValidatorFactory, ValidatorsToken } from "./dynamic-form-validators";
17+
import {
18+
DYNAMIC_VALIDATORS,
19+
DYNAMIC_GLOBAL_ERROR_MESSAGES,
20+
Validator,
21+
ValidatorFactory,
22+
ValidatorsToken,
23+
ValidatorErrorMessagesMap,
24+
} from "./dynamic-form-validators";
1825
import {
1926
DEFAULT_ERROR_STATE_MATCHER,
2027
DYNAMIC_ERROR_MESSAGES_MATCHER,
21-
DynamicErrorMessagesMatcher
28+
DynamicErrorMessagesMatcher,
2229
} from "./dynamic-form-validation-matchers";
2330

2431
@Injectable({
@@ -29,7 +36,8 @@ export class DynamicFormValidationService {
2936
constructor(@Optional() @Inject(NG_VALIDATORS) private _NG_VALIDATORS: ValidatorFn[],
3037
@Optional() @Inject(NG_ASYNC_VALIDATORS) private _NG_ASYNC_VALIDATORS: AsyncValidatorFn[],
3138
@Optional() @Inject(DYNAMIC_VALIDATORS) private _DYNAMIC_VALIDATORS: Map<string, Validator | ValidatorFactory>,
32-
@Optional() @Inject(DYNAMIC_ERROR_MESSAGES_MATCHER) private _DYNAMIC_ERROR_MESSAGES_MATCHER: DynamicErrorMessagesMatcher) {
39+
@Optional() @Inject(DYNAMIC_ERROR_MESSAGES_MATCHER) private _DYNAMIC_ERROR_MESSAGES_MATCHER: DynamicErrorMessagesMatcher,
40+
@Optional() @Inject(DYNAMIC_GLOBAL_ERROR_MESSAGES) private _DYNAMIC_GLOBAL_ERROR_MESSAGES: ValidatorErrorMessagesMap) {
3341
}
3442

3543
private getValidatorFn(validatorName: string, validatorArgs: any = null,
@@ -169,7 +177,11 @@ export class DynamicFormValidationService {
169177

170178
if (model.hasErrorMessages) {
171179

172-
const messagesConfig = model.errorMessages as DynamicValidatorsConfig;
180+
const messagesConfig = <DynamicValidatorsConfig> Object.assign(
181+
{},
182+
this._DYNAMIC_GLOBAL_ERROR_MESSAGES || {},
183+
model.errorMessages
184+
);
173185

174186
Object.keys(control.errors || {}).forEach(validationErrorKey => {
175187

@@ -179,12 +191,15 @@ export class DynamicFormValidationService {
179191
messageKey = messageKey.replace("length", "Length");
180192
}
181193

182-
if (messagesConfig.hasOwnProperty(messageKey)) {
183-
184-
const validationError = control.getError(validationErrorKey);
185-
const messageTemplate = messagesConfig[messageKey] as string;
194+
if (messagesConfig.hasOwnProperty(messageKey) || messagesConfig.hasOwnProperty('*')) {
195+
const messageTemplate = messagesConfig[messageKey] || messagesConfig['*'];
196+
const validationError = control.getError(validationErrorKey);
186197

198+
if (typeof (messageTemplate) === 'function') {
199+
messages.push(messageTemplate(model, validationError));
200+
} else {
187201
messages.push(this.parseErrorMessageConfig(messageTemplate, model, validationError));
202+
}
188203
}
189204
});
190205
}
Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { InjectionToken } from "@angular/core";
2-
import { AsyncValidatorFn, ValidatorFn } from "@angular/forms";
1+
import {InjectionToken, ValueProvider} from "@angular/core";
2+
import {AbstractControl, AsyncValidatorFn, ValidatorFn} from "@angular/forms";
3+
import {DynamicFormControlModel} from '../model/dynamic-form-control.model';
34

45
export type Validator = ValidatorFn | AsyncValidatorFn;
56

@@ -9,4 +10,10 @@ export type ValidatorsToken = Validator[];
910

1011
export type ValidatorsMap = Map<string, Validator | ValidatorFactory>;
1112

13+
export type ValidatorErrorMessageFn = (model: DynamicFormControlModel, error: any) => string;
14+
15+
export type ValidatorErrorMessagesMap = Map<string, ValidatorErrorMessageFn | string>;
16+
1217
export const DYNAMIC_VALIDATORS = new InjectionToken<ValidatorsMap>("DYNAMIC_VALIDATORS");
18+
19+
export const DYNAMIC_GLOBAL_ERROR_MESSAGES = new InjectionToken<ValidatorErrorMessagesMap>("DYNAMIC_ERROR_MESSAGES");

0 commit comments

Comments
 (0)