ngx-error-message
Version:
A package to display error messages in angular forms
293 lines (282 loc) • 12.5 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, inject, Injectable, Pipe, ElementRef, Renderer2, Input, Component, ViewContainerRef, Directive, NgModule } from '@angular/core';
import { NgControl } from '@angular/forms';
import { TranslateService, TranslateModule } from '@ngx-translate/core';
import { Subject, distinctUntilChanged } from 'rxjs';
import { CommonModule } from '@angular/common';
const octetPattern = '(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})';
const countryCode = '\\+?[-.\\s]?\\d{1,2}[-.\\s]?';
const areaCode = '\\(?\\d{3}\\)?';
const phoneNumber = '\\d{3}[-.\\s]?\\d{4}';
const protocol = '(https?:\\/\\/)?';
const domain = '[\\w\\d-]+(\\.[\\w\\d-]+)+';
const path = '(\\/[-\\w\\d.]*)*\\/?';
const query = '(\\?[\\w\\d=&]*)?';
const fragment = '(#[\\w\\d-]*)?';
const regEx = {
phoneNumber: new RegExp(`^(${countryCode})?${areaCode}[-.\\s]?${phoneNumber}$`),
websiteUrl: new RegExp(`^${protocol}${domain}${path}${query}${fragment}$`),
numeric: /^\d+$/,
smallLetters: /^[a-z]+$/,
capitalLetters: /^[A-Z]+$/,
alphabet: /^[a-zA-Z\s\u00C0-\u017F]+$/,
alphaNumeric: /^[a-zA-Z0-9\s\u00C0-\u017F]+$/,
ip: new RegExp(`^(${octetPattern})(\\.(${octetPattern})){3}$`),
};
const requiredRegex = /^((?!actual).)*$/;
const ERROR_MESSAGE_CONFIG = new InjectionToken('ERROR_MESSAGE_CONFIG');
class NgxErrorMessageService {
config = inject(ERROR_MESSAGE_CONFIG);
translate = inject(TranslateService, { optional: true });
getErrorMessage(controlErrors, patternKey, fieldName) {
const lastError = Object.entries(controlErrors).pop();
if (!lastError) {
return '';
}
const [errorKey, errorValue] = lastError;
if (typeof errorValue === 'boolean') {
return this.getMessage(errorKey, fieldName);
}
if (errorKey === 'pattern') {
const patternErrorKey = this.patternMatchExpression(errorValue, patternKey);
return this.getMessage(patternErrorKey, fieldName);
}
const requiredValue = this.getValueByRegexFromObject(errorValue, requiredRegex);
return this.getMessage(errorKey, fieldName, requiredValue);
}
patternMatchExpression(value, patternKey) {
const pattern = value['requiredPattern'];
const regExpDefault = Object.entries(regEx).find(([, val]) => val.toString() === pattern);
return `${this.config.patternsPrefix}.${regExpDefault ? regExpDefault[0] : patternKey}`;
}
getValueByRegexFromObject(obj, regex) {
const [, findValue] = Object.entries(obj).find(([key]) => regex.test(key)) ?? [];
return findValue;
}
interpolateMessage(message, params) {
return message.replace(/\{\{(\w+)\}\}/g, (_, key) => params[key] ?? '');
}
getNestedMessage(obj, path) {
return path
.split('.')
.reduce((acc, key) => acc && acc[key], obj);
}
getMessage(key, fieldName, param) {
const options = {
...(fieldName && { fieldName }),
...(param && { param }),
};
if (Object.keys(this.config.errorMessages).length > 0) {
const messageTemplate = this.getNestedMessage(this.config.errorMessages, key) ?? '';
return this.interpolateMessage(messageTemplate, options);
}
return param || fieldName
? this.translate?.instant(`${this.config.validationsPrefix}.${key}`, options)
: this.translate?.instant(`${this.config.validationsPrefix}.${key}`);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: NgxErrorMessageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: NgxErrorMessageService });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: NgxErrorMessageService, decorators: [{
type: Injectable
}] });
class NgxErrorMessagePipe {
errorMessageService = inject(NgxErrorMessageService);
cachedData;
cachedError = '';
cachedLang = '';
transform(value, lang, patternKey, fieldName) {
if (!value) {
return '';
}
if (lang !== this.cachedLang) {
this.cachedLang = lang ?? '';
this.cachedError = '';
}
const [lastErrorKey] = Object.keys(value).slice(-1);
if (lastErrorKey !== this.cachedError) {
this.cachedError = lastErrorKey;
this.cachedData = this.errorMessageService.getErrorMessage(value, patternKey, fieldName);
}
return this.cachedData ?? '';
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: NgxErrorMessagePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.8", ngImport: i0, type: NgxErrorMessagePipe, isStandalone: true, name: "ngxErrorMessage" });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: NgxErrorMessagePipe, decorators: [{
type: Pipe,
args: [{
name: 'ngxErrorMessage',
}]
}] });
class NgxErrorMessageComponent {
classNames;
fieldName;
ngControl;
when;
patternKey;
lang;
translate = inject(TranslateService);
elementRef = inject(ElementRef);
renderer = inject(Renderer2);
destroy$ = new Subject();
previousErrorState = false;
get hasError() {
const invalid = Array.isArray(this.when)
? this.when.every((condition) => this.ngControl[condition])
: this.ngControl[this.when];
if (this.previousErrorState !== invalid) {
this.previousErrorState = !!invalid;
this.updateErrorContainer(!!invalid);
}
return !!invalid;
}
ngOnInit() {
this.translate.onLangChange
.pipe(distinctUntilChanged())
.subscribe(({ lang }) => (this.lang = lang));
}
updateErrorContainer(invalid) {
const inputElement = this.elementRef.nativeElement.previousElementSibling;
if (!inputElement) {
return;
}
const errorClass = this.classNames.control;
invalid
? this.renderer.addClass(inputElement, errorClass)
: this.renderer.removeClass(inputElement, errorClass);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: NgxErrorMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.8", type: NgxErrorMessageComponent, isStandalone: true, selector: "ngx-error-message", inputs: { classNames: "classNames", fieldName: "fieldName", ngControl: "ngControl", when: "when", patternKey: "patternKey" }, ngImport: i0, template: `
(hasError) {
<small [class]="classNames.message">{{
ngControl.errors | ngxErrorMessage: lang : patternKey : fieldName
}}</small>
}
`, isInline: true, dependencies: [{ kind: "pipe", type: NgxErrorMessagePipe, name: "ngxErrorMessage" }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: NgxErrorMessageComponent, decorators: [{
type: Component,
args: [{
selector: 'ngx-error-message',
template: `
(hasError) {
<small [class]="classNames.message">{{
ngControl.errors | ngxErrorMessage: lang : patternKey : fieldName
}}</small>
}
`,
imports: [NgxErrorMessagePipe],
}]
}], propDecorators: { classNames: [{
type: Input
}], fieldName: [{
type: Input
}], ngControl: [{
type: Input
}], when: [{
type: Input
}], patternKey: [{
type: Input
}] } });
class NgxErrorMessageDirective {
set ngxErrorMessage(value) {
this.fieldName = value;
if (!this.componentRef) {
return;
}
this.componentRef.instance.fieldName = this.fieldName;
}
classNames = {
control: 'error-container',
message: 'error-message',
};
patternKey;
when = ['invalid', 'touched'];
fieldName = '';
ngControl = inject(NgControl);
container = inject(ViewContainerRef);
componentRef;
ngOnInit() {
const hostViewContainerRef = this.container;
if (!this.componentRef) {
hostViewContainerRef.clear();
this.componentRef = hostViewContainerRef.createComponent(NgxErrorMessageComponent);
}
const instance = this.componentRef.instance;
instance.classNames = this.classNames;
instance.ngControl = this.ngControl;
instance.patternKey = this.patternKey;
instance.when = this.when;
instance.fieldName = this.fieldName;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: NgxErrorMessageDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.8", type: NgxErrorMessageDirective, isStandalone: true, selector: "[ngxErrorMessage]", inputs: { ngxErrorMessage: "ngxErrorMessage", classNames: "classNames", patternKey: "patternKey", when: "when" }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: NgxErrorMessageDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngxErrorMessage]',
}]
}], propDecorators: { ngxErrorMessage: [{
type: Input
}], classNames: [{
type: Input
}], patternKey: [{
type: Input
}], when: [{
type: Input
}] } });
class NgxErrorMessageModule {
static forRoot(config) {
return NgxErrorMessageModule.forRootOrChild(config);
}
static forChild(config) {
return NgxErrorMessageModule.forRootOrChild(config);
}
static forRootOrChild(config) {
return {
ngModule: NgxErrorMessageModule,
providers: [
{
provide: ERROR_MESSAGE_CONFIG,
useValue: {
validationsPrefix: config?.validationsPrefix ?? 'validations',
patternsPrefix: config?.patternsPrefix ?? 'pattern',
errorMessages: config?.errorMessages ?? {},
},
},
NgxErrorMessageService,
],
};
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: NgxErrorMessageModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.8", ngImport: i0, type: NgxErrorMessageModule, imports: [CommonModule,
TranslateModule,
NgxErrorMessageComponent,
NgxErrorMessageDirective,
NgxErrorMessagePipe], exports: [NgxErrorMessageDirective] });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: NgxErrorMessageModule, imports: [CommonModule,
TranslateModule] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: NgxErrorMessageModule, decorators: [{
type: NgModule,
args: [{
imports: [
CommonModule,
TranslateModule,
NgxErrorMessageComponent,
NgxErrorMessageDirective,
NgxErrorMessagePipe,
],
exports: [NgxErrorMessageDirective],
}]
}] });
/*
* Public API Surface of ngx-error-message
*/
/**
* Generated bundle index. Do not edit.
*/
export { NgxErrorMessageDirective, NgxErrorMessageModule, NgxErrorMessageService, regEx };
//# sourceMappingURL=ngx-error-message.mjs.map