Creating custom validators in Angular isn’t anything new for me, I must have created dozens of them over the years, but strangely enough I haven’t had to create “async” validators in recent memory. When I tried to find more information about making a custom validator that returned an observable (or a promise), there really wasn’t a heck of a lot of information out there. Or more so, it was spread out in piecemeal code slices.

This post should hopefully group some of the core concepts together, specifically, at the end I will show how to create a “debounce” validator that only runs when a user stops typing, as this for me was the hardest thing to find examples of!

Basic Async Validator In Angular

For the purposes of this article, I’m going to write an async validator that calls an API to check if a username is currently in use or not. Imagine I’m using this on a sign up form to make sure that no two users pick the same username.

The full code is actually quite simple and looks like so :

import { Directive,  forwardRef} from '@angular/core';
import { AbstractControl, AsyncValidator, NG_ASYNC_VALIDATORS, ValidationErrors } from '@angular/forms/';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AccountService } from '../services/account.service';

@Directive({
    selector: '[usernameCheck]', 
    providers: [
        {
            provide: NG_ASYNC_VALIDATORS, 
            useExisting: forwardRef(() => UsernameCheckDirective), 
            multi: true
      }
      ]
  })
  export class UsernameCheckDirective implements AsyncValidator {
    constructor(private accountService : AccountService) {
    }
  
    validate(control: AbstractControl): Observable<ValidationErrors | null> {
        return this.accountService.exists(control.value).pipe(map(x => x.exists ? { exists : true} : null))
    }
}

There are a couple of things to point out. First notice that my provider name is NG_ASYNC_VALIDATORS and *not* NG_VALIDATORS. This is so important because Angular won’t complain if you use NG_VALIDATOR here, instead it will just never show an error (Very very annoying and took me a very long time to debug).

Next notice that my validator method returns an observable. For this, I call my AccountService which has a method called “Exists”. This returns an object with a boolean property called “exists”. If true, it means the username is taken and I should return an object like so

{ exists : true}

Otherwise, I should return null.

Easy right?

When I write HTML to use this, I can just do the following :

<input name="username" 
    #username="ngModel" 
    type="text" 
    class="form-control" 
    [(ngModel)]="createAccount.name" 
    [usernameCheck]
    required />
<div class="validation-message" *ngIf="username?.errors?.exists">
    Username "{{createAccount.name}}" is already in use
</div>

Nothing too special and it works perfectly fine!

Ignoring Empty Values

The first issue I ran into is that as soon as my form loads, I could see it pinging the server to check if a username existed, before I’ve even typed a single character! What I needed was a quick short circuit to return an empty value if the control was empty. Something like this did the trick perfect!

validate(control: AbstractControl): Observable<ValidationErrors | null> {
    if(!control.value) {
        return of(null);
    }
    return this.accountService.exists(control.value).pipe(map(x => x.exists ? { exists : true} : null))
}

Now, when the value of my control is empty, the validator simply returns null (e.g. There are no errors), and we are good to go.

But then there was another issue….

Debouncing The Validation

What I noticed was that as I typed, I would validate every single keystroke by calling the backend API. This was way overkill and was resulting in 10’s of API calls when all someone was trying to do was fill in their username. What I needed was instead of validating every single key press, I needed to wait until the user stopped typing, then go ahead and validate the username. This in Angular terms is usually called “debouncing”.

Now I had worked with debounced textboxes before, infact, here’s an article I wrote on that very subject : https://tutorialsforangular.com/2021/03/17/creating-a-textbox-with-delayed-output-in-angular/! But the issue was that how could I debounce from within a validator?

Interestingly, I headed to the Angular documentation for help and all it told me was to use the ngModelOptions to only validate on blur like so :

<input [(ngModel)]="name" [ngModelOptions]="{updateOn: 'blur'}">

I personally loathe this method because I hate the fact a user has to click somewhere else on the screen to have validation run. Often forcing them to find some white space to click on the page.

Then I read about an interesting “quirk” within Angular’s Async Validator code. It stated that if an existing validation was in progress when another request for validation came in, it would cancel that initial validation and instead subscribe to that incoming validation request. I suppose it in all likelihood was so that you didn’t end up with a race condition where as you were typing, validators were resulting at odd times and giving mixed results. But could we use this to our advantage? As it so happens, yes we could!

Here’s our new async validate method :

validate(control: AbstractControl): Observable<ValidationErrors | null> {
    if(!control.value) {
        return of(null);
    }

    return of(control.value)
          .pipe(
              delay(250), 
              //Then instead return the observable of us actually checking the value. 
              switchMap((value) => 
                  this.accountService.exists(value).pipe(map(x => {
                      return x.exists ? {exists : true} : null;
                  }))
              )
          );
}

Here’s how it works. When the validator is kicked off, the first thing it does is it waits 250ms. That may seem dumb but there’s a reason. Imagine the user keeps typing, and validation kicks off again? What happens to that first validation request? It gets cancelled! Is that a bad thing? Well no because it was just waiting 250ms so no harm done, kill the process all you want! This continues until the user stops typing, and the full 250ms passes, at which point the backend service will be called!

I’ve seen variations of this that instead utilize debounce, timers, and all sorts of manually created observables. This for me, works without fail. And for me, it makes the most sense. All we are trying to do is tap into that native ability of Angular cancelling in flight validation requests on new validation requests. So we aren’t trying to work around Angular’s limitations, but instead work with it!

I wasn’t quite sure what to call this post, so instead let’s give give some scenarios in which you want to use a “Texbox with delayed output”

  • You want to create your own custom typeahead component that waits until a user stops typing before looking up results
  • You want a textbox that waits until a user stops typing, so you can call a backend API to check for duplicated names/emails/records
  • You want to add a text filter to a table/list, that only refreshes/filters when you stop typing in a textbox for a short period of time
  • In general, we want to use a textbox that waits until the user has stopped typing for a few milliseconds, and only then return the result to us

All of these can be solved using a Textbox with a delayed (or debounced) output. And it’s actually very simple using some helpful code from RxJS.

First I’ll give you the complete code, then point out the important bits :

@Component({
  selector: 'app-delayed-textbox',
  templateUrl: './delayed-textbox.component.html',
  styleUrls: ['./delayed-textbox.component.scss'], 
  providers : [
    { 
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => DebounceTextboxComponent),
      multi: true
    }
  ]
})
export class DelayedTextboxComponent implements OnInit {
  delayTime : number = 250
  
  currentText : string;
  
  private textChanged = new Subject();

  constructor() { }

  ngOnInit(): void {
    this.textChanged.pipe(debounceTime(this.delayTime)).subscribe(() => {
      this.propogateTouch();
      this.propagateChange(this.currentText);
    });
  }

  onTextInput() {
    this.textChanged.next();
  }

   //Start implementation of ControlValueAccessor
   propagateChange = (_: any) => {};
   propogateTouch = () => {};
 
   writeValue(obj: any): void {
     this.currentText = obj;
   }
 
   registerOnChange(fn: any): void {
     this.propagateChange = fn;
   }
   registerOnTouched(fn : any) {
     this.propogateTouch = fn;
   }
   setDisabledState?(isDisabled: boolean): void {}
   //End implementation of ControlValueAccessor
}

This little piece here is where the secret sauce is :

private textChanged = new Subject();

this.textChanged.pipe(debounceTime(this.delayTime)).subscribe(() => {
  this.propogateTouch();
  this.propagateChange(this.currentText);
});

What this says is as events are pumped into the textChanged subject, “debounce” them. That is, wait until there is a gap of 250ms, then return me the *last* item. If there are multiple events pumped into the subject during this time, still I only want the last one returned to me.

In our view for our custom component, we will have just the following :

<input type="text"
        name="delayTextbox"
        [(ngModel)]="currentText"
        (ngModelChange)="onTextInput()" />

So nothing special, but in particular notice that when the model is changed, onTextInput() is called. This then enqueues a message onto our subject, which then itself debounces for 250ms, and then finally we propogate the change!

We can use this delayed textbox like so :

<app-delayed-textbox 
  name="myTextbox" 
  [(ngModel)]="myModel.Field" 
  (ngModelChange)="onMyModelFieldChanged()">
</app-delayed-textbox >

Notice how because we set up the ControlValueAccessor interface on our custom component, we can simply use the standard ngModel and ngModelChange to be alerted when the field changes.

I’ve seen some really crazy implementations of these sorts of delayed textboxes where people try and time keydowns and keyups, and just a crazy combination of setTimeout and booleans etc. But RxJS essentially does the same thing for us, using the simply debounceTime operator!

It’s pretty common in an Angular project to add the ability to “listen” for router events. That is, know that a user is about to (or already has) navigated to a new page. The most common reason is to push pageview events to analytics platforms (think Google Analytics or Azure Application Insights), but I’ve also used them to hook into whether a user is navigating away from a page (And stop them from losing their work), or to hide toaster/error messages if a user has moved away from a particular page.

In anycase, it’s actually fairly trivial to subscribe to router events.

The first thing to do is to inject the Router object into your service/component like so :

constructor(private router: Router)

Then to subscribe, we simply subscribe to a filtered list of navigation events like so :

this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe((event: NavigationEnd) => {
  //Do something with the NavigationEnd event object.
});

Notice that we are filtering only on NavigationEnd events, this is because the events stream from the router is *all* Router events. The main events you want to subscribe to are listed below (Although there are a couple more, I personally never really use them).

  • NavigationStart – Triggered when a navigation starts
  • NavigationEnd – Triggered when navigation ends successfully
  • NavigationError – Triggered when navigation ends with an error
  • NavigationCancel – Triggered when a navigation is cancelled (Often because a router guard returned false)
  • GuardsCheckStart – Triggered before guards are run as part of the routing
  • GuardsCheckEnd – Triggered at the end of guards running as part of the routing

The most important ones I’ve used are NavigationStart, to determine if a user is about to navigate away, and NavigationEnd when a user has successfully browsed to a new page. It’s also important to keep in mind NavigationError, as NavigationEnd will only trigger if a user was successfully able to visit a page, but you may be missing analytics of failed pages if you ignore NavigationError.

And that’s it! Now you can subscribe to all sorts of different router events with just a couple of lines of code!

Some forms require the ability to “clear” all input and “reset” the form. I struggled through a few different ways of clearing a form before I found a foolproof way that seemed to work in all scenarios I tested.

First, what didn’t work. NGForm does have two reset methods :

this.myForm.reset();

And

this.myForm.resetForm();

Both of these “appeared” to work, but I found it completely broke data binding at times where values got reset to null and lost their data binding. I personally use Template Driven Forms rather than Reactive Forms, so it may work in different scenarios, but for me, this really broke data binding.

If you use Template Driven Forms like me, then the best way I found to actually wipe values in the form was to first reset the underlying two way data binding object. E.g. If I’m binding to a “Person” object then I first do new Person() etc.

Then I can run the following code which only seems to reset the validity of controls rather than fiddling with the underlying data:

Object.keys(this.myForm.controls).forEach((key) => {
    const control = this.myForm.controls[key];
    control.markAsPristine();
    control.markAsUntouched();
});

This runs through every control and resets the validity without affecting binding. This seemed to work the best for me. An alternative I found would be to change the call to setErrors()

Object.keys(this.myForm.controls).forEach((key) => {
    const control = this.myForm.controls[key];
    control.setErrors(null);
});

Again, the first iteration worked for me, but try both if it doesn’t for you.

Adding An NGForm Extension Method

Now rather than copy and pasting this everywhere, instead I created an extension. I created a file called ng-form.extensions.ts with the following content :

import { NgForm } from '@angular/forms';

declare module "@angular/forms/" {
    interface NgForm {
      resetValidation(this:Form) : void;
    }
}

NgForm.prototype.resetValidation = function(this:NgForm) {
    Object.keys(this.controls).forEach((key) => {
        const control = this.controls[key];
        control.markAsPristine();
        control.markAsUntouched();
    });
}

Then in my component, I have to import this extension file like so :

import 'src/app/_core/extensions/ng-form.extensions'

And I then simply reset the form like so :

this.myForm.resetValidation();

Much cleaner!

One of the more frustrating limitations of vanilla JavaScript is that there is actually no GUID or UUID generator in the language. That’s right, out of the box, you cannot generate a GUID with a nice single line of code! Very annoying.

Luckily, there are two ways that I’ve used in the past to generate GUID or GUID-like strings.

This first piece of code is one I use in places where the randomness isn’t such a big deal. It might be just generating correlation ids, or just as a way to generate some randomized letters/numbers, but not global uniqueness and/or collisions aren’t such a big deal.

generateGuid() : string {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random() * 16 | 0,
      v = c == 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}

In most cases where I’m generating a GUID on the front end, I’m not worried too much about how secure things are. You shouldn’t be too concerned on collisions in the browser anyway because a user could just as easily inject JavaScript and/or modify an API call that uses the GUID into using their own string.

However, for a more secure GUID, there is obviously an NPM package for that!

npm install uuid

Then use the uuidv4 function to generate guids as you need them!

import { v4 as uuidv4 } from 'uuid';
let myGuid = uuidv4();

Again, in most cases I just use the first function. But I know people come up with all sorts of links on how “Math.random()” isn’t truly random etc. So both options are there!

An interesting question came up recently about the difference between the “optional” flag in Typescript, and setting something as null. More importantly, it was a question of whether the following were synonymous :

export class MyClass {
  myProperty? : string;
}
export class MyClass {
  myProperty : string | null;
}

The answer is no, these are not the same thing, but it took some explaining to do to people from backgrounds that do not have a “optional” flag, and instead just use a nullable type. For instance, in C#, we may do something like so :

class MyClass 
{
    public string? MyProperty { get; set;}
}

This tells the compiler/runtime that MyProperty is nullable, and by default, will have a null value if no value is given. However in Typescript, the question mark operator is *not* a nullable operator, it instead denotes whether something is “optional”. And there’s an important difference.

export class MyClass {
  myProperty? : string;
}

This says that the MyProperty is optional, and if no value is given, that the value will be set to “undefined”. It’s an important distinction because it’s not a “default” value, it’s “no value”. This is important because if you initialize the class like so :

let class : MyClass = { };

No error will be thrown because the property is optional.

When you instead do something like so :

export class MyClass {
  myProperty : string | null;
}

And then we initialize it the same way :

let class : MyClass = { };

We get the following error :

Property 'myProperty' is missing in type '{}' but required in type 'MyClass'

This is because while we are allowing null to be set as the value, it does not change whether a value needs to be set or not. It’s not optional. Therefore, if we use the discriminated union, we must pass in null as the initial value like so :

let class : MyClass = { myProperty : null };

Again, the most important thing about all of this is divorcing the idea that the question mark operator works the same as it does in C#. It is not a signifier of whether something can be null, it is instead used to mark something as optional. And that is an important difference.

When binding a dropdown/select list in Angular, you will come across two different ways of databinding, using [value] and using [ngValue]. They actually have a very simple difference. But let’s whip up an example first.

Imagine I have a component that holds a list of books :

export class AppComponent  {
  selectedBook : any;
  myBooks = [
    { id : 1, title : "War and Peace", author : "Leo Tolstoy" }, 
    { id : 2, title : "The Lord of the Rings", author : "J. R. R. Tolkien" }, 
    { id : 3, title : "Pride and Prejudice", author : "Jane Austen" }, 
  ];
}

Then in my view I have nothing but a dropdown, and I’m binding to the myBooks list. Also note that I’m using ngModel on the actual select to capture what is selected. And in this example, we are using the [value] attribute.

<select [(ngModel)]="selectedBook">
    <option *ngFor="let book of myBooks" [value]="book.id">{{book.title}}</option>
</select>
<br /><br />
The selected book is : {{selectedBook | json}}

At the end, I’m outputting my selected book in JSON format, just so we have some feedback on what we are selecting.

When I run this, everything works. When I select a book, I see the following :

Notice one thing however. Our “2” which is the id of our book is wrapped in quotes. This is the first thing you’ll notice about using [value]. [value] always binds as a string. Even if you pass it a number type, it will be coerced into a string.

You may not think this is a big deal at first. But if you are fully immersed in Typescripts strict typing, then this can cause unintended consequences because you may end up comparing strings and numbers (Or really any other type) when you never intended to.

That’s where [ngValue] comes in. Let’s change our binding to use [ngValue].

<select [(ngModel)]="selectedBook">
    <option *ngFor="let book of myBooks" [ngValue]="book.id">{{book.title}}</option>
</select>
<br /><br />
The selected book is : {{selectedBook | json}}

Now when we view our example in the browser :

No more quotes! This is because [ngValue] will bind any type and never try and convert things to strings.

Now obviously we are binding the id in our example. But often what happens is that you will require additional info about that item. So you end up querying the initial list to get say the title of the book. Ugh!

But remember, [ngValue] can bind keeping the original type in tact. So we can simply change our binding to be the entire book object.

<select [(ngModel)]="selectedBook">
    <option *ngFor="let book of myBooks" [ngValue]="book">{{book.title}}</option>
</select>
<br /><br />
The selected book is : {{selectedBook | json}}

Now, instead of just returning our id. Our selectedBook is the entire book object!

One final piece of the puzzle is that [value] will output HTML to the browser with the value intact like so :

<option value="1">War and Peace</option>

However if you use [ngValue], you instead get :

<option value="0: Object">War and Peace</option>

Because the binding is on an object. While this may not seem like a big deal (Who’s reading your HTML anyway?), it may be an issue if you use a browser automation tool such as selenium or cypress that is required to read the value of each option. So just keep that in mind!

In Summary :

  • Use [value] when you are binding to a string value and only a string value. Or you need access to the option value inside the HTML for automation purposes
  • Use [ngValue] when you are binding to any type and/or you want the entire option bound instead of just a string

I came across an interesting problem recently where I had to check if a date was before or after today. Getting todays date seemed trivial :

let today = new Date();

But in my test cases, things were blowing up, often at weird times of the day. And it occurred to me that I wasn’t just comparing dates, I was comparing the times on those date objects too!

Such is Javascript, there isn’t actually a way to ask a date object for *only* the date portion for comparisons sake, it always has a time component attached. So for example if I have something like so :

let someDate = someService.getTheDate();// Returns Sat Jan 16 2021 08:30:00
let today = new Date();// Returns Sat Jan 16 2021 9:30:00
let isSameDay = today == someDate; // False!

It’s comparing the time as well as the date!

A very very common pitfall is assuming that the method .getDate() on a Date object will return the date.. Well.. It does, but only the day of the month. So in our example, it will return 16 as in the 16th of Jan. But obviously if you are comparing dates across different months, this isn’t going to work!

So how can we compare just the dates? There is some.. shall we say… interesting fixes out there. One that seems the most prominent is to zero out the time portion :

let today = new Date();// Returns Sat Jan 16 2021 9:30:00
today.setHours(0, 0, 0, 0); // Today is now Sat Jan 16 2021 0:00:00

But the issue with this method is that your date object now has a zero’d out time so if you want to use it elsewhere, you should first clone the date object, then zero it out, then compare it. Something like so :

let today = new Date();// Returns Sat Jan 16 2021 9:30:00
let clonedDate = new Date(today);
clonedDate.setHours(0, 0, 0, 0); // Today is now Sat Jan 16 2021 0:00:00
console.log(today);// Still returns Sat Jan 16 2021 9:30:00

However, this setHours method didn’t sit well with me. It was somewhat ambiguous what it was doing, even with the cloning. So my prefered approach was actually to create a new date object using only the parts I cared about.

let today = new Date();// Returns Sat Jan 16 2021 9:30:00
let clonedDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());

Then I can compare clonedDate to another date that we create using only the date portion, and the comparison works perfect!

One of the most infuriating parts of a new project setup is the npm install. A new Angular project from the CLI (Running “ng new”) can even take several minutes. And even if it’s an existing project that you know you have already setup, but you just want to check, a simple npm install can still take 30 seconds just to say “Yep all good here!”.

An option you see get thrown around often is to switch to using Yarn. Using Yarn from the command line is very similar to NPM (Infact we have a NPM vs Yarn Cheatsheet right here!), but what you’ll quickly find is that Yarn is so much more performant. The biggest reason is that Yarn has an offline global cache on your computer so repeated installs of the same package (For example, creating new Angular projects over and over again), will simply utilize the cached version rather than downloading the package all over again over the internet. Sounds good to me!

Adding Yarn after the fact to an existing project may be relatively straight forward. But what we are actually looking for is the ability to use Yarn with the actual internal angular cli when it tries to install a package.

Using Yarn Globally

If you want to use Yarn globally for *all* Angular projects on your machine, then all you need to do is run :

ng config -g cli.packageManager yarn

This updates the .angular-config.json inside C:\Users\YourUser on Windows to look like so :

{
  "version": 1,
  "cli": {
    "analytics": false,
    "packageManager": "yarn"
  }
}

Using Yarn On A Single Project

Sometimes you don’t want to globally use Yarn, and you just want to set it up on a new project you are about to create. This can be a bit of a headache as the initial call to ng new is the the most painful part of the entire process as it will use npm to install all the base packages, even if once the project is setup you switch to Yarn.

But we have a bit of a cheatcode to get around it!

When creating our project we can run :

ng new --skip-install

Where the skip install flag will actually skip over the final npm install after creating the project. From there, we can run the following command *from inside the project* :

ng config cli.packageManager yarn

Notice how we don’t have the -g flag like before. This sets up yarn as the package manager for this project only. From there, we just run

yarn

And the initial install of Angular components will happen like they normally would on an ng new command, but using Yarn instead.

While many API’s allow you to upload files via multi-part POST forms, others prefer to stick with a pure JSON approach and accept uploads in base64 format allowing you to always be sending and receiving JSON. This means that if a user selects a file in your Angular app, you need to be able to convert that file to Base64 before sending it on.

I thought this would be a rather trivial task but there seems to be some really complicated ways to do what seemed simple at first. Many examples tried to use async/await and promises, but Angular really prefers observables. Ontop of that, people were using an API that was not actually about returning base64 strings, but returning DataURI’s which are very close, but some browsers return the result as data:base64here rather than just the base64.

Anyway! The actual code looks like this. In my view I have :

<input type="file" (change)="onFileSelected($event)" />
<br /><br />
base64 Output : <br />
{{base64Output}}

And in my code behind I have :

export class AppComponent  {
  base64Output : string;

  onFileSelected(event) {
    this.convertFile(event.target.files[0]).subscribe(base64 => {
      this.base64Output = base64;
    });
  }

  convertFile(file : File) : Observable<string> {
    const result = new ReplaySubject<string>(1);
    const reader = new FileReader();
    reader.readAsBinaryString(file);
    reader.onload = (event) => result.next(btoa(event.target.result.toString()));
    return result;
  }
}

Notice a few things about the code.

  1. FileReader is event driven. e.g. You can’t say “Load this file” and then immediately get the result. This means that we either have to use promises or observables, because we are using Angular, let’s use observables.
  2. We use a ReplaySubject because we only need to calculate the file once. e.g. If we subscribed multiple times to the same return subject of this method, it doesn’t need to recalculate the base64 since it’s deterministic (The same file is always the same Base64 string).
  3. I saw some guides saying to use reader.readAsDataURL(file), but again this had weird issues in some browsers. It’s better to load the file as per normal, then use the btoa function to actually return Base64 without any other gunk in the response.

Hopefully that makes sense!