Rationale

HTML comes with a form tag, which allows to create complicated forms with many different controls like check boxes, text areas etc. Unfortunately it lacks other functionality that is important for user and crucial for web application. Java Script can help to fill up these holes. In this document we will try to describe how to prepare a good form, implementing very useful functionality like data validation on client and server side, handling errors in data provided by user, handling server side errors, along with ability to submit a form without refreshing a whole web site. We also tried to provide ready made libraries and tools that will make the process of creating a form easier. This approach should be used in every form in a dashboard framework.

Dependencies

In our particular example we will use several tools and libraries to achieve our task. This tools are used by dashboard framework.

  • jQuery - for handling events and ajax request and as a basic dependency for the rest of tools
  • jQueryUI - customized form controlls
  • form validation engine - to validate and indicate errors in data provided by client
  • notifications - for presenting confirmation from server or indicating error on the server side

Markup

Basic form markup should look like this:
<form action="someAction" id="theForm">
<!--- definition of controls comes here -->
<input type="submit" value="Sumbit"/>
</form>

Form id will be used by a script as a reference to the form to overload default submit action, decorate controls, and attach submit event. action attribute is specified only to achieve a graceful degradation for users who have disabled Java Script.

Java Script part

After we finish with html markup we want to call Java Script to:

  • Decorate controls
  • Hook up some events to validate data that will result in sending data to server and handling the answer or displaying error message. So we can write very generic piece of code that will try to address this tasks when our page is loaded:

$(document).ready
    (
        function(){
        decorate();
        prepareFormValidation();
        }
    );

decorate() function

decorate() function is used for customization form controls including submit button. We can also use it to assign default values to form controls. Look at the example:

    function decorate()
    {
      var form = $("#theForm"); // take a handle to form
      $("input:submit",form).button(); // select submit button of the form and decorate it
      $('select.cars, w').selectmenu({style:'dropdown', width:150}); // decorate select contol
      $('.name, w').val(jQuery(document).getUrlParam("name")); // assign default value to the text area taken form URL parameter
    }

prepareFormValidation() function:

This purpose of this function is to handle submit and key events of the form and make a validation:

function prepareFormValidation()
{

Firs we attach validation engine to the form and ask it to trigger on keyup. This means each time user leaves some form control this control will be checked if it contains correct data:

    $("#theform").validationEngine({validationEventTriggers:"keyup"});

Then we hook up submit event of the form:

   
    $("#theform").submit(
    function(event){

What we do inside is asking validation engine if the form contains correct (validating) data:

    if($("#theform").validationEngine('validate'))
    {

If the if statement above fail, the validation engine will automatically display error message to the user and underline fields containing errors.

If data is valid we can send it to server without refreshing the web page using ajax request. There are several things to notice:

  • To gather data from the form we are using separate function called gatherData().
  • The url attribute of ajax() function is the same as action attribute of the form. This is done to achieve graceful degradation of the form.
  • In case of serious error on the client side (server has crashed, request have timed out, etc. ) we use apprise to inform user about the problem.
  • If server responds we will handle this calling modifyCallback callback.

So here we go:

    $.ajax({
            "url": "someAction",
            "data": gatherData(),
            "success": modifyCallback,
            "contentType": "application/json",
            "dataType": "json",
            "cache": false,
            "error": function (xhr, error, thrown) {
                    apprise( "error description" );
            }
            });
}

We want to prevent from executing default action of the form (if we came so far we are sure that user have Java Script enabled).

        event.preventDefault();

And that's all:

    });
}

Gathering data

gatheData() function is really simple:

function gatherData()
{

First lets define an array that will contain all the data that will be send to server:

    var oInfoData = [];

Then for each control we are reading its value and putting it to an array. You should note two things here:

  • Each piece of data (a 'parameter' from server piont of view) is a Java Script object (dictionary) containing only two entries: name and value.
  • To preserve graceful degradation name entry in a dictionary, and corresponding control name attribute as well as control id attribute should be the same

Lets take a look:

   
    oInfoData.push({
        "name" : "number",
        "value" : $("#number").val()
    });

   ...

   oInfoData.push(...);

At the end we return our data array:

                               
    return oInfoData;
}

Security Note

Warning, important Important! You should remember that we do client side validation of the form just for our user comfort. It is better for users when most trivial errors are detected almost immediately after submitting form on client side without waiting for server to respond. But the server itself should NEVER RELY ON DATA FROM CLIENT, even if we have client side form validation. Forgetting about this fact will create serous security hole.

Server side

After we send ajax request to the server it should check if user has credentials to perform this request and validate data. If user have privileges and data is valid server computes and sends back the result. As a convention for all dashboard form requests, an answer from server should be send in JSON format. If server detected an error, JSON should contain 'error' filed with error description. If everything was fine, among fields directly related to specific request, the server should attach 'info' field which should contain confirmation message text. 'error' and 'info' fields are therefore mutually exclusive and are the way to indicate success or failure of user request.

Handling server response

In ajax() function all above we specified function callback that will be executed if we get a response from server. This callback have to check if server said that data was fine and provided useful response that we can render or there was error detection on server side and we have to indicate it. This is how we do it:

function modifyCallback(json)
{

according to our protocol, if we find in response 'error' field we should just notify user about problem:

    if (typeof json.error !== 'undefined') {
        $.pnotify({
                    pnotify_title: 'Error',
                    pnotify_text: json.error,
                    pnotify_type: 'error'
            });
    }

On the other hand if we found 'info field' we should display confirmation message:

    else if (typeof json.info !== 'undefined') {
        $.pnotify({
            pnotify_title: 'Success',
                    pnotify_text: json.info,
                    pnotify_opacity: .8
            });

In this case we should also clear form controls to allow user to perform next action and protect him from sending the same action twice by accident:

           
            clearForm();

Thats all:

    }
}

Graceful Degradation

If user have javascript disabled he still can use the form. He just can't make the form validation on a client side and get results without refreshing web page. To achieve graceful degradation we have to make sure that form tag has specified action attribute and this attribute is the same as ajax() function url attribute.

Server can easily detect that client can't handle javasript because in this case request 'accept' header contains different value (xhtml+text) instead (application/javascript) We can detect this situation and provide response that can e easily interpreted by such a browser.

Note on the style

Using this approach we can completely eliminate use of hidden fields. Hidden fields were user to bypass state of some values from one page to another. Since we are doing everything on one page using ajax calls we don't need them anymore. Hidden fields in general are very bad practice because they make html markup harder to understand, hide form user some important pieces of information and create complicated protocol between client and server. This is why they should be avoided everywhere where it's possible.

-- MmNowotka - 17-Sep-2011

Edit | Attach | Watch | Print version | History: r1 | Backlinks | Raw View | WYSIWYG | More topic actions
Topic revision: r1 - 2011-09-17 - unknown
 
    • Cern Search Icon Cern Search
    • TWiki Search Icon TWiki Search
    • Google Search Icon Google Search

    ArdaGrid All webs login

This site is powered by the TWiki collaboration platform Powered by PerlCopyright & 2008-2022 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
or Ideas, requests, problems regarding TWiki? use Discourse or Send feedback