Javascript support

Javascript support – Client-side validation and Javascript-backed elements

Overview

If client-side validation in QuickForm2 doesn't work, you probably did not include the file it depends upon.

While HTML_QuickForm implemented client-side validation and provided several Javascript-backed elements, support for Javascript there was quite limited. Basically, all scripts were inlined and there were some rudimentary checks to prevent outputting the same (library) code twice.

This was addressed in HTML_QuickForm2, Javascript handled by the package is logically split into several parts:

  1. Libraries;
  2. Form setup code;
  3. Inline script.

A page containing two Javascript-backed forms will look like this:


...
[JS libraries used by form1 and form2]
...
[form1 html, including inline javascript]
[form1 setup code]
...
[form2 html, including inline javascript]
[form2 setup code]
...

An instance of HTML_QuickForm2_JavascriptBuilder takes care of libraries and setup code, inline code can be added using HTML_QuickForm2_Element_Script element.

The new approach allows keeping the code that does not change (libraries) in external *.js files, instead of bloating the resultant HTML. If form's structure does not change either, generated form setup code may also be moved to an external file. Only code that changes from one request to the other (e.g. setting the element's value) has to be inlined.

Javascript builder class

HTML_QuickForm2_JavascriptBuilder is used together with renderers. Form Javascript is generated in the course of HTML_QuickForm2::render() call, so that call is mandatory if you are using Javascript in your form.

Javascript library files are registered via addLibrary(). You will only need to call this directly if you create custom Elements or Rules or maybe want to override validation behaviour. Correct $webPath and $absPath need to be provided so that later call to getLibraries() can generate both links to external files and inline code.

Form setup code usually contains client-side validation rules added with addRule() and element setup code added with addElementJavascript(), the latter dealing with event handlers necessary for element behaviour. For example, built-in Hierselect element adds onchange handlers for contained selects, onreset handler for a containing form and onload handler for window. Once again, you will only directly call addElementJavascript() if you are creating a custom element or want to add some custom setup code. You don't need to call addRule() at all, as it is done automatically if you added a Rule to an Element with HTML_QuickForm2_Rule::CLIENT flag set. Javascript added by these two methods is later returned by getFormJavascript() method which is usually called automatically by a renderer. It is also possible to use separate getSetupCode() and getValidator() methods.

HTML_QuickForm2_JavascriptBuilder also contains a static helper method encode() which encodes a PHP value as a Javascript literal. This is similar to json_encode(), but does not enforce UTF-8 charset.

Including JS library files

There are currently three library files installed with the package:

quickform.js
Helper methods and validation library.
quickform-hierselect.js
Support functions for hierselect elements.
quickform-repeat.js
Support functions for repeat elements.

Human-readable versions of these files are installed into HTML_QuickForm2/js directory under PEAR's data_dir and minified versions are installed into HTML_QuickForm2/js/min. If you have trouble finding where data_dir is, you can use config-show command of PEAR installer.

While form setup code and inline Javascript is automatically added to output when rendering a form, libraries are not added automatically. The main reasons for this are

  1. Links to the library files are usually placed in <head></head> section of HTML document, while renderer outputs only the form itself.
  2. There is no reliable way to generate <script src="..."></script> tags referencing installed *.js files. Inlining the code works for package examples but leads to useless bloat in most other circumstances.

Libraries should be added to a page before outputting forms and definitely before outputting form setup code. Both inline scripts and form setup code can contain calls to library functions and changes to library properties.

It is possible to just copy *.js files from PEAR's data_dir to a directory accessible via HTTP and manually add necessary <script src="..."></script> tags to the page. You will however need to manually update this list if you decide to use a new Element or a new Rule which requires an additional Javascript file. A better approach is to let HTML_QuickForm2_JavascriptBuilder keep track of the used libraries and rely on its getLibraries() method to include them.

There are two ways to include the library code in your page using HTML_QuickForm2_JavascriptBuilder::getLibraries():

  • Inline the libraries, including their contents into the page

    <?php
    require_once 'HTML/QuickForm2/Renderer.php';

    $renderer HTML_QuickForm2_Renderer::factory('default');
    $form->render($renderer);

    echo 
    $renderer->getJavascriptBuilder()->getLibraries(truetrue);
    echo 
    $renderer;
    ?>

    This is the approach taken by package example files as it is guaranteed to work with no additional steps required from the user. It may also be useful while developing new package-related *.js files as you won't have problems with browser caching them.

  • Copy/symlink *.js files installed with the package to some directory under your website's document root and provide this information to JavascriptBuilder:

    <?php
    require_once 'HTML/QuickForm2/Renderer.php';
    require_once 
    'HTML/QuickForm2/JavascriptBuilder.php';

    $renderer HTML_QuickForm2_Renderer::factory('default');
    // Here '/path/to/libraries' is whatever directory available via HTTP you copied libraries to
    $renderer->setJavascriptBuilder(new HTML_QuickForm2_JavascriptBuilder('/path/to/libraries'));
    $form->render($renderer);

    // This will output necessary <script src="/path/to/libraries/..."></script> tags
    foreach ($renderer->getJavascriptBuilder()->getLibraries() as $link) {
        echo 
    $link "\n";
    }
    echo 
    $renderer;
    ?>

The latter approach is obviously recommended for production use.

If your page contains several forms, you'll only need to have one set of libraries for them. Use the same instance of HTML_QuickForm2_JavascriptBuilder when rendering all forms.

dualselect.php example shows among other things how to create a custom element with an additional JS library.

Outputting form setup code

Form setup code is returned by HTML_QuickForm2_JavascriptBuilder::getFormJavascript(). This method is automatically called by built-in Renderers and thus the code is included in their output.

Note that HTML_QuickForm2's Javascript library does not use DOMContentLoaded event like jQuery and similar libraries do:

$(document).ready(function() {
    // some code that will be run when complete DOM tree is built
});

Instead, form setup code is run immediately and should be output after the form, when form's DOM tree is already available.