This is a directive that provides an uncomplicated way to display Angular ValidationErrors in Reactive Forms
See It working on this demo.
You can also try it in your browser here.
npm install --save ngx-formcontrol-errors-msgs
- Import
FormcontrolErrorsDirectivein the component (You must import ReactiveFormsModule too)
import{Component}from'@angular/core';import{FormBuilder,ReactiveFormsModule,Validators}from"@angular/forms";import{FormcontrolErrorsDirective}from"ngx-formcontrol-errors-msgs"; @Component({selector: 'app-form',standalone: true,imports: [FormcontrolErrorsDirective,ReactiveFormsModule],templateUrl: './app-form.component.html',styleUrl: './app-form.component.scss',})exportclassAppForm{constructor(privatereadonlyformBuilder: FormBuilder){} ... }- Create a form as usual for Reactive Forms
form=this.formBuilder.group({name: ["",[Validators.required,Validators.maxLength(10)]],email: ["",[Validators.required,Validators.email]],});- Place the directive in the template along with FormControlName or FormControl
<form[formGroup]="form"><divclass="form-row"><labelfor="name">Name</label><inputid="name" type="text" formControlName="name" ngxFormcontrolErrors/></div><divclass="form-row"><labelfor="email">Email</label><inputid="email" type="email" formControlName="email" ngxFormcontrolErrors/></div></form>Note:
By default this module provides the following messages for Angular built-in Validators:
exportconstMessages: KeyValueObject={required: "This field is required",min: "The minimun allowed values is{{min}}",max: "The max allowed value is{{max}}",minlength: "The minimun allowed length is{{requiredLength}}",maxlength: "The max allowed length is{{requiredLength}}",email: "Invalid email",pattern: "Invalid pattern",};Where KeyValueObject is custom type defined by:
exporttypeKeyValueObject={[key: string]: string};Strings enclosed in double brackets, like {{min}}, {{max}}, {{requiredLength}}, are replaced at runtime by the actual validation reference value. Those messages can be overrided or extended by injecting new ones using FORM_ERROR_MESSAGES_PROVIDER in the ApplicationConfig object.
exportconstappConfig: ApplicationConfig={providers: [ ... {provide: FORM_ERROR_MESSAGES_PROVIDER,useValue: {// This message will override the default messagerequired: "This is a <b>required</b> field",// This is a message for a custom validator and will extend the default// messagesmyCustomValidation: "There is an error",},}, ... ],};HTML tags are allowed.
If the application uses Angular Internationalization, FORM_ERROR_MESSAGES_PROVIDER could be provided using $localize after adding all necessary settings for Angular I18N
exportconstappConfig: ApplicationConfig={providers: [ ... {provide: FORM_ERROR_MESSAGES_PROVIDER,useValue: {required: $localize`This field is required`,min: $localize`The minimun allowed values is{{min}}`,max: $localize`The max allowed value is{{max}}`,minlength: $localize`The minimun allowed length is{{requiredLength}}`,maxlength: $localize`The max allowed length is{{requiredLength}}`,email: $localize`Invalid email`,pattern: $localize`Invalid pattern`,},}, ... ],};If the application uses ngx-translate, the following settings are required:
- Install the message parser service for
ngx-translate
npm install --save ngx-formcontrol-msgs-translate-parser- Provide
ERROR_MSG_COMPONENT_FACTORYinApplicationConfigusing classTranslateErrorMsgComponentFactoryService
exportconstappConfig: ApplicationConfig={providers: [ ... {provide: ERROR_MSG_COMPONENT_FACTORY,useClass: TranslateErrorMsgComponentFactoryService,}, ... ],};- Add the messages in the locale file of each language (as usual for
ngx-translate)
English (en.json)
{..."FORM_ERROR_MESSAGES":{"REQUIRED": "This field is required", "MIN": "The minimun allowed values is{{min}}", "MAX": "The max allowed value is{{max}}", "MINLENGTH": "The minimun allowed length is{{requiredLength}}", "MAXLENGTH": "The max allowed length is{{requiredLength}}", "EMAIL": "Invalid email", "PATTERN": "Invalid pattern", "CUSTOM": "Ups, something went wrong", ... } ... }Spanish (es.json)
{..."FORM_ERROR_MESSAGES":{"REQUIRED": "Este campo es obligatorio", "MIN": "El mínimo valor permitido es{{min}}", "MAX": "El máximo valor permitido es{{max}}", "MINLENGTH": "El mínimo número de caracteres es{{requiredLength}}", "MAXLENGTH": "El máximo número de caracteres es{{requiredLength}}", "EMAIL": "Email inválido", "PATTERN": "Entrada inválida", "CUSTOM": "Ups, Algo salió mal", ... } ... }- Provide
FORM_ERROR_MESSAGES_PROVIDERreferencing the values in the locale files
exportconstappConfig: ApplicationConfig={providers: [ ... {provide: FORM_ERROR_MESSAGES_PROVIDER,useValue: {required: "FORM_ERROR_MESSAGES.REQUIRED",min: "FORM_ERROR_MESSAGES.MIN",max: "FORM_ERROR_MESSAGES.MAX",minlength: "FORM_ERROR_MESSAGES.MINLENGTH",maxlength: "FORM_ERROR_MESSAGES.MAXLENGTH",email: "FORM_ERROR_MESSAGES.EMAIL",pattern: "FORM_ERROR_MESSAGES.PATTERN",custom: "FORM_ERROR_MESSAGES.CUSTOM", ... },}, ... ],};If the application uses I18N methods other than NGX-TRANSLATE or Angular I18N, there are two posible aproaches: service driven, component driven
- Create a class or service that implements
ErrorMsgParserand override the methodparseto return customized translations that could reliy on a custom I18N service
@Injectable({ ... })exportclassCustomMsgParserServiceimplementsErrorMsgParser{constructor(privatereadonlyi18nService: CustomI18NService, @Inject(FORM_ERROR_MESSAGES_PROVIDER)privatecustomErrorMessages: KeyValueObject){}parse(error: ValidationErrors): string{ ... // Develop the logic to translate `customErrorMessages` using `CustomI18NService` ... }}- Provide
ERROR_MSG_PARSERinApplicationConfigusing the custom class created in the previous step.
exportconstappConfig: ApplicationConfig={providers: [ ... {provide: ERROR_MSG_PARSER,useClass: CustomMsgParserService,}, ... ],};Select component driven aproach whenever you want to use any existing pipe, like the translate pipe available in ngx-translate, this way you can provide a custom component that uses the already available pipes.
- Create a component class that implements
ErrorMsgComponent
@Component({ ... })exportclassCustomErrorMsgComponentimplementsErrorMsgComponent{ @Input()messages: ErrorMessage[]; ... }ErrorMessage is an interface with two properties:
exportinterfaceErrorMessage{/** * String to be displayed, customized or translated */message: string;/** * ValidationError content to be replaced in the `message` string */value?: unknown;}- Create a service that implements
ErrorMessageComponentFactory, thecreateComponentmethod should return aComponentRefof the component created in the previous step
@Injectable({ ... })exportclassCustomMsgComponentFactoryServiceimplementsErrorMessageComponentFactory{constructor(){}createComponent(viewContainerRef: ViewContainerRef): ComponentRef<CustomErrorMsgComponent>{returnviewContainerRef.createComponent(CustomErrorMsgComponent);}}- Provide
ERROR_MSG_COMPONENT_FACTORYinApplicationConfigusing classCustomMsgComponentFactoryService(created in the previous step)
exportconstappConfig: ApplicationConfig={providers: [ ... {provide: ERROR_MSG_COMPONENT_FACTORY,useClass: CustomMsgComponentFactoryService,}, ... ],};This module does not provide any CSS stylesheet or settings, so a custom style must be applied to fit the look and feel of the application.
This directive attaches a ngx-formcontrol-errors component as siblings of the input elements, styles to those components can be applied globally in the styles.scss of the application
:root{--error-color:#ff0000} ngx-formcontrol-errors{display: block; font-size:0.75rem; color:var(--error-color); text-align: right; min-height:1rem}Styles can also be applied at component level using ng-deep
::ng-deepngx-formcontrol-errors{display: block; font-size:0.75rem; color:var(--error-color); text-align: right; min-height:1rem}If you create a Custom error component like in Component driven, you have to replace ngx-formcontrol-errors for your custom component selector.