HTML Form Manipulation

Site Map
 
Home
 
Application Guide
  Quick Start
  PHP and ASP
  Writing Apps
  Applications
  Tutorials
    Replication
    Postgres Interface
    XML Processing
    Using XPath
    Binary Object
    Using HttpRequest
    SmtpRequest
    Session
    Meta Data
    Iterators
    Memb Services
    Page Look&feel
    Authentication+
    Questionnaires
    Message Groups
    Form Handling
  Samples
Reference
Community
Contact Whitebeam
To-Do
Download
Credits
Licence
Whitebeam Users
 
 
 

HTML Form Manipulation

Subject

This tutorial illustrates how the Whitebeam 'forms' library can help you write and manage complex form interactions in web browsers. The library primarily consists of a set of XML tags that allow web-designers to define and manipulate the forms they design. The following topics are covered:


Overview

The Whitebeam Form library simplifies the development of user interaction with websites through HTML forms. The aim is not to do something that cannot be done using the raw server-side and client-side capabilities of the environment, but rather to apply a set of abstractions to simplify the implementation and speed development.

The model encapsulates a set of form fragments and command handlers in a single 'Presentation Page' (file). A simple example should illustrate the capabilities. Consider a simple form sequence that does the following:

  1. Displays a list of users
  2. Allows the user to edit one of the users configurations.
  3. Allows a user to be 'removed' - with the safety check of a 'confirm' page.

In a traditional web design the forms would be designed in one page - possibly static. A 'submit' button would direct the data to a server side programme - such as a CGI programme. That page would apply the changes then go back to the main page.

This can be very clumsy. Take a simple sequence:

  • Display Users
  • User selects a user and pushes the 'remove' button
  • Server sends a 'confirmation' page
  • User a) confirms or b) cancels
  • On confirm the user is removed and the user list is redisplayed
  • On cancel we simply go back to the user list having made no changed.

Traditionally you'd implement the initial list, the confirm box and the delete action in separate files. Most server side processing environments, including Whitebeam, would allow you to keep all three elements in a single file. This con be complex and at times confusing. The Whitebeam form library takes this common behaviour and makes it much simpler by defining a set of XML tags to encapsulate and abstract away the complexity.


Dependencies

The Whitebeam form library is implemented in a single file 'form.inc'. This file contains all the JavaScript and tag implementations to support the form abstraction. To use this mechanism in a file - simply include it at the start of your file using:

      <rb:include src="form" system="yes"/>

Formal Definitions

Formal definitions for the tags described in this tutorial can be found here.


Design

If you are following the Whitebeam recommended web design practices you will already be familiar with this step. Forms and user interraction can be complex - and while it is possible to implement a complex interraction by trial and error it is much easier if the system is properly designed up front. As an example we're going to build on the proposal in the previous section.

First of all some basics. Each user interraction is going to be broken into a number of 'commands'. Each command is either the result of a user pressing a 'submit' button or clicking on a hotlink within the displayed page. We're going to use the design notation to describe parts of the user interaction, and each of the 'bubbles' represents a page as seen by the user - not necessarily a separate Whitebeam presentation page. In fact you'll see that most of the client pages are implemented as a single Presentation Page (file).

The following diagram illustrates the interraction we are going to implement:

In this diagram there are 8 logical 'pages' - but remember those with 'dotted' lines are transient - they perform some action such as creating a member - then immediately move on to another page. The initial page displayed to the browser is the 'Member List' - this is the entry point for the sequence.

The Whitebeam form library encapsulates this entire sequence in a single presentation page. Each of the 'pages' is represented as a 'command'. The set of commands that make up this sequence are called the 'formset'. Each command has a name - these have to be unique within the formset. The entry command to the formset has the name 'default'. The formset and the commands are represented within the file using XML tags as follows:


   <rbm:formset>
      <rbm:command name="default">
         <!-- The 'default' command. This is the page displayed if the page contains -->
      </rbm:command>
      <rbm:command name="createform">
         <!-- First stage in the create sequence - display a user creation form.  -->
      </rbm:command>
      <rbm:command name="createmember">
         <!-- Transient command to create a new user. -->
      </rbm:command>
   </rbm:formset>

In this example skeleton we have a formset with three actions - no implementation yet! This would generally be the first stage of generating the form handler after the diagram above. That is - take the diagram and create a formset with command tags for each identified command.

The form library decides which command to load by looking for a form parameter with a name stating with 'rbcmd-'. For example 'rbcmd-create'. 'rbcmd-' will be stripped from the front of this leaving a string, in this example 'create'. The form library looks for a command handler with this name.

If there are no parameters with the 'rbcmd-' prefix, or the resulting command cannot be loaded, the library will attempt to load the command called 'default'.


Form Data

If you include 'form.inc' in your application we can be pretty sure you are going to want to use form data of some kind. To simplify the use of this - the form library itself extracts the form data and stores it in a special global variable called rb$params. This means that if you want to check for a specific parameter, for example 'username', then you just have to look at rb$params.username, rather than have to call rb.page.formdata().


Processing Command Handlers

The library will execute the selected command handler. Execution comprises the following steps.

  1. Look for a 'test' attribute, if found evaluate the test expression.
  2. If the test passes (evaluates to true) or there is no test, run the command.
  3. If the test is present and fails - execute the specified error command instead.
  4. See if the command wants to execute another command, if so locate the command handler and go to stage 1.

Command Test

There is often a pre-condition that must be tested before a command can be executed. Take our membership example above. In this case, in order for the 'view member' command to be correctly executed, the user must have selected a user on the default page.

These tests can be performed manually within the body of the command, most commonly using an <rb:test...> tag. This does bloat the code though - and makes the resulting page more complex to understand. The form library 'test' facility is a good alternative if the test is fairly straightforward.

The test facility uses 3 XML attributes on the <rbm:command...> tag:

attributevaluecomments
testJS expressionMust evaluate to 'true' for the command to execute. If the command fails then instead of running the command, run the command specied by 'error' attribute.
errorcommand nameUsed if 'test' expression evaluates to false. Name of an alternative command to execute.
msgError MessageUsed if test evaluates to false. Stores the value part in the special Whitebeam variable called rb$message before executing the error command handler. This attribute is optional.

This seems simple - but can make for very easy form error checking that can cover a multitude of error recovery situations. Again - take the membership example. In this example, all the commands except create need to have the identification of the member on which to operate (who are we going to edit/view/delete?). We decide we're always going to pass this through as a parameter called 'memberid'. If the member is not specified then we simply want to go back to the member list - but also display an error message telling the user to first select a member.

With the Whitebeam form library this is very easy. Remember from the previous section the form data is already stored in a global variable, rb$params. To check for the 'memberid' parameter we simply write our rbm:command tag as follows:

<rbm:command test="rb$params.memberid!=null" error="default" msg="First select a member">
   [body - skipped if the test fails.]...
</rbm:command>   

If the test fails the body of the command is not executed, instead the default command is executed and the rb$message JavaScript variable is set to contain an error message. More complex tests can be performed by defining a validation function and calling that from the 'test' expression - having the function return 'false' of validation fails.


Command Sequences

As previously described the form library will select a command handler basic on the 'rbcmd-' parameters passed from the client. Some of these command will display information on the browser and wait for the user to take some action - a user event.

Other commands thought may take some internal action - consider the command we execute when the user has entered new member information and pressed the 'OK' button. The handler must take the form information presented - validate that information then either@

  1. If the data is valid - create the new member - then display the list of members once more.
  2. If there are errors in the data - such as missing mandatory fields - then show the form again, telling the user what errors they have made.

Obviously the 'create' handler could do all this, but the member list is already handled by the 'default' command and the form is already presented by the 'createUser' command. These need to be reused to avoid duplication. In effect the create code wants to redirect execution to another command. Ideally the create command will look like:

<rbm:command name="createMember">
   <rb:script>
      if (doValidateParams(rb$params)) {
         // Its OK to try and create the member.
         var aMember = new Member(rb$params);
         aMember.create();

         rb$command = 'default';
      }
      else {
         // Validation failed.
         rb$command = 'showCreateForm';
      }
   </rb:script>
</rbm:command>

Note that this is not intended to be complete - the parameters and the creation result should check to make sure it happend. You can however see that one command handler passes control to another simply by setting the value of rb$command before returning. In this way an entire sequence of handlers can be executed. Note that setting thiw propertry

Notes

  1. Commands can redirect to themselves - or sequences of command could in theory get stuck in infinte loops. To avoid this the Presentation Engine will not execute more than 5 commands before requiring a user event (basically a submit or click on a URL).
  2. Automatically passing control using rb$command does not cause any intervention from the browser and the operational environment (parameters etc) are not changed.

Displaying and Manipulating the Forms

Some of the commands in the form set will want to display real HTML forms - showing data and allowing input from the user. These forms can be implemented in exactly the same way as standard HTML forms - using HTML tags. The Whitebeam Form Library on the other hand provides some facilities to make this a lot simpler. This additional behaviour is provided by enhancing the standard form tags using server-side processing before they reach the clients browser. The following sections describe the facilities available.


Initialising Form Fields

The following HTML creates a very simple form containing two text elements:


<form action="membadmin.rhtm" method="get">
   <table class="pretty">
      <tr>
         <td>Title</td>
         <td><input type="text" size="10" name="title"/></td>
      <tr>
      <tr>
         <td>First Name</td>
         <td><input type="text" size="50" name="first_name"/></td>
      </tr>
      <tr>
         <td>Surname</td>
         <td><input type="text" size="50" name="surname"/></td>
      </tr>
      <tr>
         <td colspan="2">
            <input type="submit" name="rbcmd-apply" value="OK" />
            <input type="submit" name="rbcmd-cancel" value="cancel" />
         </td>
      </tr>
   </table>
</form>
   

This form displays three edit boxes along with two buttons. This basic skeleton could be used both for a 'create member' and a 'edit member' type form. However - in the latter, it would have to be modified to contain the existing values. This is achieved in HTML using the 'value' attribute to the input tag.

So to create both forms there would usually be two instances of this HTML in the file: one for the create form and a more complex for the edit form. The edit form would be required to load its data from some dynamic JavaScript variable containing the members information, something like:


<form action="membadmin.rhtm" method="get">
   <table>
      <tr>
         <td>Title</td>
         <td><input type="text" size="10" name="title"
                    rb:eval="value#aMember.title"/></td>
      <tr>
      <tr>
         <td>First Name</td>
         <td><input type="text" size="50" name="first_name"
                    rb:eval="value#aMember.first_name"/></td>
      </tr>
      <tr>
         <td>First Name</td>
         <td><input type="text" size="50" name="surname"
                    rb:eval="value#aMember.surname"/></td>
      </tr>
      <tr>
         <td colspan="2">
            <input type="submit" name="rbcmd-apply" value="OK" />
            <input type="submit" name="rbcmd-cancel" value="cancel" />
         </td>
      </tr>
   </table>
</form>
   

This becomes cumbersome, especially if you consider the case where some (or all) fields may not be present. To simplify this process the forms library allows you to specify a data source before creating the form. The library then searches this JavaScript object for values matching the field name. If found the library automatically create a value clause for that item. To use this facility you must take the following steps:

  1. Set rb$data to reference something containing initialisation parameters for the form items.
  2. Make sure the <input...> name attribute matches the field name in rb$data.

So the simplification of the form above is as follows:


<rb:script>
   rb$data = aMember;
</rb:script>

<form action="membadmin.rhtm" method="get">
   <table>
      <tr>
         <td>Title</td>
         <td><input type="text" size="10" name="title"/></td>
      <tr>
      <tr>
         <td>First Name</td>
         <td><input type="text" size="50" name="first_name"/></td>
      </tr>
      <tr>
         <td>First Name</td>
         <td><input type="text" size="50" name="surname"/></td>
      </tr>
      <tr>
         <td colspan="2">
            <input type="submit" name="rbcmd-apply" value="OK" />
            <input type="submit" name="rbcmd-cancel" value="cancel" />
         </td>
      </tr>
   </table>
</form>
   

Which coincidentally is the same as the format for an empty form - so including the form in an rb:block tag and using it in both places becomes possible. For the create form, set rb$data to null before including the block.


Highlighting Errors in Form Entry

We can now fairly simply generate a form preloaded with data and accept the submit of that data back into the browser via the command model. The first thing the submit handler does is to validate the information entered by the user. There remains the problem of what to do if the validataion fails - for example because the name contains illegal characters. Ideally we would display the form again, this time with an error message and with the incorrect field highlighted.

The form library supports the marking of individual fields in a form as it is constructed. It does this by looking at a JavaScript variable called rb$errors for a property name matching the field name. If it is present and the value is 'true' then this field is considered in need of highlighting and the library will show the item with a different background colour. By default this is a light blue, but the colour can be changed to storing the appropriate string in rb$errorColour.

At the moment the colour of the text in a highlighted field is not modified so choose a highlighting colour appropriate to the text colour!


Static Display

It is sometimes useful to be able to display a form as static text - i.e. use the same fields as a defined form - but simply display the values rather than include edit boxes. The standard method would be to use separate HTML along with server-side rb:evals to display the static data.

Instead - if a form has been defined that contains the necessary definitions, then the form library allows the form to be display as 'static' (i.e. not a form) data. To do this simply set the rb$static JavaScript variable to 'true' before displaying the form.


Example

This example combines all the elements described above to implement a user create/edit/view system. Note that in this example the code for the default command has been omitted, as have several command handlers.


<rb:script>
   rb$data = aMember;
</rb:script>

<!-- Define the form in a block so it can be reused. -->
<rb:block rb:id="form">
         <tr>
            <td>Title</td>
            <td><input type="text" size="10" name="title"/></td>
         <tr>
         <tr>
            <td>First Name</td>
            <td><input type="text" size="50" name="first_name"/></td>
         </tr>
         <tr>
            <td>First Name</td>
            <td><input type="text" size="50" name="surname"/></td>
         </tr>
</rb:id>

<!-- Clear the rb$data field. -->
<rb:script>
   // Make sure there is no data laying around.
   rb$data = null;
</rb:script>

<!-- Collection of commands. -->
<rbm:formset>

   <!-- Display member details in a table.. -->
   <rbm:command name="view">
      <form action="membadmin.rhtm" method="get">
         <table>
            <rb:script>
               // Make it static.
               rb$static = true;

               // Output the form
               var tags=rb.xml.tree("form");
               tags.regenerate();
               rb.page.write(tags.bodytext());
            </rb:script>
            <tr>
               <td colspan="2">
                  <!-- Just a cancel button here. -->
                  <input type="submit" name="rbcmd-cancel" value="cancel" />
               </td>
            </tr>
         </table>
      </form>
   </rbm:command>

   <!-- Display a table containing editable field values. -->
   <rbm:command name="createForm">
      <form action="membadmin.rhtm" method="get">
         <table>
            <rb:script>
               // Output the form
               var tags=rb.xml.tree("form");
               tags.regenerate();
               rb.page.write(tags.bodytext());
            </rb:script>
            <tr>
               <td colspan="2">
                  <!-- Just a cancel button here. -->
                  <input type="submit" name="rbcmd-do_create" value="Create" />
                  <input type="submit" name="rbcmd-cancel" value="cancel" />
               </td>
            </tr>
         </table>
      </form>
   </rbm:command>

   <!-- Display a table containing editable field values, preloaded
        from the currently selected member. -->
   <rbm:command name="editForm">
      <form action="membadmin.rhtm" method="get">
         <table>
            <rb:script>
               // rb$data needs to reference my member
               // data I read from the membership component.
               rb$data = aMember;

               // Output the form
               var tags=rb.xml.tree("form");
               tags.regenerate();
               rb.page.write(tags.bodytext());
            </rb:script>
            <tr>
               <td colspan="2">
                  <!-- Just a cancel button here. -->
                  <input type="submit" name="rbcmd-do_edit" value="Apply" />
                  <input type="submit" name="rbcmd-cancel" value="cancel" />
               </td>
            </tr>
         </table>
      </form>
   </rbm:command>

   <!-- When the button is actually pressed. -->
   <rbm:command name="do_create">
      <rb:script>
         // There MUST e a unique name field and an email address in the form data.
         var error = false;

         if (rb$params.uName==null) {
            // No uName entered.
            rb$error.uName = true;
            error = true;
         }
            
         if (rb$params.email==null) {
            // No uName entered.
            rb$error.email = true;
            error = true;
         }
         // Any errors detected?
         if (error) {
            // YES - go back and display the create form again,
            // but this time load the form with the values entered by the
            // user last time around to save having them re-entered.
            rb$command="createForm"
         }
         else {
            // Create the new member.
            aMember.create();

            // Now go and display the default page again.
            rb$command = 'default';
         }
      </rb:script>
   </rbm:command>
</rbm:formset>

   

Download form library

The form library in encapsulated in a single library file that you include in your Presentation Pages as follows :

<rb:include src="/include.inc"/>

(change the path if you store the file elsewhere!)

Whitebeam release 1.3.35
(loadtime : 15ms)