Write Angular like a pro. Angular Icon

Follow the ultimate Angular roadmap.

Introducing ngxErrors, declarative form errors for Angular

I’ve been working on an open source project to bring better validation to Angular’s reactive forms. In this post we’ll take a look at how to use it, why I created it and the problems it aims to solve.

Go to GitHub repo for latest updates as the API has evolved with new features since writing this article

What is ngxErrors?

If you’re familiar with AngularJS (1.x) then you’ve likely stumbled across ngMessages. It’s a really tidy form errors module that allows you to declaratively add errors to your templates using a switch-statement style approach.

ngxErrors is my stab at the beginning of achieving similar results using Angular (v2 and onwards), with a little more ease of use, less directives and making use of observables.

Problem to solve

The problem that ngxErrors aims to solve is the template side of adding validation messages to your templates. It’s specficially designed in a way to make things easy.

We’ll boot off this demo with this component code:

export class AppComponent implements OnInit {

  form: FormGroup;

  constructor(
    private fb: FormBuilder
  ) {}

  ngOnInit() {
    this.form = this.fb.group({
      username: ['', [Validators.required]],
      password: ['', [Validators.required, Validators.minLength(5), Validators.maxLength(12)]]
    });
  }

}

Nice and simple, two form controls with a few validation rules. Then, the template:

<form [formGroup]="form" (ngSubmit)="onSubmit()">

  <input type="text" formControlName="username">
  <div>
    <div>
      Username is required
    </div>
  </div>

  <input type="text" formControlName="password">
  <div>
    <div>
      Password is required
    </div>
    <div>
      5 characters minimum, 12 characters maximum
    </div>
  </div>

</form>

Looks clean, let’s then add their conditional triggers:

<form [formGroup]="form" (ngSubmit)="onSubmit()">

  <input type="text" formControlName="username">
  <div>
    <div>
      Username is required
    </div>
  </div>

  <input type="text" formControlName="password">
  <div>
    <div>
      Password is required
    </div>
    <div>
      5 characters minimum, 12 characters maximum
    </div>
  </div>

</form>

In the words of Ron Burgundy - that escalated quickly.

Angular Directives In-Depth eBook Cover

Free eBook

Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.

  • Green Tick Icon Observables and Async Pipe
  • Green Tick Icon Identity Checking and Performance
  • Green Tick Icon Web Components <ng-template> syntax
  • Green Tick Icon <ng-container> and Observable Composition
  • Green Tick Icon Advanced Rendering Patterns
  • Green Tick Icon Setters and Getters for Styles and Class Bindings

Wouldn’t it be nice to add this behaviour in just a few simple steps? With ngxErrors you can! Here’s the exact same example above using ngxErrors:

<form [formGroup]="form" (ngSubmit)="onSubmit()">

  <input type="text" formControlName="username">
  <div>
    <div>
      Username is required
    </div>
  </div>

  <input type="text" formControlName="password">
  <div>
    <div>
      Password is required
    </div>
    <div>
      5 characters minimum, 12 characters maximum
    </div>
  </div>

</form>

It took me a while to get the design of this API to what I’d consider a clean and readable solution. Let’s dive into some further explanations as to how each piece works.

ngxErrors

ngxErrors requires reactive forms and a form control to use the directive on.

ngxErrors directive

Taking this example:

<input type="text" formControlName="username">

We have the formControlName with a value of "username". To kick off ngxErrors all we need to do is pass the value into it:

<input type="text" formControlName="username">
<div ngxErrors="username">

</div>

This will then tell ngxErrors to look for status changes for that particular form control, as well as props such as “dirty”, “pristine”, “touched” - and error states such as “required” and friends. It also supports custom validators and async validators.

ngxError directive

Next, ngxErrors requires an “ngxError” - a single validation message you wish to display:

<input type="text" formControlName="username">
<div ngxErrors="username">
  <div ngxError="required">
    Password is required
  </div>
</div>

The ngxError directive accepts a string or array of values to then match against their particular error, giving us three possible syntaxes:

<div ngxError="required">
  Password is required
</div>
<div [ngxError]="'required'">
  Password is required
</div>
<div [ngxError]="['required', 'minlength']">
  Password is required
</div>

When using the array syntax, you’ll need to remember to [] data bind the values.

ngxError #when

The when directive is the controller of visibility rules. Ideally we don’t want to display messages at runtime without user interaction so we can hook into "touched" or similar to await user interaction:

<input type="text" formControlName="username">
<div ngxErrors="username">
  <div ngxError="required" when="touched">
    Password is required
  </div>
</div>

This also supports a string as well as array syntaxes for multiple conditions:

<div ngxError="required" when="touched">
  Password is required
</div>
<div ngxError="required" [when]="'touched'">
  Password is required
</div>
<div ngxError="required" [when]="['touched', 'dirty']">
  Password is required
</div>

Dynamic errors

With ngxErrors you can also dynamically render out your messages:

@Component({
  selector: 'app-root',
  template: `
    <form [formGroup]="form" (ngSubmit)="onSubmit()">
      <input type="text" formControlName="username">
      <div ngxErrors="person.username">
        <div 
          *ngFor="let error of errors" 
          [ngxError]="error.name" 
          [when]="error.rules">
          {{ error.text }}
        </div>
      </div>
    </form>
  `,
})
export class AppComponent implements OnInit {
  errors = [
    {
      name: 'required',
      text: 'This field is required',
      rules: ['touched', 'dirty'],
    },
    { name: 'minlength', text: 'Min length is 5', rules: ['dirty'] },
  ];
  //...
}

Live demo

Check out the live demo below:

Installing it

You can check out the GitHub repo for further documentation.

To install in your project is super simple:

yarn add @ultimate/ngxerrors

# OR
npm i @ultimate/ngxerrors

From there, you’ll just include it in your @NgModule and off you go:

import { NgxErrorsModule } from '@ultimate/ngxerrors';

@NgModule({ imports: [NgxErrorsModule] })
export class AppModule {}

And you’re good to go!

Summing up

I’m sure this is just the beginning of the library, but I think it’s headed in a good direction to achieve easy validation with Angular’s reactive forms. Hope you make use of it!

Learn Angular the right way.

The most complete guide to learning Angular ever built.
Trusted by 82,951 students.

Todd Motto

with Todd Motto

Google Developer Expert icon Google Developer Expert

Related blogs 🚀

Free eBooks:

Angular Directives In-Depth eBook Cover

JavaScript Array Methods eBook Cover

NestJS Build a RESTful CRUD API eBook Cover