Skip to content

NgxControlError

Created by Robby Rabbitman

NgxControlError is a structural directive for displaying form control errors consistently while reducing boilerplate.

Import

import { NgxControlError } from 'ngxtension/control-error';

Usage

<label>
<b>Name</b>
<input type="text" [formControl]="name" />
<strong *ngxControlError="name; track: 'required'">Name is required.</strong>
</label>

The template will be rendered, when the control is in an error state and its errors include the tracked error(s).

without NgxControlError:

<label>
<b>Name</b>
<input type="text" [formControl]="name" />
@if (name.hasError('required') && (name.touched || form.submitted)) {
<strong>Name is required.</strong>
}
</label>

Configuration

A StateMatcher defines when the provided control is in an error state. A StateMatcher is a function which returns an observable. Every time the StateMatcher emits a value, the directive checks whether it should render or hide its template: The directive renders its template when the StateMatcher emits true and the errors of the control include at least 1 tracked error, else its template will be hidden.

export type StateMatcher = (
control: AbstractControl,
parent?: FormGroupDirective | NgForm,
) => Observable<boolean>;

Per default the control is considered in an error state when 1. its status is INVALID and 2. it is touched or its form has been submitted.

You can override this behavior:

/**
* A control is in an error state when its status is invalid.
* Emits whenever statusChanges emits.
* You may want to add more sources, such as valueChanges.
*/
export const customErrorStateMatcher: StateMatcher = (control) =>
control.statusChanges.pipe(
startWith(control.status),
map((status) => status === 'INVALID'),
);

Via DI

provideNgxControlError({ errorStateMatcher: customErrorStateMatcher });

Via Input

<label>
<b>Name</b>
<input type="text" [formControl]="name" />
<strong
*ngxControlError="name; track: 'required'; errorStateMatcher: customErrorStateMatcher"
>
Name is required.
</strong>
</label>

Integration

NGX Translate

You can iterate over all possible errors and pass the errors to the translate pipe:

<label>
<b>Mail</b>
<input type="email" [formControl]="mail" />
@for (error of ['required', 'email', 'myCustomError']; track error) {
<strong *ngxControlError="mail; track: error">
{{ "PATH.TO.MAIL_CONTROL.ERRORS." + error | translate: mail.errors }}
</strong>
}
</label>

Angular Material

<mat-form-field>
<mat-label>Name</mat-label>
<input matInput [formControl]="name" />
<mat-error *ngxControlError="name; track: 'required'">
Name is required.
</mat-error>
</mat-form-field>