From bfb370fae1c306749077fbbd4f934d934b3d443a Mon Sep 17 00:00:00 2001 From: Steve Rhoades Date: Sat, 21 Dec 2019 21:42:01 -0800 Subject: [PATCH 1/2] Feature - Add global error message support, add wildcard fallback message support, add custom error message function support --- .../dynamic-form-validation.service.spec.ts | 96 ++++++++++++++++++- .../dynamic-form-validation.service.ts | 35 +++++-- .../lib/service/dynamic-form-validators.ts | 11 ++- 3 files changed, 128 insertions(+), 14 deletions(-) diff --git a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validation.service.spec.ts b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validation.service.spec.ts index 37d407aa4..79c8e564f 100644 --- a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validation.service.spec.ts +++ b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validation.service.spec.ts @@ -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"; @@ -45,6 +49,14 @@ describe("DynamicFormValidationService test suite", () => { useValue: new Map([ ["testValidatorFactory", testValidatorFactory] ]) + }, + { + provide: DYNAMIC_GLOBAL_ERROR_MESSAGES, + useValue: new Map([ + ['testDynamicError', 'this is a test'], + ['testFunc', (model: DynamicFormControlModel, error: string) => error], + ['*', 'this is a catch-all'], + ]), } ] }); @@ -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', } }); @@ -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"); }); @@ -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"); + }); + }); }); diff --git a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validation.service.ts b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validation.service.ts index 74cabbb30..a9a7c535a 100644 --- a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validation.service.ts +++ b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validation.service.ts @@ -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({ @@ -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, - @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, @@ -169,7 +177,11 @@ export class DynamicFormValidationService { if (model.hasErrorMessages) { - const messagesConfig = model.errorMessages as DynamicValidatorsConfig; + const messagesConfig = Object.assign( + {}, + this._DYNAMIC_GLOBAL_ERROR_MESSAGES || {}, + model.errorMessages + ); Object.keys(control.errors || {}).forEach(validationErrorKey => { @@ -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)); + } } }); } diff --git a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validators.ts b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validators.ts index d78533e1e..c65991d7c 100644 --- a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validators.ts +++ b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validators.ts @@ -1,5 +1,6 @@ -import { InjectionToken } from "@angular/core"; -import { AsyncValidatorFn, ValidatorFn } from "@angular/forms"; +import {InjectionToken, ValueProvider} from "@angular/core"; +import {AbstractControl, AsyncValidatorFn, ValidatorFn} from "@angular/forms"; +import {DynamicFormControlModel} from '../model/dynamic-form-control.model'; export type Validator = ValidatorFn | AsyncValidatorFn; @@ -9,4 +10,10 @@ export type ValidatorsToken = Validator[]; export type ValidatorsMap = Map; +export type ValidatorErrorMessageFn = (model: DynamicFormControlModel, error: any) => string; + +export type ValidatorErrorMessagesMap = Map; + export const DYNAMIC_VALIDATORS = new InjectionToken("DYNAMIC_VALIDATORS"); + +export const DYNAMIC_GLOBAL_ERROR_MESSAGES = new InjectionToken("DYNAMIC_ERROR_MESSAGES"); From 5941513b3c58c37584b58f82a8e7c77c6441c40a Mon Sep 17 00:00:00 2001 From: Steve Rhoades Date: Sat, 21 Dec 2019 22:22:52 -0800 Subject: [PATCH 2/2] small cleanup of un-used imports --- .../core/src/lib/service/dynamic-form-validators.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validators.ts b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validators.ts index c65991d7c..1f4c93391 100644 --- a/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validators.ts +++ b/projects/ng-dynamic-forms/core/src/lib/service/dynamic-form-validators.ts @@ -1,5 +1,5 @@ -import {InjectionToken, ValueProvider} from "@angular/core"; -import {AbstractControl, 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;