Skip to page content or Skip to Accesskey List.

Work

Main Page Content

Form Validation An Object Oriented Approach

Rated 3.81 (Ratings: 3)

Want more?

 

Ben Gustafson

Member info

User since: 05 Nov 2001

Articles written: 1

Everyone who develops a form for a website, be it a simple contact form or a more complex form for collecting order information for an e-commerce site, needs to address the issue of validating and handling the data collected from it. There are opinions both for and against validating form data. Let's assume, however, that you've designed your form with international audience in mind in order to cause no headaches to folks outside your country when it's validated. Let's also assume that your boss or client doesn't want a lot of the registrations from their lead-generation website being submitted empty or with "asdf" typed in every element of the form. Similarly, people sometimes mistakenly type their e-mail address in the line for their phone number, for example. And then there's the issue of how to collect the data from the form and process it in a methodical manner.

An object-oriented approach can lend scalability, code reusability and readability to such programming tasks. This article will describe an object-oriented methodology for form data validation and collection using ASP and JavaScript (or JScript, as the folks at Microsoft like to call their implementation in ASP). The methodology can be applied to other scripting languages that use a class or object orientation, such as VBScript, JSP or PHP.

Broadly defined, the steps followed here in processing a form are:

  1. Instantiate a form validation object;
  2. Request data from the form;
  3. Insert the data and corresponding form element names in arrays in the object;
  4. Check the data for each element against validation rules (regular expressions or functions);
  5. Flag invalid data in the object;
  6. Display the form in the Web browser again with elements containing invalid data marked;
  7. Repeat the above steps until all the data the visitor inputs is valid;
  8. Process the validated data and redirect to the confirmation page.

Form design

The method for marking form elements containing invalid entries requires that either the <P> tag containing the text label for the form element or the form element itself have an ID that matches the name of the form element (depending on which you want to be marked in the form). In the example below, the text label for the form element will be marked:

<p id="first_name">First name <INPUT TYPE="text" NAME="first_name"></p>

<p id="last_name">Last name <INPUT TYPE="text" NAME="last_name"></p>

A quirk in the Form Collection in ASP is that checkboxes and radio buttons are not included in the collection, and thus not added to the Request.Form.Count property, if they are not checked (or if one of the elements in a group of radio buttons or checkboxes is not checked). Thus, a group of radio buttons that does not have a button selected will be considered "valid." To overcome this, include a hidden form element after each group of radio buttons and checkboxes with the same name as the group and an empty value, like so:

<p id="mood">Mood</p>

<input type="radio" name="mood" value="good"> Good<BR>

<input type="radio" name="mood" value="bad"> Bad<BR>

<input type="radio" name="mood" value="indifferent"> Indifferent<BR>

<input type="hidden" name="mood" value="">

A side-effect of this is that there will be an extra element containing a space in the Request.Form array, or a trailing comma and space after the last value if it is converted to a string. I'll show you how to trim it off the string in the function that processes valid data.

So that a visitor doesn't need to fill out the whole form again if some of the data is flagged as invalid, your server-side code should fill in the value with the data that the visitor input. For a radio button or checkbox, use:

<input type="radio" name="mood" value="good"

<% if (String(formData.str["mood"]).indexOf("good") != -1) Response.Write(" checked")%>>

And for a text element, use:

<input type="text" name="first_name"

value="<%=formData.str["first_name"]%>">

Give your Submit button a name and value so that you can use Request.Form to flag when the form has been submitted:

<input type="submit" name="btnReg" value="Register">

The form validation object

A form validation object consists of the following properties and methods:

  • An array for storing the data;
  • An array for storing the name of the corresponding form element;
  • An array for storing a Boolean value (true or false) for the validity of the data;
  • A variable for indicating whether the object has any invalid entries;
  • A method for collecting the data;
  • A method for validating the data entries;
  • A method for flagging invalid entries;
  • A method for deciding whether to process the form or mark invalid entries in the browser;
  • A method for processing the data.

The formValidationObject function creates the form validation object with the above properties and methods.

function formValidationObject()

{

this.str = new Array();

this.elem = new Array();

this.isValidEntry = new Array();

this.hasInvalidEntry = false;

this.getData = getData;

this.validate = validate;

this.flagInvalidEntries = flagInvalidEntries;

this.processOrMarkInvalidEntries = processOrMarkInvalidEntries;

this.processData = processData;

}

Requesting and inserting data into the form validation object

Instantiate a form validation object in the form page and call its getData method to fill the array properties with data. You should make the form page also be the action page, to keep the form validation object alive throughout the process, and to fill in the form with the user's data if some is invalid.

var formData = new formValidationObject();

if (Request.Form("btnReg") == "Register")

formData.getData();

The getData method creates an enumerator object to count the number of items in the Form object and loops through the object, inserting the data into the str array and the form element name in the elem array. Note that str is indexed by the counter, and elem is indexed by the name of the form element. The isValidEntry array property for the validity of the data is also filled here. (It is important to assume the data is valid by default.)

After the while loop finishes, the methods for validating the data and determining whether to process the data or mark invalid entries in the browser are called.

function getData()

{

var formItems = new Enumerator(Request.Form);

var i = 0;

while (!formItems.atEnd())

{

var elem = formItems.item();

this.str[elem] = Request.Form(elem);

this.elem[i] = elem;

// assume data is valid:

this.isValidEntry[i] = true;

i++;

formItems.moveNext();

}

this.validate();

this.processOrMarkInvalidEntries();

}

Checking the data against validation rules

The validate method loops through the str and elem arrays of the formData object, and checks the data against rules in regular expressions or functions within a switch statement. This is where it becomes important and useful that the str array is indexed by the name of the form element, and the elem array is indexed numerically. The elem array is used for setting the number of repetitions of the loop, and the name of the form element in elem at each increment of the loop is used as the index for str when testing str's data in the if statements within the switch.

function validate()

{

// validation regular expression for characters

// that shouldn't appear in a person's name:

var reJunkChars = /\; \[ \] \: \, \^ \? \{ \} \\ \! \@ \# \$ \% \& \* \( \) \+ \-$/;

// to check if a string contains a number:

var reNums = /\d/;

for (var i = 0; i < this.elem.length; i++)

{

switch (this.elem[i])

{

case "first_name":

case "last_name":

if (this.str[this.elem[i]] == ""

reJunkChars.test(this.str[this.elem[i]])

reNums.test(this.str[this.elem[i]]))

{

this.isValidEntry[i] = false;

this.hasInvalidEntry = true;

break;

}

case "mood":

// convert to string and use indexOf for radio button and checkbox values,

// since value will have a comma at the end of it from the hidden form field:

if (this.str[this.elem[i]] == "" this.str[this.elem[i]].indexOf("bad") != -1) // c'mon! cheer up!

{

this.isValidEntry[i] = false;

this.hasInvalidEntry = true;

break;

}

//' no default case, so that fields not required don't need to be filled out

}

}

}

Process or flag invalid data?

Now that each piece of data in the formData object has been labeled as valid or invalid, we determine whether to process it or flag any invalid entries in the browser. The processOrMarkInvalidEntries method checks the hasInvalidEntry property to see if an element in the object was flagged as invalid. If not, we call the processData method; if so, we go back to the form by calling the flagInvalidEntries method.

function processOrMarkInvalidEntries()

{

if (this.hasInvalidEntry)

this.flagInvalidEntries();

else

this.processData();

}

Marking invalid data in the Web form

If the flagInvalidEntries method is called, it will mark in the browser form elements containing invalid data. It does so by writing out a stylesheet that marks elements containing invalid data. Below, each element containing invalid data gets an ID selector that sets its color to red and font weight to bold. (This is where following the paragraph-ID-matching-the-form-element-name convention when designing your form comes into play.)

function flagInvalidEntries()

{

Response.Write("

<P style=\"color: red;\">Please enter or correct data in the fields marked with bold red text.</P>

");

Response.Write("<style type=\"text/css\">

");

for (var i = 0; i < this.elem.length; i++)

if (this.isValidEntry[i] == false)

Response.Write("P#" + this.elem[i] + "{ color: red; font-weight: bold; font-size: 14pt; }

");

Response.Write("</style>

");

}

Processing data

When all the data the visitor has submitted is flagged as valid, you can now do something useful with it when the processData method is called. One way to process the data is to use a loop to write out SQL that inserts the data into a database. This assumes the database column names are the same as the form element names.

function processData()

{

var insertSQL = "INSERT INTO registrant (";

for (var i = 0; i < this.elem.length; i++)

if (this.elem[i] != "btnReg") // exclude the Submit button

// add commas between column (form element) names

insertSQL += this.elem[i] + ", ";

// chop off the trailing comma at end of SQL string:

insertSQL = insertSQL.substring(0, insertSQL.lastIndexOf(","));

insertSQL += ") VALUES (";

for (var i = 0; i < this.elem.length - 1; i++)

{

if (this.elem[i] != "btnReg")

{

var inp = String(this.str[this.elem[i]]);

// get rid of trailing comma and space in radio button and checkbox data:

if (inp.lastIndexOf(", ") == inp.length - 2)

insertSQL += "'" + inp.substring(0, inp.lastIndexOf(",")) + "', ";

else

insertSQL += "'" + inp + "', ";

}

}

insertSQL = insertSQL.substring(0, insertSQL.lastIndexOf(","));

insertSQL += ")";

insertData = conn.Execute(insertSQL);

conn.Close();

conn = null;

insertData = null;

insertSQL = null;

// send the visitor to the confirmation page:

Response.Redirect("thanks.htm");

}

Conclusion

This methodology can be used for forms with one or 100 elements, by adding cases to the switch statement and rules for validating different types of data, and putting the appropriate form fill-in ASP code in your form elements. You could also have different types of data processing functions, depending on whether you wanted to insert data into a database as above, use an e-mail object to send it as a message, or use another processing method.

The access keys for this page are: ALT (Control on a Mac) plus:

evolt.org Evolt.org is an all-volunteer resource for web developers made up of a discussion list, a browser archive, and member-submitted articles. This article is the property of its author, please do not redistribute or use elsewhere without checking with the author.