You have unlimited access as a PRO member
You are receiving a free preview of 3 lessons
Your free preview as expired - please upgrade to PRO
Contents
Recent Posts
- Object Oriented Programming With TypeScript
- Angular Elements Advanced Techniques
- TypeScript - the Basics
- The Real State of JavaScript 2018
- Cloud Scheduler for Firebase Functions
- Testing Firestore Security Rules With the Emulator
- How to Use Git and Github
- Infinite Virtual Scroll With the Angular CDK
- Build a Group Chat With Firestore
- Async Await Pro Tips
Angular Reactive Forms Async Validation With Firebase
Episode 87 written by Jeff DelaneyReactive form validation can be a complex and difficult feature to implement, especially if you need to validate fields asynchronously. Angular ships with a few built-in validators, but they can only take you so far…
Today, we are building a custom async validator that can verify username uniqueness in Firebase Firestore. My goal is to show you async validator for your reactive forms that you can apply to virtually any backend data source.
Full source code for custom async validators Angular.
Initial Setup
This tutorial assumes you have…
- An Angular 5.x project
- Installed AngularFire2
- You have a collection of users with usernames (so we have something to validate uniqueness against)
Your app.module.ts
should have the following imports:
@NgModule({ |
If you’re new to Firebase auth, I recommend also checking out Episode 55 Custom Firebase User Data
Building the Reactive Form
Now let’s put together a basic reactive form with some of Angular’s built-in validators.
import { Component, OnInit } from '@angular/core'; |
Our Reactive form’s HTML looks like this:
<form [formGroup]="loginForm" novalidate> |
Async Username Validator
First, take a close look at this interface - it gives us the signature that a custom validator must follow. It’s a function that takes a form control as it’s argument, then returns a error object if INVALID or null if VALID.
interface Validator<T extends FormControl> { |
You don’t need this interface in your code, I just want you to be aware of it.
Providing a Service in a Custom Validator
There are several ways we might inject a service into a custom validator, but I want to show you the way that is most flexible, especially if you plan on reusing the validator in multiple forms.
Basically, we wrap our validator function with an outer function that takes a service as an argument. This gives our inner validator access the outer arguments, which is AngularFirestore
in this case, but it could be any service. The pattern is similar to closures in vanilla JS.
The function is defined as a static method in a class named CustomValidators
. You can extract this logic to a new file, or define it directly in the component.
import { map, take, debounceTime } from 'rxjs/operators'; |
Fetching Data and Mapping the Validation Error
Inside the validation function, we first get access to the user’s input with control.value
, then use it to make a query to a Firestore collection with the matching value.
If firestore returns an empty array, we know the username is available. But if that array is not empty, the username is already taken.
By default, Firestore gives us a realtime stream of data, but what we actually want is an Obsevable that completes, which we can force with take(1)
. To prevent inefficient queries, I also added a debounceTime(500)
to wait 500ms after the user stops typing before making the query.
return (control: AbstractControl) => { |
Using the Custom Validator in the Form
The last step is easy. We just drop our custom validator into the reactive form and the rest is magic.
this.loginForm = this.fb.group({ |
Show an Async Validation Messages
At this point our validator will work, but we should also show the user some useful feedback. In the HTML below, we show three different messages based on the possible states of VALID, INVALID, and PENDING.
<div *ngIf="username.invalid && username.dirty" class="notification is-danger"> |
And there you have it - async uniqueness validation in Angular with Firestore as our data source. Let me know if you have any questions on Slack or in the comments below.