Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature - Add global error message support #1054

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { TestBed, inject } from "@angular/core/testing";
import { ReactiveFormsModule, FormControl, NG_VALIDATORS, NG_ASYNC_VALIDATORS, ValidationErrors, Validators } from "@angular/forms";
import { DynamicFormValidationService } from "./dynamic-form-validation.service";
import { DYNAMIC_VALIDATORS, Validator, ValidatorFactory } from "./dynamic-form-validators";
import {
DYNAMIC_GLOBAL_ERROR_MESSAGES, DYNAMIC_VALIDATORS,
Validator, ValidatorErrorMessageFn,
ValidatorFactory,
} from "./dynamic-form-validators";
import { DynamicFormControlModel } from "../model/dynamic-form-control.model";
import { DynamicInputModel } from "../model/input/dynamic-input.model";
import { isFunction } from "../utils/core.utils";
Expand Down Expand Up @@ -45,6 +49,14 @@ describe("DynamicFormValidationService test suite", () => {
useValue: new Map<string, Validator | ValidatorFactory>([
["testValidatorFactory", testValidatorFactory]
])
},
{
provide: DYNAMIC_GLOBAL_ERROR_MESSAGES,
useValue: new Map<string, string | ValidatorErrorMessageFn>([
['testDynamicError', 'this is a test'],
['testFunc', (model: DynamicFormControlModel, error: string) => error],
['*', 'this is a catch-all'],
]),
}
]
});
Expand Down Expand Up @@ -163,7 +175,9 @@ describe("DynamicFormValidationService test suite", () => {
required: "Field is required",
minLength: "Field must contain at least {{ minLength }} characters",
custom1: "Field {{ id }} has a custom error",
custom2: "Field has a custom error: {{ validator.param }}"
custom2: "Field has a custom error: {{ validator.param }}",
customFunc: (model: DynamicFormControlModel, error: string) => error,
'*': 'catch-all',
}
});

Expand All @@ -190,6 +204,18 @@ describe("DynamicFormValidationService test suite", () => {
errorMessages = service.createErrorMessages(testControl, testModel);
expect(errorMessages.length).toBe(1);
expect(errorMessages[0]).toEqual("Field has a custom error: 42");

testControl.setErrors({customFunc: 'error message'});

errorMessages = service.createErrorMessages(testControl, testModel);
expect(errorMessages.length).toBe(1);
expect(errorMessages[0]).toEqual("error message");

testControl.setErrors({unknownToken: true});

errorMessages = service.createErrorMessages(testControl, testModel);
expect(errorMessages.length).toBe(1);
expect(errorMessages[0]).toEqual("catch-all");
});


Expand Down Expand Up @@ -223,4 +249,70 @@ describe("DynamicFormValidationService test suite", () => {
expect(service.isFormHook("change")).toBe(true);
expect(service.isFormHook("submit")).toBe(true);
});

it("can create global error messages", () => {
inject([DynamicFormValidationService],
(validationService: DynamicFormValidationService) => {
const testControl: FormControl = new FormControl();
const testModel: DynamicFormControlModel = new DynamicInputModel({
id: "testModel",
minLength: 5,
});

let errorMessages;

errorMessages = validationService.createErrorMessages(testControl, testModel);
expect(errorMessages.length).toBe(0);

testControl.setErrors({testDynamicError: true});

errorMessages = validationService.createErrorMessages(testControl, testModel);
expect(errorMessages.length).toBe(1);
expect(errorMessages[0]).toEqual("this is a test");
});
});

it("error messages can be functions", () => {
inject([DynamicFormValidationService],
(validationService: DynamicFormValidationService) => {
const testControl: FormControl = new FormControl();
const testModel: DynamicFormControlModel = new DynamicInputModel({
id: "testModel",
minLength: 5,
});

let errorMessages;

errorMessages = validationService.createErrorMessages(testControl, testModel);
expect(errorMessages.length).toBe(0);

testControl.setErrors({testFunc: 'this should echo'});

errorMessages = validationService.createErrorMessages(testControl, testModel);
expect(errorMessages.length).toBe(1);
expect(errorMessages[0]).toEqual("this should echo");
});
});

it("error messages can be catch-alls", () => {
inject([DynamicFormValidationService],
(validationService: DynamicFormValidationService) => {
const testControl: FormControl = new FormControl();
const testModel: DynamicFormControlModel = new DynamicInputModel({
id: "testModel",
minLength: 5,
});

let errorMessages;

errorMessages = service.createErrorMessages(testControl, testModel);
expect(errorMessages.length).toBe(0);

testControl.setErrors({unknown: 'this should not echo'});

errorMessages = service.createErrorMessages(testControl, testModel);
expect(errorMessages.length).toBe(1);
expect(errorMessages[0]).toEqual("this is a catch-all");
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,27 @@ import {
ValidatorFn,
Validators,
NG_VALIDATORS,
NG_ASYNC_VALIDATORS
NG_ASYNC_VALIDATORS,
} from "@angular/forms";
import { DynamicFormControlModel } from "../model/dynamic-form-control.model";
import {
DynamicFormHook,
DynamicValidatorDescriptor,
DynamicValidatorsConfig
DynamicValidatorsConfig,
} from "../model/misc/dynamic-form-control-validation.model";
import { isObject, isString } from "../utils/core.utils";
import { DYNAMIC_VALIDATORS, Validator, ValidatorFactory, ValidatorsToken } from "./dynamic-form-validators";
import {
DYNAMIC_VALIDATORS,
DYNAMIC_GLOBAL_ERROR_MESSAGES,
Validator,
ValidatorFactory,
ValidatorsToken,
ValidatorErrorMessagesMap,
} from "./dynamic-form-validators";
import {
DEFAULT_ERROR_STATE_MATCHER,
DYNAMIC_ERROR_MESSAGES_MATCHER,
DynamicErrorMessagesMatcher
DynamicErrorMessagesMatcher,
} from "./dynamic-form-validation-matchers";

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

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

if (model.hasErrorMessages) {

const messagesConfig = model.errorMessages as DynamicValidatorsConfig;
const messagesConfig = <DynamicValidatorsConfig> Object.assign(
{},
this._DYNAMIC_GLOBAL_ERROR_MESSAGES || {},
model.errorMessages
);

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

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

if (messagesConfig.hasOwnProperty(messageKey)) {

const validationError = control.getError(validationErrorKey);
const messageTemplate = messagesConfig[messageKey] as string;
if (messagesConfig.hasOwnProperty(messageKey) || messagesConfig.hasOwnProperty('*')) {
const messageTemplate = messagesConfig[messageKey] || messagesConfig['*'];
const validationError = control.getError(validationErrorKey);

if (typeof (messageTemplate) === 'function') {
messages.push(messageTemplate(model, validationError));
} else {
messages.push(this.parseErrorMessageConfig(messageTemplate, model, validationError));
}
}
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { InjectionToken } from "@angular/core";
import { AsyncValidatorFn, ValidatorFn } from "@angular/forms";
import {InjectionToken} from "@angular/core";
import {AsyncValidatorFn, ValidatorFn} from "@angular/forms";
import {DynamicFormControlModel} from '../model/dynamic-form-control.model';

export type Validator = ValidatorFn | AsyncValidatorFn;

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

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

export type ValidatorErrorMessageFn = (model: DynamicFormControlModel, error: any) => string;

export type ValidatorErrorMessagesMap = Map<string, ValidatorErrorMessageFn | string>;

export const DYNAMIC_VALIDATORS = new InjectionToken<ValidatorsMap>("DYNAMIC_VALIDATORS");

export const DYNAMIC_GLOBAL_ERROR_MESSAGES = new InjectionToken<ValidatorErrorMessagesMap>("DYNAMIC_ERROR_MESSAGES");