How To Use Client Side JavaScript Validation in BC4J JSP Generated HTML Edit Pages 

An Oracle JDeveloper TechNote
October 2002

Content

Product Version

This document was developed using Oracle9i JDeveloper release 9.0.3. If you are using JDeveloper 9.0.2 the solution is the same, but you will find some differences in the screenshots provided.

Introduction

Client side validation allows immediate response to a user interacting within an HTML form. For example, the response could display an alert when a business rule is violated or correct data if an entry does not have the desired format. Client side validation is not meant to replace server side enforcement of business and format rules. It should be thought of as a convenience for the end user to avoid waiting for a server to respond just to find out whether the submitted data was correct.

This document explains how to add JavaScript validation logic to HTML UIs generated with BC4J JSP data tags. The JavaScript validation logic can be added to attributes of either the BC4J entity object or the view object, depending on the desired scope that the validation logic. If you are a Struts developer with JDeveloper 9.0.3, you can choose to combine the client side validation provided by the Struts framework with the JavaScript validation solution explained in this document--as both approaches are not mutually exclusive. 

Creating a new BC4J JSP Browse&Edit Form with JDeveloper 9.0.3, adds the component file, DataEditComponent.jsp, to the project. This component file handles the rendering and data binding of the HTML form fields in the application. Because the DataEditComponent.jsp file is designed for reuse with many JSP pages requiring data input forms, the rendering code in the JSP page is generic. By default the following code is generated by JDeveloper when creating  DataEditComponent.jsp.

...
<jbo:AttributeIterate id="def" datasource="dsEdit">
    <tr>
    <td title="<jbo:ShowHint hintname='TOOLTIP'/>" align="right"><jbo:ShowHint hintname="LABEL" /></td>
    <td title="<jbo:ShowHint hintname='TOOLTIP'/>"><jbo: InputRender datasource="dsEdit" formname="<%=formName%>"/></td>
    </tr>
</jbo:AttributeIterate>
...

The actual field rendering is performed by the BC4J JSP InputRender tag and will layout all fields as text fields. 

Example HTML Form and Case

The following HTML form is created by the DataEditComponent.jsp referenced from PeopleView1_Edit.jsp. When using the BC4J JSP wizard to create a default set of pages, then the name of the edit page always contains the name of the underlying view object upon which it operates. Throughout this document name of the view object instance, that the edit form is build upon, is named PeopleView1. PeopleView1_edit.jsp contains all JSP code related to building the page, except for rendering  the HTML form and the form fields. This information is included to the page during runtime by the BC4J JSP DataEdit tag.

...
<jbo:DataEdit datasource="ds" enctype=""/>
...

The image below shows a data entry form rendered by the DataEditComponent.jsp component contained in the PeopleView1_Edit.jsp application. 

Because all attributes are rendered by the same InputRenderer tag, it is obvious that client side validation on an item level cannot be added directly to the JSP page  but must be provided on the BC4J view object or entity object. In this example, we define the JavaScript event handler as a property on the entity attribute. Because the Java and JavaScript definitions reside side by side at the entity level, it will be much easier to maintain consistency between the middle tier validation logic and the client tier JavaScript. Also, storing the definition at this level makes it available to all HTML UIs that render this attribute. Adding the JavaScript event handler as a property on the view object attribute changes the scope of validation to HTML UIs rendered for this view object only.

The desired client side validation behavior in this example, enforced with JavaScript added to the entity object attribute,  does not allow a user to provide the same string for the username and password field. If the same strings are used, then an alert shall be displayed informing the user that the entered data is not valid.

About BC4J JSP HTML Renderers

The BC4J InputRenderer tag uses a Java class on the server to define the HTML field including data read from the associated attribute in the view object. All default BC4J HTML renderers are stored in the oracle.jdeveloper.html package and can be subclassed from there. And this is how the client side validation is added to the view object or entity object in the following example. 

Client Side Validation Renderer Example

Client side validation in HTML is performed by JavaScript functions called  through JavaScript events. The associated JavaScript events for an HTML text field are: onBlur, onChange, onFocus, onKeyDown, onKeyPress, onKeyUp, and onSelect. All these events, or at least some of them, must be rendered by the BC4J HTML renderer to perform client side validation.

In this howto document, the clientSideValidationrenderer.java file was created, representing a subclass of the oracle.jdeveloper.html.TextField renderer. The clientSideValidationrenderer.java contains additional code for the following tasks:

The following two methods are contained in clientSideValidationrenderer.java as either an overwriting method or new method.

renderToString() method

The renderToString() method in the custom HTML renderer overwrites the renderToString() method of the oracle.jdeveloper.html.TextField superclass and is modified to find JavaScript event names within the properties of an attribute [1]. If a JavaScript event is found for this attribute, then the associated JavaScript command is retrieved and analyzed [2]. If the JavaScript command contains <%attribute_name%> placeholders [3], then the string gets parsed separately using the parseArguments() method, replacing the placeholders by the actual attribute value [4].

public String renderToString(Row row)
    {
      // get all properties defined for a BC4J EO or VO
     AttributeDef aDef = getAttributeDef();
     [1] String[] jsTextEvents = {"onBlur","onChange","onFocus","onKeyDown",
        "onKeyPress","onKeyUp","onSelect"};
     HashMap jsAttr= new HashMap();

     String jsEventName = "";
     String jsEventVal = "";

     for (int i = 0; i < jsTextEvents.length ; i++)
      {
        jsEventName = jsTextEvents[i];
     [2]   jsEventVal = (String) aDef.getProperty(jsEventName);
     [3]   if (jsEventVal!= null && jsEventVal.indexOf("<%")>=0)
          { 
     [4]       jsEventVal = parseArguments(jsEventVal, row);
          }
        if (jsEventVal != null && jsEventVal !="")
          {
             jsAttr.put(jsEventName,jsEventVal);
         }
      }
     setHtmlAttributes(jsAttr);
     return super.renderToString(row);
   }

 

parseArguments() method

The parseArguments() method replaces placeholders in the command string by the value of the attribute they define. The placeholder <%Username%> in a string is replaced (for example, by "ddursley" in the resulting JavaScript command). The use of placeholders allows the JSP to add default values, read from BC4J into the JavaScript code.

Complete code of  clientSideValidationRenderer.java
  package oracle.java.bc4j.howto;

  import oracle.jdeveloper.html.TextField;
  import oracle.jbo.Row;
  import oracle.jbo.AttributeDef;
  import java.util.StringTokenizer;
  import java.util.Hashtable;
  import oracle.jdeveloper.html.HTMLInputElement;
  import com.sun.java.util.collections.*;
  import java.util.Iterator;

  /**
  * add client side JavaScript validation. All JavaScript events start with
  * on<EventName>. The JavaScript value can either be a call to a
  * function contained in the JSP page or the actual JavaScript code itself.
  * For the purpose of ease of maintenance and a finer grained problem
  * tracking it is recommended to use a JavaScript source file *.js and import
  * this in the JSP page.
  */

public class clientSideValidationRenderer extends TextField
 {

    public clientSideValidationRenderer()
    {
    }

    public String renderToString(Row row)
    {
      // get all properties defined for a BC4J EO or VO
     AttributeDef aDef = getAttributeDef();
     String[] jsTextEvents = {"onBlur","onChange","onFocus",
          "onKeyDown","onKeyPress","onKeyUp","onSelect"};
     HashMap jsAttr= new HashMap();

     String jsEventName = "";
     String jsEventVal = "";

     for (int i = 0; i < jsTextEvents.length ; i++)
      {
        jsEventName = jsTextEvents[i];
        jsEventVal = (String) aDef.getProperty(jsEventName);
        if (jsEventVal!= null && jsEventVal.indexOf("<%")>=0)
          { 
             jsEventVal = parseArguments(jsEventVal, row);
          }
        if (jsEventVal != null && jsEventVal !="")
          {
             jsAttr.put(jsEventName,jsEventVal);
         }
      }
     setHtmlAttributes(jsAttr);
     return super.renderToString(row);
   }

   public String parseArguments(String args, Row rw)
    {
      String oldString="";
      String newString="";
      StringBuffer sbCompletedText= new StringBuffer();
      String sAttribute="",sAttributeValue="";

      int currPos=0;
      int startArgumentPos = 0; // the <% string
      int endArgumentPos = 0; // the >% string 
      int eoArgument = args.length();

     oldString = args;

     while (((startArgumentPos = oldString.indexOf("<%",currPos))>-1) 
     && ((endArgumentPos = oldString.indexOf("%>",startArgumentPos))>-1))
     {
         sbCompletedText.append(oldString.substring(currPos,startArgumentPos));
        // pos of <% + 2
       currPos = startArgumentPos +2;
       sAttribute = oldString.substring(currPos,endArgumentPos);
       currPos=endArgumentPos +2;
       sAttributeValue=(String)rw.getAttribute(sAttribute);
       if (sAttributeValue!=null && sAttributeValue!=""){
          sbCompletedText.append(sAttributeValue);
      }
    }
    // add the final part of the resulting String

   sbCompletedText.append(oldString.substring(currPos,eoArgument));
   newString=sbCompletedText.toString();

   return newString;

  }
}

 

With this code by hand we now have a modified HTML text renderer that can be used with client side JavaScript validation. The same code works for text fields and password fields with only one change is required. This required change is for the HTML renderer to extend the PasswordField class within the oracle.jdeveloper.html package rather than the TextField class.

Example JavaScript Code

The required client side validation for the example in this document is that username and password must be different strings. To check this, four JavaScript functions and two page variables are defined. Two JavaScript functions are called whenever a user changes the value within one of the two HTML fields, updating one of the two variables with the current username or password. One function is called when a user first navigates to either the password field or the username field, providing the default username and password pair to the variables. The fourth function is called to compare both strings, displaying an alert if they are the same. 

The JavaScript code is not directly added into PeopleView1_Edit.jsp, which would have been an option too, but instead is stored in a file howto.js for better manageability. Storing the JavaScript code in a separate file allows reuse and makes it easier to build test cases if needed for error tracking.

Configuration of the View Object or Entity Object

The custom HTML renderer clientSideValidationRenderer needs to be registered with a view object or entity object attribute to be used during runtime instead of the default item renderer.

The image above shows the JavaScript events defined as properties of the Username attribute in the People entity object. The placeholder <%Username%> and <%Password%> indicate that BC4J values need to be passed to the JavaScript function. As shown in the JavaScript code above, the initializeUsername Password() method sets the values of the username and password page variable, so that when the user clicks into the Username text field, the current attribute values of the fields to compare are known. Similar for the password field:

 

JSP Page Configuration

The JSP include directive is used to import the JavaScript code from the howto.js source file into the  PeopleView1_Edit.jsp page. Note that surrounding <script> tags are required to mark the JavaScript for the browser to recognize it.

Important: For the custom HTML renderer to work, it is necessary to add the location of the custom HTML renderer class as an additional path in the JSP project. Changes to a project always take effect after recompiling the project.

Client Side Validation in Action

Starting the same application as before, using the custom HTML renderer and the JavaScript code of this document, the page now immediately shows an alert when the username and password field values are the same.

The HTML page source contains the JavaScript events defined for the HTML text fields

...

<INPUT onFocus="initializeUsernamePassword('ddursley','dudley');" VALUE="ddursley" TYPE="TEXT" MAXLENGTH="15" NAME="Username" SIZE="20" CLASS="clsUsername" onChange="setUsername(this.value); compare();">

Summary

Client side validation is performed using JavaScript for HTML forms. To add this type of validation to the generic data edit component of Oracle9i JDeveloper, DataEditComponent.jsp, a custom BC4J HTML renderer needs to be written and registered with the view or entity object attributes. Client side validation is for user convenience and does not take away the responsibility of enforcing business rules on the middle tier.


Recommended additional reading: HOWTO: Use BC4J HTML Field Renderers

false ,,,,,,,,,,,,,,,