Back in the before times, it wasn't such an uncommon thing to hack up an html INPUT to include "extra" attributes like 'errorMsg="Enter a number between 1 and 12" minVal="1" maxVal="12"' to aid in the client side validation of an input. Of course in the modern day web, this practice is well, let's just say undesirable. The most common alternative is to create a bunch of dynamically generated "onblur" and separate on "onsubmit" function calls.
Things like 'onblur="checkRange(this, 1, 52, 'Enter a valid Week Number');"', combined with "validateSubmit( … code … if(!checkRangeS($get('MyWeekTextBox'),1,15,' Enter a valid Week Number')){ return false; } …more code …);". The basic issue with this approach is there tends to be some duplicated code. On the other hand things like the errorText can be pushed into a "hash" table and accessed as a resource. There are other things that can be done to make this a cleaner approach, but to me they all fall short of using the object like structures of JavaScript.
What if you create a validation object, attach it to a property on the input, then wire some event handlers, you can define something from any point in the page's client lifecycle that has that write-once, use and re-use goodness of object oriented development. There are several considerations with this approach, the first of which is that depending on your approach you can introduce a fair amount of runtime JavaScript bloat. On the other hand if you really take some time to understand what you're getting into you can create a clean, reusable lightweight validation framework.
Before I go any farther, let me state in the most direct of language, that Client Side validation is only a usability enhancement for a web application, It has absolutely nothing to do with insuring valid data for processing. It cannot ever be trusted as a reasonable source of validation for any serious data capture system. Sure you can get pretty clever with your client side checks, but remember especially with some of the client JavaScript debugging tools out there, anything you do can be bypassed. So why even do it? Well the vast majority of users will benefit from the aid that rapid client side intervention on erroneous data entry provides. Range, type or required field validations are very helpful to your average user.
Okay enough tongue wagging. Let's take a look at some functions:
The numeric validator – In this function we are both, storing the min, max, required, and error message as well as providing a method to initiate the validation. One note, the Validate function takes an obj parameter, and truthfully this is not really necessary, because this validator will be assigned to an input, it can carry a reference to the input it is validating and do away with that parameter. I'm not a fan of that, I liken to say a Domain with Person and Car. A Person may have 1-n Cars, and a Person may Sell a Car, but a Car cannot sell itself from a Person. Even though the data defines the relationship as 2 way, there is a clear owner and owned that define who in Code should really know about the other. In effect Person may have a Collection of Cars, but a Car might only have a PersonID, a pointer, but not a reference to its owner. When I thought about this object based validation this seems like a good approach.
function numVdtrSet(req, min, max, msg){
this.Req = req;
this.Min = min;
this.Max = max;
this.Msg = msg;
this.Error_handler = null;
this.Value = null;
this.Validate = function(obj){
this.Value = obj.value;
if (!validateNumberField(this)){
obj.error = true;
return false;
}
obj.error = false;
return true;
}
}
The validator creation and assignment – Here we're creating the validator, and if an event is provided wiring it up. In our case the we will be passing in an iEvt of onblur for the textbox, and an evt of focus to pull the cursor back. Critical to this is tracking the error state of the input, if we've flagged it as bad, let's not keep pulling them back in an endless loop. We have a submit check, and remember this is only meant to be helpful, not strict enforcement.
function setHandler(obj, iEvt, req, min, max, msg, evt){
obj.error = false;
obj.validator = new numVdtrSet(req, min, max, msg);
obj.validator.Error_handler = function(s){
alert("Error: " + s + "\r\n" + this.Msg);
if(evt && !obj.error){
obj[evt]();
}
}
$addHandler(obj, iEvt, doValidation);
}
The validator – nothing special here, it's not the best validation check, but this is part that should be most familiar if you've ever done client side validation.
function validateNumberField(vldtr){
var _iVal = null;
if (vldtr.Req || vldtr.Value != null){
try {
_iVal = parseInt(vldtr.Value)
if (isNaN(_iVal)){
processError("type", vldtr);
return false;
}
} catch(e){
processError("type", vldtr);
return false;
}
if (vldtr.Min && (vldtr.Min > _iVal)){
processError("minimum", vldtr);
return false;
}
if (vldtr.Max && (vldtr.Max < _iVal)){
processError("maximum", vldtr);
return false;
}
}
return true;
}
The error handler – if ones provided call out to it.
function processError(errType, vldtr){
if (vldtr.Error_handler){
vldtr.Error_handler(errType);
}
}
The triggers – what fires these validations.
function doValidation(){
if (this.validator){
this.validator.Validate(this);
}
}
function onSubmit(){
var inps = document.forms[0].getElementsByTagName("input");
for(var i=0;i<inps.length;i++){
if (inps[i].validator){
if (!inps[i].validator.Validate(inps[i])){
return false;
}
}
}
return true;
}
The markup – actual usage
Enter a score between 50 and 3000:<input type="text" id="score" name="box" /><br />
Enter a number between 5 and 15:<input type="text" id="num" name="box" /><br />
<asp:Button ID="FakeSubmit" runat="server" Text="FakeSubmit" OnClientClick="return onSubmit();" />
<script type="text/javascript">
function setValidations(){
setHandler(document.getElementById('score'), 'blur', true, 50, 3000, 'Enter a valid score between 50 and 3000', 'focus');
setHandler(document.getElementById('num'), 'blur', true, 5, 15, 'Enter a valid number between 5 and 15', 'focus');
}
window.onload=setValidations;
</script>
I mentioned using this pattern in front of a WebMethod. That's still my intent, but this ended up being a lot to post, so we'll have to cover that in a subsequent post. This isn't a fully polished validator, rather just a pattern for perusal. I like the idea of working a more formal typed/oop process, even if JavaScript doesn't really enforce it.