Advanced Form Validation Made a Little Easier

Jake Howlett, 22 July 2001

Category: JavaScript; Keywords: RegExp regular expression replace match validate

By now there should be very few Domino Developers who haven't, at the very least, tried using JavaScript in their applications. The extent to which people use it tends to vary hugely. There are experts out there and there are people who have reached as far as the line of code necessary to check the value of a field isn't blank.

I can't think of any other language in which the level of knowledge varies so much between users. I have been proclaimed a Guru by somebody I once helped to debug a simple few lines of code. Please, I am not a guru - just somebody who has taken the time to learn JavaScript and the Browser Document Object Model (DOM). The Notes.net Café features numerous comments like I'm trying to use this bit of code but I don't know JavaScript very well or I wrote this JavaScript but I am stuck now because I am a novice. Those that never take the language any further than its basic uses tend to be left with a lot of misconceptions about what JavaScript is and what it can and can't do. This article is not about to describe either of these in any great detail. All I will say is that JavaScript is a lot more than you are probably thinking and well worth taking time to get to know in depth.

If the above applies to you then it is probably better to carry on learning it a little more instead of reading this article.

So you know JavaScript then do you? Do you know about Regular Expressions? You may have heard of them already if you have developed in other languages such as Perl or AWK. If you haven't then you are missing out on a very powerful feature that could save you lines and lines of code and hours of effort.

What are Regular Expressions:

Basically, a Regular Expression (RegExp) is used to describe a pattern that can then be used to test the format of a string. Its main use to us as Domino Developers is in our form level data validation. We can use RegExps to easily check for certain patterns in the data and alert the user when it is badly formatted. We could probably do the same thing without RegExps but, trust me, they are a lot simpler to code.

There are a myriad of sites out there covering RegExps in more detail than this article could ever start to touch upon. For that reason I shall crack on with a few simple examples, an overview of the characters we need to know about and then discuss how we can use them in our Domino Forms. There is a list of a few of the better of the sites at the bottom.

A quick example:

Let's say we have a field in which we want to check the user has entered a sixteen digit number. How might an inexperienced developer write the validation function? Maybe something like this:

function isNumberSixteenDigitsLong( val ){
//check there is a value and it's the right size
if ( val == "" || val.length != 16 ){
return false;
}

//check each digit is a number
for ( var i = 1; i < val.length; i++ ){
if ( '0123456789'.indexOf(val.charAt( i )) == -1 ){
return false;
}
}

//everything is OK
return true;
}

This works well enough but consider the same function if we were to use a Regular Expression:

function isNumberSixteenDigitsLong( val ){
return /^\d{16}$/.test( val );
}

Hence we have a one line function rather than a nine line function. That has to be worth learning this for alone. Don't start worrying just yet about what the funny looking /^\d{16}$/ part is. All will come clear very soon.

How do I write a Regular Expression:

First thing we do is declare it as a variable. Its value is always inside the / and / limiters and followed by any of the optional parameters that we shall cover later. You can declare the variable like the two examples below or in the manner I used in the above example.

var re = /codestore/

or

var re = new RegExp('codestore', 'i')

This would be used to match the word "codestore" anywhere within a string. This is just a simple example of RegExp declaration. Not very useful in itself as we can easily use the string object's indexOf() method instead.

OK, let's get the boring bits out of the way. Firstly, Table 1. This shows us the most common of the special characters we have available to use, such as the \d (used to match numeric digits) that we used above. The special characters are placed in the pattern at the point at which you want to test for the string that they represent.

Table 1. Special Characters
Notation Matches Example
\b Word boundary /\bDo/ matches Domino\no\b\ matches Domino
\B Non word boundary /\Bot/ matches Notes
\s White space /Lotus\sDomino/ matches Lotus Domino
\S Non white space /Lotus\Somino/ matches LotusDomino
\d Numeric 0 to 9 /\d\d/ matches 55
\D Non Numeric /\D\d/ matches R5
\w Letter, numeral or underscore /\w\w/ matches R5
\W Non letter, numeral or underscore /\Wfile/ matches $file
. Anything but a new line /../ matches R5
^ At the beginning of the string /^Lotus/ matches Lotus Domino
$ At the end of the string /$Domino/ matches Lotus Domino


We use the above operators in combination so as to specify a particular pattern we want to match. The next thing we need to be able to handle is the number of the above elements we are expecting. Table 2 shows the notation to use. These always come directly after the character of the pattern that we are interested in.

Table 2. Multiple Testing
Character Matches Example
* 0 or more /R\d*/ matches "R", "R46" and "R5"
+ 1 or more /R\d+/ matches "R46" and "R5"
? 0 or 1 /R\d?/ matches "R" and "R5"
{n} Exactly n times /R\D{4}/ matches "RNext"
{n,m} Between n and m times /R\d{3,4}/ matches "R507" and "RNext"


All we need to know now is how to group our operators together, how to apply a logic to them and how to match reserved characters. Here are the methods we use:

Table 3. Advanced Techniques
Character Matches Example
[...] Any of the contained characters /T[aio]n/ matches 'Tan', 'Tin' or 'Ton'
[^...] None of the contained characters /T[^aio]n/ matches 'Ten' or 'Tun'
(...) This particular sequence of characters /\D(ab)\d/ matches 'Cab1', 'tab5' or 'Zab9'
\ Escape any of these special character to take them literally:^ . [ $ ( ) | * + ? { \ /4\.6/ matches ?4.6?/\?Open/ matches '?Open' /\[<\d+/ matches '[
| This or that /R(4|5)/ matches R4 and R5
- Between this and this /\d[a-c]/ matches 9a, 9b and 9c


Finally let's talk about the optional parameters available that I mentioned earlier. We can use g when you want to apply a search to the whole string, thus making it Global. If we wanted to replace all occurrences of a string found then we would use this. Without it only the first match is affected. Extra to this we have the i option which tells the process to Ignore Case of the strings being compared. We shall see examples of them in use below. These options always appear after the delimiting / of the RegExp

Some more advanced examples:

Now that we know just about all we need to know about the syntactical behaviour of the RegExp component let's apply it to some examples that become increasingly advanced. I'm assuming that you have read and understood all of the above!? Looking at each one in turn try to understand how each one works.



The examples could go on and on. Don't worry though I'm not going to. The web links at the bottom include numerous examples and should cover just about everything you ever wanted to do.

Applying all of this to Domino:

The fact that you are reading this leads me to presume that you're a Domino Developer. Even if you aren't the majority of the following remains applicable to anybody who is involved in any kind of data capture using forms on the Internet. There are few places in Domino other than the form that Regular Expressions are of any use at all, so I shall focus on them.

Using Domino R5 we can easily add all the validation functions we need in to the form's JS Header component. The advantage of this being that we can debug the code as it is written and we keep it all in one place.

image
Figure 1. The JS Header


The function I have added here is what I hope to be an all encompassing one that can be used to do a simple test or to replace all the occurrences in the string and return the new value. Where we call this function from depends on how and when we want the data to be validated. One method is to add it to the field's onChange event, as in figure 2. This way the user's input is corrected as the form is filled out and they are aware of any changes. You could adjust your function to alert them of any changes so that they are aware.

image
Figure 2. Field onChange Event


The other option is to save all the validation until the form is complete and the user hits the submit button. Your function can then cycle through each field one at a time.

Here is the function I am using in my testing form:

function checkValueWithRegExp( e, f, a, r ){

//create the RegExp component
var re = new RegExp( e, 'g' );

switch ( a ) {
//alert the user
case "test":
alert( ( re.test( f.value ) ) ? 'Pattern MATCHED' : 'Pattern FAILED' );
break

//replace all values
case "replace":
rw = ( r == null ) ? prompt('Replace all occurrences with:', '') : r;
f.value = f.value.replace( re, rw );
break

return
}
}

The function gets passed four arguments.
  1. The expression to use in the validation (without the //)
  2. The field whose value we want to validate
  3. The action we want to perform (Test or Replace)
  4. The value to replace any occurrence with
The form is very simple and only has two fields on it. The first is used to enter the expression and the second the string to be validated. There are also two buttons on it; one to "Test" the pattern and one to perform the "Replace". The Test button has no affect on the original string, it simply tells us if it passes or not. Pressing the Replace button replaces all occurrences of the pattern with the specified string (If we didn't specify a value for the fourth parameter and we hit the button we would be prompted for the string to replace matches with).

Regular Expression:
No need for the //s

Validation String:



Obviously this particular example is of no use at all in a real Domino application but does show one way of adding them to a Domino form. If you are serious about learning the technique or about using them often then it is well worthwhile creating your own form so you can create, test and edit them all in one place. How you do this depends on your requirements.

Before I finish let's have a look at one RegExp that may be of use to us all, as Domino Developers. Imagine you don't want your informed users to be able to enter Pass-Thru HTML in to a form. The first thing we want to look for is the opening square bracket followed by the opening angle bracket "[<":

\[<

Now we need to check that this combination is followed immediately by something other than whitespace.

\[<\S+

The best thing to do now is alert the user telling them they are not allowed to do this. Alternatively we could simply replace the open "[<" with "<" like this.

onChange="checkValueWithRegExp( '\[<\S+', this, 'replace', '' );"


Two useful examples:

This function will be useful to Abbreviate a canonical name selected in the browser.

There may not be that many circumstances in which this would be useful but it also acts as a good example of the power of Regular Expressions in place of complicated procedures to find & replace text in a string.

function nameAbbrev(canName) {

if (canName.indexOf("CN=") == 0) {
return canName.replace(/\/C=/, "/").replace(/\/O=/, "/").replace(/\/OU=/g, "/").replace(/^CN=/, "")

} else {
return canName
}

}


This example takes an e-mail address as its argument and should tell you whether or not it's in a volid format (via MSDN):


function isValidEmailAddress( str ) {
var reg1 = /(@.*@)|(\.\.)|(@\.)|(\.@)|(^\.)/; // not valid
var reg2 = /^.+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,3}|[0-9]{1,3})(\]?)$/; // valid
if (!reg1.test(str) && reg2.test(str)) { // if syntax is valid
return true;
}
return false;
}


Conclusion:

For those of you familiar with RegExps who have read this far hopefully you may have picked up something new to use. For those of you previously unaware of this functionality I just hope you are now thinking of all the places you can start to use it and how much easier it is going to make your life. The method almost has no limits in terms of what you can do with it when you are manipulating strings in JavaScript.



Some useful links:

An article on Microsoft's Developer Network

An article on the Netscape Developer site

An article on the WebReference site

An article on the O'Reilly Online site

Aimed at PHP developers but still relevant

A nice article found by Erhardt Nel

Note: I originally wrote this article for the September 2001 edition of Xephon's Domino Update journal.