Web UI validation (December 2014 edition) – Part II

I wrote part of this a few days ago. I was thinking, foolishly, that I was pretty-much done! However, I ended spending a LOT of time on figuring out the next kind of validation: custom validation.

How do you do your own validation, and TELL (not ask) HTML5 that a field failed validation?

Well, that was a few frustrating days ago. I tried many things. Ultimately, I ran across this blog post (here) – which doesn’t work by the way. However, it was barking up the right tree. What I needed was an AngularJS “directive”, and I needed to use that to tell HTML5 that our validation failed.

What is an AngularJS directive?
A directive is an attribute that you can add on to an HTML element, and have it run your custom code. Here is the page on it, and it’s actually pretty helpful:

https://docs.angularjs.org/guide/directive

The HTML – showing more detail in the error:
You might recall that where we left off, we had some basic validation. Since then, I also learned that if you use a “title” attribute, you can customize the message for the “pattern” validation:

<input type="password" placeholder="••••••••••••••••" class="form-control"
        ng-model="model.Password" id="password1" name="password1" pattern="^(?=.*d).{4,50}$" required 
        title="Password should be 4 to 50 characters long, and include a number." />

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

First, note how the “required” attribute shows a good error in the UI (in Chrome, Firefox, and IE – respectively):

image imageimage

Next, note the “title” attribute is used once the next validator starts – again, in Chrome, Firefox, and IE:

image image image

So, instead of just showing “Please match the requested format”, which is not clear – you can add a “title” attribute to give more details about what the validation problem was.

The HTML – the custom validation directive:
OK, now that we have pretty error messages, let’s wire up our directive. This is how we want it to work, ultimately:

<input type="password" placeholder="••••••••••••••••" class="form-control"
        ng-model="model.PasswordConfirm" id="password2" name="password2" required
        custom-match="model.Password"
        title="Needs to match the first password." />

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Notice the “custom-match”. Now, per the AngularJS website best-practices, this should NOT have an ng prefix, because it might conflict with future versions. Ideally, this should be a prefix for something meaningful. For me, this would probably be my company name. For this example though, “custom” is effective too because it’s very clear to the developer that this is not built-in to AngularJS and that they should go look for code for this thing!

The Directive:
OK, the UI is ready to go, now we need to do the hard part, write a directive. Again, I started from the code I found on this site – however, it simply didn’t work. I wouldn’t get errors, but it also didn’t tell HTML5 that the validation failed. On that web page, I think they were doing their own angular/bootstrap hybrid validation maybe? I don’t know, I couldn’t get it working.

I was really missing one piece – which is what took me a couple days to discover. I needed to find a way to tell HTML5 that the validation failed. If you are already familiar with this, you might say: “No doy, Rob! Just use .setCustomValidity(..), dum-dum!”. I say to you: “yes, but where and how!”

I tried doing this with JavaScript events (like oninput), through JavaScript code, and on ANgularJS events like ng-change, etc, etc. I couldn’t quite find the right combination. Do you set that validity on the ng-model? Or on the <input control? I just couldn’t get it working.

Well – it finally dawned on me that maybe the answer is in that non-working directive code? I thought “according to the internet, I need to setValidity on the HTML element” – well, there is a poorly named “elem”, maybe that is element? It was! OK, but that didn’t work. Next, I debugged during that code execution and eventually found that elem.context is a handle to the actual UI control!

I immediately cleaned up the horrible names in the directive and continued.

OK, I actually got this to work. So, here is what the directive looks like:

(function() {
    'use strict';

    var app = angular.module("app");
    var directiveId = "customMatch";

    var customMatchDirective = function($parse) {
        var directive = {
            link: link,
            restrict: 'A',
            require: '?ngModel'
        };
        return directive;

        function link(scope, element, attributes, controllers) {

            if (!controllers) return;
            if (!attributes[directiveId]) return;

            var firstPassword = $parse(attributes[directiveId]);

            var validator = function(secondPasswordText) {
                var firstPasswordText = firstPassword(scope),
                    isMatch = secondPasswordText === firstPasswordText;

                controllers.$setValidity('match', isMatch);
                if (isMatch)
                    element.context.setCustomValidity("");
                else
                    element.context.setCustomValidity("Passwords must match.");

                return secondPasswordText;
            }

            controllers.$parsers.unshift(validator);
            controllers.$formatters.push(validator);
            attributes.$observe(directiveId, function() {
                validator(controllers.$viewValue);
            });
        };
    };

    app.directive(directiveId, customMatchDirective);

})();

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Note how we check if “isMatch” – and we can tell HTML5 that the validation is successful or not? If you set “”, that “clears” the validation error. OK, also note you have to add that file to your javascript references, and I also now have this for a folder structure:

image

UI check out:
OK, let’s see how it works. When we type in the password field, that is “required” and must adhere to a regular expression. When we type in the 2nd password field, it simply must match the first field. Here’s how that looks in Chrome, Firefox, and IE, respectively:

image image  image

then we do a non-matching password:

image image image

This isn’t a huge deal, but notice how Chrome got it’s error message from the title attribute in HTML, where Firefox and IE got their error message from the directive? Just something to note. And yes, when the passwords match, the validation error goes away and I can submit the form!

image image image

Posted in Computers and Internet, Development Tools, General, JQuery, Mobile, New Technology, Security, Uncategorized

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives
Categories

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 2 other followers

%d bloggers like this: