Custom form validation in Angular JS

facebooktwittergoogle_pluslinkedin

This article is part of the bigger series about my Software Engineering internship this summer. To read all the posts, simply visit the category archive.

Today just a quick Angular JS gem on how to create your own form validation directives. I’m still working on the follow up post to the article From Request to Response and it will certainly follow soon, but for now, some more Angular JS action.

Form handling in Angular JS

Angular JS already has some basic form validation features and explains them well and in detail in their developer guide. It even shows how to create your own validation rules. I’d just like to provide an example of a common use case. So in short form validation works with a lot of the new HTML 5 elements and attributes (number, email, etc.). In addition to that Angular provides some directives that allow pattern matching for example. This covered almost all use cases I encountered during development.

When a validation fails two things happen:

The constructor object on the current scope (named by the form name) gets some flags set to new values indicating the failure and the reason for the failure. If an input for example isn’t an email address as it’s supposed to be, the following relevant fields get updated (and some additional fields, but mostly I use the following):

  • form.$invalid is set to true
  • form.inputfield.$error.email is set to true

Based on these fields you can then modify the content of the DOM to give the use guidance on how to fix the error and also to disable form submission.

The second thing that happens, might not seem that important: The model does not get updated! So if your input field was bound to a model value (as it is usually the case) the model value is set to undefined. When I first started implemented my own validation I forgot this one and therefore I didn’t get optimal results.

Testing for duplicate values

At several places in my application I want to check client-side if an input value already exists (user names, emails etc.). So as usual when writing your own directives, I started writing the html as I want it to look like in the end:

<input id="username" type="text" name="username" ng-model="user.username" duplicate="existingValues" required>
<span ng-show="createuser.username.$error.required">Required</span>
<span ng-show="createuser.username.$error.duplicate">Username does already exist!</span>

So I’d like to set an additional attribute on my input element an pass along a list of existing values (which I get from the Server). That looks fine so far. Now the JavaScript code for the directive looks as follows:

components.directive('duplicate', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elm, attrs, ctrl) {
            ctrl.$parsers.unshift(function (viewValue) {
                var duplicate = scope[attrs.duplicate];
                if (scope.duplicate.indexOf(viewValue) !== -1) {
                    ctrl.$setValidity('duplicate', false);
                    return undefined;
                } else {
                    ctrl.$setValidity('duplicate', true);
                    return viewValue;
                }
            });
        }
    };
});

components.js

Looks pretty neat, doesn’t it? When the directive is rendered the link function registers a new parser on the controller (at the beginning of the array). The parser gets then called when the value in the view get’s updated and simply checked against the list of duplicates. When it’s already in the list, we set the validity for a new error key on false and that’s all the magic.

Yep, that’s already everything I wanted to share.

Cheers,
Joel

Additional remarks

Just popped into my mind: Of course the above example makes the list of user names visible to the client, which might in a lot of cases not be a smart idea. In my case it doesn’t matter. But of course you can also use the duplicate directive for any other kind of input which is not an issue, when shared with the client.

2 thoughts on “Custom form validation in Angular JS

  1. I know that this post is a little old, but it came in really handy (thanks!) because I have an almost exact scenario at the moment.

    I ran into a little brain-twister though, and wanted to point this out for anyone else that stumbles on this post. It appears as though the order of your validators matters. I had my ‘required’ validator listed before my ‘duplicate’ validator in the input markup, and the result was that I would get both errors if the duplicate error was tripped (because the custom directive returns ‘undefined’, it was tripping the ‘required’ validator).

    Just a little heads-up!

  2. Thanks for sharing Joel, its so helpfull.
    I am currently working on a project thats using firebase and angularjs and i want my application to check if an email already exists everytime a user try to create an account.

Comments are closed.