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
Table of contents
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.
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.
- Observables and Async Pipe
- Identity Checking and Performance
- Web Components <ng-template> syntax
- <ng-container> and Observable Composition
- Advanced Rendering Patterns
- 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!