Forums >> Programming >> Proof of Concept (POC) >>
Using AJAX and RPG for Web Page Validation




Posted:
bvstone

Using AJAX and RPG for Web Page Validation

 
Using AJAX and RPG for Web Page Validation

AJAX is a great little tool for any web programmer to take advantage of.  It can make pages seem more "interactive" and can help make youre site seem more professional.

One way that we're using AJAX in the Field Exit site is for validating fields in web forms.

Examine the following web page source for our Log In page:

<form id="updateForm" action="/forum/login" method="POST">
<input name="id" id="loginID" type="hidden">
<table>
	<tr>
		<td>User ID:</td>
		<td><input class="validateField" id="userid" name="userid">
			<div class="error" id="useridError"></div>
		</td>
	</tr>
	<tr>
		<td>Password:</td>
		<td>
			<input class="validateField" id="password" name="password" type="password">
			<div class="error" id="passwordError"></div>
		</td>
	</tr>
	<tr>
		<td colspan="2"><a class="button roundedall" id="loginButton" href="#">Log In</a></td>
	</tr>
</table>
</form>

This is a pretty standard form that has three input variables.  First, a hidden field name "id" and fields so the user can enter their userid and password.  You'll see each of the fields has not only a name (which is used when reading the value of it in our CGI program) but an ID as well, which should be unique.  Each field also has a class named "validateField".  This specific class will be used by jQuery to determine which fields will actually be validated.

There is then a <div> under each input field and the ID attribute for them is the same as the ID of the field, plus "Error".  This <div> will be used to display any errors that are specific to the field it is related to.

When a user clicks the loginButton, jQuery is called to first perform validation on any fields with a class of "validateField", as showen in the jQuery (JavaScript) specific to our login page:

$(document).ready(function(){

    $('#loginButton').click(function(e) {
		e.preventDefault();
		$('#loginID').val(randomString(256));
		$('#updateForm').submit();
    });		
	
});

When the loginButton is clicked, we first prevent the default from happening (ie, the hyperlink to go the specified location, this is because we're using hyperlinks with CSS for our buttons instead of "real" buttons).  Then we create a random string and change the value of the hidden ID field in our form to this value (this is just for validation so we know if it's a real person or not doing it).

Now, when the .submit() method is called for the form, that will trigger another event to fire (which is is our base javascript source):

$("#updateForm").submit(function(e) {
	e.preventDefault();
	$('.validateField').each(function(){
		validateField($(this).attr('id'),1);
	});
});

This next section of source will be run when a form with an ID of "updateForm" is submitted.  Again, first we prevent the default form action from happening (ie, submitting the form).  This is because we want to validate any fields for errors.   The jQuery to follow will loop through each (using the .each() jQuery function) of the fields in the form with a class of "validateField" and call the validateField() function, which looks like the following:

function validateField(inID, index) {
	xhr_get(inID, index, '/forum/a.validate').done(function(data) {
		$('#' + inID + 'Error').text(data);
		
		if (data.trim().length > 0) {
			numberOfErrors++;
		}
		
	});
}

The validateField() function will make an AJAX call (which is the call to the xhr_get() function).   If an error is returned, we increment the numberOfErrors counter (which is set to zero in another spot we'll cover later).  

The AJAX function looks like the following:

function xhr_get(inID, index, url) {
	var field = $('#' + inID).attr('name');
	var dataValue = $('#' + inID).val();
	var formData = $("#updateForm").serialize() + '&field=' + field + '&index=' + index;
	return $.ajax({
		type: 'POST',
		url: url,
		//async: false,
		data: formData
	})
	.fail(function() {
			// failed request; give feedback to user
			return('<p class="error"><strong>Error validating field.</p>');
	});
}

The AJAX call will be to an eRPG program named A.VALIDATE and it will pass in, at a minimum, the field name and the field data that we are validating.  It will also pass in any other name/value pairs that may be in the form using the .serialize() method.  When the AJAX call is done, it will have either returned blanks (no errors) or an error message specific to that field.  We can see then when control is returned back to the validateField() function, if there was any data we increment a counter for the number of errors.

This is where it gets a little tricky.  jQuery has a lot of built in functions that allow you to do some pretty cool stuff.  One of them is you can run a function when an AJAX call starts, and another when an AJAX call ends.  So, we use those to know when the validation starts and when it stops:

$(document).ajaxStart(function() {
	numberOfErrors = 0;
	showMessage('Validating data...');
});

$(document).ajaxStop(function() {
	$.unblockUI();

	if (numberOfErrors <= 0) {
		showMessage('Processing data...');
		$("#updateForm").unbind().submit();
	}
});

So, the first time our AJAX validation program is called, we reset numberOfErrors to zero (and show a message on the screen that also locks any UI which is done using the blockUI addon for jQuery UI)  When the AJAX call ends, we check the number of errors.  If it's less than or equal to zero, we finally let the form submit by "unbinding" it from the previous .submit() action and then submitting it (this way it won't get caught in a "loop" and never get submitted).

If there are any errors returned, we see that the validateField() function updates the division related to the field (ie, <fieldID>Error) with the text returned.  Here's the specific line from the validateField()  function that does that:

$('#' + inID + 'Error').text(data);

So, if the data is blank, the div will remain empty (or get "reset" if there was an error there previously).  If there is an error, then it will show in the <div> for the field.

Now, our AJAX call is to a program named /forum/a.validate.  That just means it's calling our eRPG program named "A.VALIDATE".  I like to prefix any AJAX specific programs with "A.", that's why it's named that way.

Again, this program is pretty straightforward.  The only "downside" to this is that for each special validation you need to update this program to accommodate that.  Not a big deal, but still something that needs to be done.  There are generic functions for checking a field isn't blank, and for checking numeric values, but other than that each subroutine is specific to each field.

     H DFTACTGRP(*NO) BNDDIR('GREENBOARD')
      ****************************************************************
      * Prototypes                                                   *
      ****************************************************************
      /COPY QCOPYSRC,P.ERPGSDK
      /COPY QCOPYSRC,P.STRING
      /COPY QCOPYSRC,P.GBFORUM
      /COPY QCOPYSRC,P.VLDL
      ****************************************************************
      * Copy Members                                                 *
      ****************************************************************
      /COPY QCOPYSRC,SQL
      ****************************************************************
      * Data read in from page
     D inField         S            128
     D inIndex         S             10i 0
      *
      * Work Variables
     D data            S           1024
     D message         S           1024
     D inUserID        S            256
     D inNewCPassword  S            256
     D i               S             10i 0
     D n               S             25  5
      ****************************************************************
      /free
       Exec Sql Set Option Datfmt=*Iso, Commit=*None, Closqlcsr=*Endmod;

       #vldl_setVldl('GBFUSERS':'GREENBOARD');
       #startup();
       message = ' ';
       exsr $Input;
       exsr $Validate;
       #writeTemplate('stdhtmlheader.erpg');
       #loadTemplate('a.validate.erpg':'top');
       #replaceData('/%data%/':message);
       #writeSection();

       #cleanup();
       return;
       //*INLR = *on;
       //-------------------------------------------------------------/
       // Validate the Data                                           /
       //-------------------------------------------------------------/
       begsr $Validate;

         select;
         when (inField = 'newuserid');
           exsr $newuserid;
         when (inField = 'fpwuserid');
           exsr $fpwuserid;
         when (inField = 'newpassword');
           exsr $newpassword;
         when (inField = 'password');
           exsr $password;
         when (inField = 'newemail');
           exsr $email;
         when (inField = 'none');
           // do nothing here...
           message = ' ';
         other;
           exsr $notBlank;
         endsl;

       endsr;
       //-------------------------------------------------------------/
       // Validate New User ID                                        /
       //-------------------------------------------------------------/
       begsr $newuserid;

         select;
         when (data = ' ');
           message = 'User ID connot be blank.';
         other;

           select;
           when (#vldl_userExists(data));
             message = 'User ' +%trim(data) +' already exists.';
           endsl;

         endsl;

       endsr;
       //-------------------------------------------------------------/
       // Forgot Password User ID                                     /
       //-------------------------------------------------------------/
       begsr $fpwuserid;

         select;
         when (data = ' ');
           message = 'User ID connot be blank.';
         other;

           select;
           when (not #vldl_userExists(data));
             message = 'User ID ' +%trim(data) +' doesn''t exist.';
           endsl;

         endsl;

       endsr;
       //-------------------------------------------------------------/
       // Validate Password                                           /
       //-------------------------------------------------------------/
       begsr $newpassword;

         select;
         when (data = ' ');
           message = 'Password cannot be blank.';
         other;

           if (data <> inNewCPassword);
             message = 'Passwords do not match.';
           endif;

         endsl;

       endsr;
       //-------------------------------------------------------------/
       // Validate Password                                           /
       //-------------------------------------------------------------/
       begsr $password;

         select;
         when (data = ' ');
           message = 'Password cannot be blank.';
         other;

           i = #gbf_isValidUser(inUserID:data);

           select;
           when (i < 0);
             message = 'Invalid password for ' +
                        %trim(inUserID) + '. rc(' + %char(i) + ')';
           when (i = 0);
             message = 'User is not active.';
           endsl;

         endsl;

       endsr;
       //-------------------------------------------------------------/
       // Validate email address                                      /
       //-------------------------------------------------------------/
       begsr $email;

         select;
         when (data = ' ');
           message = 'EMail Address cannot be blank.';
         other;

           if (%scan('@':data) < 2);
             message = 'Invalid Email ADdress.';
           endif;

         endsl;

       endsr;
       //-------------------------------------------------------------/
       // Validate Number (no decimals)                               /
       //-------------------------------------------------------------/
       begsr $number;

         select;
         when (data = ' ');
           message = 'Value cannot be blank.';
         other;
           monitor;
             n = %dec(data:15:0);
               on-error;
                 message = 'Value must be a number with no decimal places.';
           endmon;
         endsl;

       endsr;
       //-------------------------------------------------------------/
       // Validate Number (2 decimals)                                /
       //-------------------------------------------------------------/
       begsr $number2d;

         monitor;
           n = %dec(data:17:2);
             on-error;
               message = 'Value must be a number with ' +
                         'maximum of 2 decimal places.';
         endmon;

       endsr;
       //-------------------------------------------------------------/
       // Validate Field for Not Blank                                /
       //-------------------------------------------------------------/
       begsr $notBlank;

         if (data = ' ');
           select;
           when (inField = 'subject');
             message = 'Subject cannot be blank.';
           when (inField = 'postdata');
             message = 'Please enter a message.';
           when (inField = 'userid');
             message = 'User ID cannot be blank.';
           when (inField = 'password');
             message = 'Password cannot be blank.';
           when (inField = 'newuserid');
             message = 'User ID cannot be blank.';
           when (inField = 'newpassword');
             message = 'Password cannot be blank.';
           when (inField = 'newemail');
             message = 'EMail Address cannot be blank.';
           other;
             message = 'Invalid field ' + %trim(inField) + '.';
           endsl;

         endif;

       endsr;
       //-------------------------------------------------------------/
       // Read input from web page                                    /
       //-------------------------------------------------------------/
       begsr $Input;

         inUserID = #getData('userid');
         inNewCPassword = #getData('newcpassword');
         inField = #getData('field');
         inIndex = #CtoN(#getData('index'));

         if (inIndex <= 0);
           inIndex = 1;
         endif;

         data = #getData(inField:inIndex);

       endsr;
      /end-free

So, what we do here first is read the input data from the web page.  Again, at the very least we will have a field named "field" that names the data field, and the data associated with that field.

We then use a trusty SELECT statement to determine which subroutine to call to validate that particular piece of data.  We also have one special case for a field named "none" so that if we do have a web page with no validation, we can put a hidden field with this name in it and still process things through our AJAX validation.

If an error is found, it is written out to the page, which then is returned to the validateField() function and the error can then be displayed on the web page.

You can test some of this out by going to the login screen and entering some invalid information, or just leaving the fields blank.  Have fun!


Last edited 07/26/2015 at 14:28:38


Reply




Copyright 1983-2017 BVSTools
GreenBoard(v3) Powered by the eRPG SDK, MAILTOOL Plus!, GreenTools for Google Apps, jQuery, jQuery UI, BlockUI, CKEditor and running on the IBM i (AKA AS/400, iSeries, System i).