I use the built-in ASP.net validation controls all the time and they work quite nicely. However, they’re boring. Sure, some CSS formatting could be applied to make it look a little nicer and images could be used for the validation errors, but they still only work a certain way.
The ValidationSummary especially can be problematic when you have a lengthy form that requires scrolling. To make this as user-friendly as possible you more than likely would have to provide two validation summaries…one on the top and bottom of the page. Of course, if a user is midway down the page and hits enter to submit the form, they may or may not see the validation message because frankly, the page doesn’t always scroll like it’s supposed to.
A few months ago I was working on a project with a lot of forms, some simple and some a little more involved. These forms brought me to my tipping point. I couldn’t take the boringness (yes, boringness) any more! Something had to be done.
So, with a little research followed by a little trial and error I was able to make the ASP.net form validation process a little more slick. If you too are fed up with this mundane built-in validation process, here’s what you can do to spice it up a little.
Override the built-in ASP.net Javascript validation functions
When an ASP.net form renders as an HTML page it registers a plethora of javascript files. If you look in the code-behind of the page, inside the <form> tag you will see these resource files referenced like this:
<script src="/WebResource.axd?d=blahblahblah" type="text/javascript"></script>
The nice thing about it is the page only includes resources you need. If you don’t have any form validation controls on the page the resource file will not be included.
If you open the resource file to see the javascript you’ll notice several functions. To really give this validation stuff some real pizzazz, we only need to deal with two of these functions: ValidatorUpdateDisplay and ValidationSummaryOnSubmit.
(I haven’t looked to see if this resource file can be permanently changed so we don’t have to do this for each project. I’m sure it can probably be done but I’ve created a javascript file with these two functions in it and have built it into my workflow to just include them in my master page. I you’ve overhauled your resource file so it’s automatically added be sure to let me know in the comments section.)
Whether you create a .js file for these two functions or if you just want to stick them on the page (that’s a lot of copying and pasting though if you work on multiple sites) they need to go below where the default web resources are referenced. In my Master Pages I always add a ContentPlaceHolder just before the closing </body> tag so that I can add javascript on each page at the bottom.
I reference the .js file directly above this content placeholder, like so (I always put my javascript files in either a js or scripts folder at the root:
<script type="text/javascript" src="js/validators.js"></script> <asp:ContentPlaceHolder runat="Server" id="Scripts"></asp:ContentPlaceHolder>
This will not work if put this reference in the <head> of the page. The web resources are called after this inside the ASP.net form.
ValidatorUpdateDisplay
This function goes through each validation control and checks whether it’s valid or not. By default if there is a validation error, either the Text property of the validator will show if there is not a ValidationSummary on the page or the ErrorMessage property if there is a summary control on the page. (Note: if the Display is set to dynamic the validation control will be an empty <span> tag, if Display is static then the span tag will have the text/error message in the span with a visibility:hidden style.). Here’s the built-in function:
function ValidatorUpdateDisplay(val) { if (typeof(val.display) == "string") { if (val.display == "None") { return; } if (val.display == "Dynamic") { val.style.display = val.isvalid ? "none" : "inline"; return; } } if ((navigator.userAgent.indexOf("Mac") > -1) && (navigator.userAgent.indexOf("MSIE") > -1)) { val.style.display = "inline"; } val.style.visibility = val.isvalid ? "hidden" : "visible"; }
Pretty straightforward.
What I want to do is if a control’s value fails the validation add a “validationError” class to the control (textbox, dropdownlist, etc) to make it stand out a little…like maybe a thick red border and a gray (or grey if you prefer) background. Here’s the CSS class:
/* of course you can get as creative as you want, this is just a simple example */ .validationError { background-color: #d7d7d7; color: #5b5b5b; border: 1px solid #f04138; }
For my first attempt I simply created a var to hold the control and added the validationError class to the control, like so:
function ValidatorUpdateDisplay(val) { if (typeof(val.display) == "string") { if (val.display == "None") { return; } if (val.display == "Dynamic") { val.style.display = val.isvalid ? "none" : "inline"; return; } } if ((navigator.userAgent.indexOf("Mac") > -1) && (navigator.userAgent.indexOf("MSIE") > -1)) { val.style.display = "inline"; } var ctl = $('#' + val.controltovalidate); if (!val.isvalid) { ctl.addClass('validationError'); }else{ ctl.removeClass('validationError'); } val.style.visibility = val.isvalid ? "hidden" : "visible"; }
Like I said, this sort of works. It works if you only have one validation control set to a control. If you have, say, an email that is required and you have both a RegularExpressionValidator and a RequiredFieldValidator then it won’t work. To make a long story short, it all depends on the order you place the validation controls in for this to work. Basically the solution is you have to loop through all the validators for a control to determine whether or not to add the validatonError class. So here’s the final version.
ValidatorUpdateDisplay = function (val) { var ctl = $('#' + val.controltovalidate); var eCount = 0; for (var i = 0; i < Page_Validators.length; i++) { var v = Page_Validators[i]; if (v.controltovalidate == val.controltovalidate) { if (!v.isvalid) { eCount ++; ctl.addClass('validationError'); } }; } if (eCount > 0) { ctl.addClass('validationError'); }else{ ctl.removeClass('validationError'); } if (typeof(val.display) == "string") { if (val.display == "None") { return; } if (val.display == "Dynamic") { val.style.display = val.isvalid ? "none" : "inline"; return; } } if ((navigator.userAgent.indexOf("Mac") > -1) && (navigator.userAgent.indexOf("MSIE") > -1)) { val.style.display = "inline"; } val.style.visibility = val.isvalid ? "hidden" : "visible"; }
The function creates a counter (eCount) to hold the total number of validation errors for the current control. If the error count is greater than 0 the validationError class will be added to the control. If the count equals 0 the error class will be removed.
OK I’ll admit, it’s still rather boring. There is a lot more within this function that can be done to spice this even more though. You could animate the spans (the validation messages), use icons to make the errors more noticeable or do all sorts of other crazy css stuff to the control. I’ll leave the creative stuff you you, but I’d love to see what you came up with. If I think it’s snazzy enough, your example could be highlighted for the world to see.
In part 2 I’ll show you how to take this a step farther on the fancy meter by overriding another function.