{"id":8042,"date":"2021-03-24T06:30:00","date_gmt":"2021-03-24T11:30:00","guid":{"rendered":"https:\/\/upmostly.com\/?p=8042"},"modified":"2022-11-01T09:49:05","modified_gmt":"2022-11-01T14:49:05","slug":"async-validators-in-angular","status":"publish","type":"post","link":"http:\/\/upmostly.com\/angular\/async-validators-in-angular","title":{"rendered":"Async Validators In Angular"},"content":{"rendered":"\n<p>Creating custom validators in Angular isn\u2019t anything new for me, I must have created dozens of them over the years, but strangely enough I haven\u2019t had to create \u201casync\u201d 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\u2019t a heck of a lot of information out there. Or more so, it was spread out in piecemeal code slices.<\/p>\n\n\n\n<p>This post should hopefully group some of the core concepts together, specifically, at the end I will show how to create a \u201cdebounce\u201d validator that only runs when a user stops typing, as this for me was the hardest thing to find examples of!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Basic Async Validator In Angular<\/h3>\n\n\n\n<p>For the purposes of this article, I\u2019m going to write an async validator that calls an API to check if a username is currently in use or not. Imagine I\u2019m using this on a sign up form to make sure that no two users pick the same username.<\/p>\n\n\n\n<p>The full code is actually quite simple and looks like so :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"jsx\" class=\"language-jsx\">import { Directive,  forwardRef} from '@angular\/core';\nimport { AbstractControl, AsyncValidator, NG_ASYNC_VALIDATORS, ValidationErrors } from '@angular\/forms\/';\nimport { Observable } from 'rxjs';\nimport { map } from 'rxjs\/operators';\nimport { AccountService } from '..\/services\/account.service';\n\n@Directive({\n    selector: '[usernameCheck]', \n    providers: [\n        {\n            provide: NG_ASYNC_VALIDATORS, \n            useExisting: forwardRef(() =&gt; UsernameCheckDirective), \n            multi: true\n      }\n      ]\n  })\n  export class UsernameCheckDirective implements AsyncValidator {\n    constructor(private accountService : AccountService) {\n    }\n  \n    validate(control: AbstractControl): Observable&lt;ValidationErrors | null&gt; {\n        return this.accountService.exists(control.value).pipe(map(x =&gt; x.exists ? { exists : true} : null))\n    }\n}<\/code><\/pre>\n\n\n\n<p>There are a couple of things to point out. First notice that my provider name is NG_ASYNC_VALIDATORS and&nbsp;<strong>*not*<\/strong>&nbsp;NG_VALIDATORS. This is&nbsp;<em>so<\/em>&nbsp;important because Angular won\u2019t 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).<\/p>\n\n\n\n<p>Next notice that my validator method returns an observable. For this, I call my AccountService which has a method called \u201cExists\u201d. This returns an object with a boolean property called \u201cexists\u201d. If true, it means the username is taken and I should return an object like so<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"jsx\" class=\"language-jsx\">{ exists : true}<\/code><\/pre>\n\n\n\n<p>Otherwise, I should return null.<\/p>\n\n\n\n<p>Easy right?<\/p>\n\n\n\n<p>When I write HTML to use this, I can just do the following :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"jsx\" class=\"language-jsx\">&lt;input name=\"username\" \n    #username=\"ngModel\" \n    type=\"text\" \n    class=\"form-control\" \n    [(ngModel)]=\"createAccount.name\" \n    [usernameCheck]\n    required \/&gt;\n&lt;div class=\"validation-message\" *ngIf=\"username?.errors?.exists\"&gt;\n    Username \"{{createAccount.name}}\" is already in use\n&lt;\/div&gt;<\/code><\/pre>\n\n\n\n<p>Nothing too special and it works perfectly fine!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Ignoring Empty Values<\/h3>\n\n\n\n<p>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\u2019ve 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!<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"jsx\" class=\"language-jsx\">validate(control: AbstractControl): Observable&lt;ValidationErrors | null&gt; {\n    if(!control.value) {\n        return of(null);\n    }\n    return this.accountService.exists(control.value).pipe(map(x =&gt; x.exists ? { exists : true} : null))\n}<\/code><\/pre>\n\n\n\n<p>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.<\/p>\n\n\n\n<p>But then there was another issue\u2026.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Debouncing The Validation<\/h3>\n\n\n\n<p>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\u2019s 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 \u201cdebouncing\u201d.<\/p>\n\n\n\n<p>Now I had worked with debounced textboxes before, infact, here\u2019s an article I wrote on that very subject :&nbsp;<a href=\"http:\/\/angulartut.onpressidium.com\/2021\/03\/17\/creating-a-textbox-with-delayed-output-in-angular\/\" target=\"_blank\" rel=\"noreferrer noopener\">http:\/\/angulartut.onpressidium.com\/2021\/03\/17\/creating-a-textbox-with-delayed-output-in-angular\/<\/a>! But the issue was that how could I debounce from within a validator?<\/p>\n\n\n\n<p>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 :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"jsx\" class=\"language-jsx\">&lt;input [(ngModel)]=\"name\" [ngModelOptions]=\"{updateOn: 'blur'}\"&gt;<\/code><\/pre>\n\n\n\n<p>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.<\/p>\n\n\n\n<p>Then I read about an interesting \u201cquirk\u201d within Angular\u2019s 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\u2019t 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!<\/p>\n\n\n\n<p>Here\u2019s our new async validate method :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"jsx\" class=\"language-jsx\">validate(control: AbstractControl): Observable&lt;ValidationErrors | null&gt; {\n    if(!control.value) {\n        return of(null);\n    }\n\n    return of(control.value)\n          .pipe(\n              delay(250), \n              \/\/Then instead return the observable of us actually checking the value. \n              switchMap((value) =&gt; \n                  this.accountService.exists(value).pipe(map(x =&gt; {\n                      return x.exists ? {exists : true} : null;\n                  }))\n              )\n          );\n}<\/code><\/pre>\n\n\n\n<p>Here\u2019s how it works. When the validator is kicked off, the first thing it does is it waits 250ms. That may seem dumb but there\u2019s 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!<\/p>\n\n\n\n<p>I\u2019ve 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\u2019t trying to work around Angular\u2019s limitations, but instead work with it!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Creating custom validators in Angular isn\u2019t anything new for me, I must have created dozens of them over the years, but strangely enough I haven\u2019t had to create \u201casync\u201d 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\u2019t [&hellip;]<\/p>\n","protected":false},"author":131,"featured_media":8045,"comment_status":"open","ping_status":"open","sticky":false,"template":"post-tutorial-new.php","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[150],"tags":[],"class_list":["post-8042","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-angular"],"acf":[],"_links":{"self":[{"href":"https:\/\/upmostly.com\/wp-json\/wp\/v2\/posts\/8042","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/upmostly.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/upmostly.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/upmostly.com\/wp-json\/wp\/v2\/users\/131"}],"replies":[{"embeddable":true,"href":"https:\/\/upmostly.com\/wp-json\/wp\/v2\/comments?post=8042"}],"version-history":[{"count":3,"href":"https:\/\/upmostly.com\/wp-json\/wp\/v2\/posts\/8042\/revisions"}],"predecessor-version":[{"id":13416,"href":"https:\/\/upmostly.com\/wp-json\/wp\/v2\/posts\/8042\/revisions\/13416"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/upmostly.com\/wp-json\/wp\/v2\/media\/8045"}],"wp:attachment":[{"href":"https:\/\/upmostly.com\/wp-json\/wp\/v2\/media?parent=8042"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/upmostly.com\/wp-json\/wp\/v2\/categories?post=8042"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/upmostly.com\/wp-json\/wp\/v2\/tags?post=8042"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}