ngx-reactive-form-extensions
v2.2.1
Published
[](https://gitlab.com/Riuen/reactive-form-extensions/-/commits/main) [
A lightweight, zero-dependency Angular library that enhances Reactive Forms with automatic validation message display, dynamic CSS error classes, and input trimming — all through simple directives. Eliminate boilerplate and let your forms handle themselves.
Table of Contents
- Features
- Installation
- Getting Started
- Directives
- Global Configuration
- Extended Validators (
RfxValidators) - Utility Functions
- Full Example
Features
- Automatic validation messages — Display contextual error messages below form fields without writing any conditional template logic.
- Dynamic CSS error classes — Automatically add/remove CSS classes on form fields based on their validation state.
- Input trimming — Trim leading and trailing whitespace on blur, with an option to convert empty strings to
null. - Extended validators — Drop-in replacements for Angular's built-in validators that support custom error messages, plus additional validators like
fieldMatch,passwordComplexity,dateAfter, anddateBefore. - Utility functions — Programmatically append or remove errors from form controls.
Installation
npm install ngx-reactive-form-extensionsAngular Version Compatibility
| Angular Version | Library Version | |-----------------|-----------------| | ≤ 15.x | v1.0.4 | | 16.x | v2.0.8 | | ≥ 17.x | v2.1.x+ |
Note: Starting from v2.0.0, all directives are standalone. For v1.x, import
NgxReactiveFormExtensionsModuleinstead.
Getting Started
Import the directives you need directly into your standalone component or module:
// Standalone component
@Component({
selector: 'app-my-form',
imports: [
ReactiveFormsModule,
ShowValidationMessagesDirective,
ChangeClassOnErrorDirective,
TrimDirective
],
templateUrl: './my-form.component.html'
})
export class MyFormComponent { }// Or in an NgModule
@NgModule({
imports: [
ReactiveFormsModule,
ShowValidationMessagesDirective,
ChangeClassOnErrorDirective,
TrimDirective
]
})
export class MyFeatureModule { }Then add the directives to any reactive form field:
<form [formGroup]="form">
<input type="text" formControlName="firstName" showValidationMessages changeClassOnError trim />
</form>That's it — validation messages appear automatically, error classes are applied, and inputs are trimmed on blur.
Directives
Displaying Validation Messages (showValidationMessages)
Automatically displays the relevant error message below an invalid form field. Works with both Angular's built-in Validators and the extended RfxValidators.
Default messages for Angular's built-in validators:
| Validator | Default Message |
|--------------|------------------------------------------------------------------------------|
| required | This field is required. |
| min | Value must be greater than or equal to {min}. |
| max | Value must be less than or equal to {max}. |
| minlength | Value entered must be greater than or equal to {requiredLength} characters.|
| maxlength | Value entered must be less than or equal to {requiredLength} characters. |
| pattern | Invalid input. |
| email | Invalid email address. |
When using RfxValidators, the custom message you provide is displayed instead.
Usage:
<input type="text" formControlName="firstName" showValidationMessages />Configurable attributes:
| Attribute | Values | Description |
|------------------------------|---------------------|-------------|
| validationMessageClass | string | CSS class applied to the validation message element. |
| validationTriggerCondition | touched / dirty | When to start showing messages. Default: touched. |
Example with custom CSS class:
<input type="text" formControlName="email"
showValidationMessages
validationMessageClass="text-danger" />Changing CSS Class on Error (changeClassOnError)
Adds a CSS class to the form field when it has validation errors, and removes it when the field becomes valid or the form is reset.
Usage:
<input type="text" formControlName="firstName" changeClassOnError />Configurable attributes:
| Attribute | Values | Description |
|------------------------------|---------------------|-------------|
| errorClass | string | The CSS class to apply on error. Overrides the global default. |
| validationTriggerCondition | touched / dirty | When to start applying the class. Default: touched. |
Example with Bootstrap:
<input type="text" formControlName="email"
class="form-control"
changeClassOnError
errorClass="is-invalid" />Trimming Inputs (trim)
Trims leading and trailing whitespace from text inputs on blur. Only applies to trimmable input types: text, password, email, tel, search, and url. Date fields and non-string values are left untouched.
Usage:
<input type="text" formControlName="firstName" trim />Configurable attributes:
| Attribute | Values | Description |
|------------------|------------------|-------------|
| setEmptyToNull | true / false | Convert empty/whitespace-only values to null after trimming. Overrides the global default. |
Example:
<input type="text" formControlName="firstName" trim setEmptyToNull="true" />Global Configuration
Set default behavior for all directives by providing an RfxConfig via the RFX_CONFIG injection token:
import { RfxConfig, RFX_CONFIG } from 'ngx-reactive-form-extensions';
const rfxConfig: RfxConfig = {
showValidationMessages: {
cssClass: 'error-text', // Default: ''
validationTriggerCondition: 'touched' // Default: 'touched'
},
trim: {
setEmptyValuesToNull: true // Default: false
},
changeClassOnError: {
cssClass: 'is-invalid', // Default: ''
validationTriggerCondition: 'touched' // Default: 'touched'
}
};
// In a standalone application
bootstrapApplication(AppComponent, {
providers: [
{ provide: RFX_CONFIG, useValue: rfxConfig }
]
});
// Or in an NgModule
@NgModule({
providers: [
{ provide: RFX_CONFIG, useValue: rfxConfig }
]
})
export class AppModule { }All configuration properties are optional — only include the directives you want to customize.
RfxConfig Reference
type RfxConfig = {
showValidationMessages?: {
cssClass?: string; // CSS class for the error message element
validationTriggerCondition?: 'touched' | 'dirty' // When to show messages
};
trim?: {
setEmptyValuesToNull: boolean // Convert empty strings to null
};
changeClassOnError?: {
cssClass?: string; // CSS class to add on error
validationTriggerCondition?: 'touched' | 'dirty' // When to apply the class
};
};Extended Validators (RfxValidators)
RfxValidators is a drop-in replacement for Angular's Validators class. Each validator accepts an optional custom error message that integrates seamlessly with the showValidationMessages directive.
Validator Wrappers with Custom Messages
These wrap Angular's built-in validators and add custom message support:
import { RfxValidators } from 'ngx-reactive-form-extensions';
const form = new FormGroup({
name: new FormControl(null, RfxValidators.required('Name is required.')),
age: new FormControl(null, RfxValidators.min(18, 'Must be at least 18.')),
score: new FormControl(null, RfxValidators.max(100, 'Cannot exceed 100.')),
bio: new FormControl(null, RfxValidators.maxLength(500, 'Bio is too long.')),
code: new FormControl(null, RfxValidators.minLength(3, 'Code is too short.')),
id: new FormControl(null, RfxValidators.pattern(/^\d+$/, 'Must be numeric.')),
email: new FormControl(null, RfxValidators.email('Enter a valid email.')),
});| Validator | Parameters | Default Message |
|--------------|-----------------------------------------------|-----------------|
| required | message?: string | This field is required. |
| min | value: number, message?: string | Value must be greater than or equal to {min}. |
| max | value: number, message?: string | Value must be less than or equal to {max}. |
| minLength | value: number, message?: string | Value entered must be greater than or equal to {requiredLength} characters. |
| maxLength | value: number, message?: string | Value entered must be less than or equal to {requiredLength} characters. |
| pattern | pattern: string \| RegExp, message?: string | Invalid input. |
| email | message: string | Invalid email. |
Additional Validators
fieldMatch
Validates that two form controls have the same value. Useful for password confirmation fields. Must be added at the FormGroup level.
const form = new FormGroup({
password: new FormControl(null, Validators.required),
passwordConfirm: new FormControl(null, Validators.required),
});
form.addValidators(RfxValidators.fieldMatch('password', 'passwordConfirm', 'Passwords do not match.'));| Parameter | Type | Required | Description |
|-----------|----------|----------|-------------|
| field1 | string | Yes | Form control name of the first field. |
| field2 | string | Yes | Form control name of the second field. |
| message | string | No | Default: Value for {field1} does not match {field2}. |
passwordComplexity
Requires at least 1 lowercase letter, 1 uppercase letter, 1 digit, and 1 special character.
new FormControl(null, RfxValidators.passwordComplexity('Password is too weak.'));| Parameter | Type | Required | Description |
|-----------|----------|----------|-------------|
| message | string | No | Default: Password must contain at least 1 lowercase, uppercase, digit and a special character. |
dateAfter
Validates that the field value is a date occurring after the specified date.
new FormControl(null, RfxValidators.dateAfter(new Date(), 'Date must be in the future.'));| Parameter | Type | Required | Description |
|-----------|-------------------------------|----------|-------------|
| date | Date or valid date string | Yes | The date to compare against. |
| message | string | No | Default: Date entered must be greater than {formattedDate}. |
dateBefore
Validates that the field value is a date occurring before the specified date.
new FormControl(null, RfxValidators.dateBefore('2030-01-01', 'Must be before 2030.'));| Parameter | Type | Required | Description |
|-----------|-------------------------------|----------|-------------|
| date | Date or valid date string | Yes | The date to compare against. |
| message | string | No | Default: Date entered must be less than {formattedDate}. |
Utility Functions
Programmatically add or remove errors from form controls:
import { appendFormControlError, removeFormControlError } from 'ngx-reactive-form-extensions';
// Add a custom error to a control
appendFormControlError(form.get('email'), 'emailTaken', 'This email is already in use.');
// Remove a specific error from a control
removeFormControlError(form.get('email'), 'emailTaken');| Function | Parameters | Description |
|---------------------------|---------------------------------------------------------------------|-------------|
| appendFormControlError | control: AbstractControl, errorName: string, errorMessage: string | Adds an error to the control. No-op if the error already exists. |
| removeFormControlError | control: AbstractControl, errorName: string | Removes a specific error from the control. Clears all errors if none remain. |
Full Example
A registration form with validation messages, error styling, input trimming, password matching, and password complexity:
Component:
import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import {
ShowValidationMessagesDirective,
ChangeClassOnErrorDirective,
TrimDirective,
RfxValidators,
RFX_CONFIG,
RfxConfig
} from 'ngx-reactive-form-extensions';
const rfxConfig: RfxConfig = {
showValidationMessages: { cssClass: 'error-text', validationTriggerCondition: 'touched' },
trim: { setEmptyValuesToNull: true },
changeClassOnError: { cssClass: 'is-invalid', validationTriggerCondition: 'touched' }
};
@Component({
selector: 'app-register',
imports: [
ReactiveFormsModule,
ShowValidationMessagesDirective,
ChangeClassOnErrorDirective,
TrimDirective
],
providers: [{ provide: RFX_CONFIG, useValue: rfxConfig }],
templateUrl: './register.component.html'
})
export class RegisterComponent {
form = new FormGroup({
firstName: new FormControl(null, [Validators.required, Validators.maxLength(30)]),
lastName: new FormControl(null, [Validators.required, Validators.maxLength(30)]),
email: new FormControl(null, [Validators.required, Validators.email]),
password: new FormControl(null, [Validators.required, RfxValidators.passwordComplexity()]),
passwordConfirm: new FormControl(null, Validators.required),
});
constructor() {
this.form.addValidators(
RfxValidators.fieldMatch('password', 'passwordConfirm', 'Passwords do not match.')
);
}
}Template:
<form [formGroup]="form">
<label>First Name</label>
<input type="text" formControlName="firstName" trim showValidationMessages changeClassOnError />
<label>Last Name</label>
<input type="text" formControlName="lastName" trim showValidationMessages changeClassOnError />
<label>Email</label>
<input type="email" formControlName="email" trim showValidationMessages changeClassOnError />
<label>Password</label>
<input type="password" formControlName="password" trim showValidationMessages changeClassOnError />
<label>Confirm Password</label>
<input type="password" formControlName="passwordConfirm" trim showValidationMessages changeClassOnError />
</form>