npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ngx-reactive-form-extensions

v2.2.1

Published

[![pipeline status](https://gitlab.com/Riuen/reactive-form-extensions/badges/main/pipeline.svg)](https://gitlab.com/Riuen/reactive-form-extensions/-/commits/main) [![coverage report](https://gitlab.com/Riuen/reactive-form-extensions/badges/main/coverage.s

Readme

pipeline status coverage report

Reactive Form Extensions (ngx-reactive-form-extensions)

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

  • 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, and dateBefore.
  • Utility functions — Programmatically append or remove errors from form controls.

Installation

npm install ngx-reactive-form-extensions

Angular 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 NgxReactiveFormExtensionsModule instead.


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>

License

MIT