Ajax auto completion

Table of Contents

  1. Preparation
  2. The Ajax Provider
Auto completion is a very common use for asynchronous sever requests and this tutorial is going to show you how fancy stuff like this demo here can easily be achieved with SGL and the Scriptaculous javascript framework.

The new release 0.6.2 of SGL offers its own easy and flexible AJAX handling. Therefore you're more independent of PEAR's heavy weight HTML_AJAX.

Preparation

You can download the code for the demo module at the bottom of the page.

OK, lets start with a module called autocomplete. For the database stuff (including example data) please revert to the downloads.

Create a new manager AutocompleteMgr in your classes directory:

class AutocompleteMgr extends SGL_Manager
{
    public function AutocompleteMgr()
    {
        parent::SGL_Manager();
        $this->template         = 'autocomplete.html';
        $this->_aActionsMapping = array(
            'list'  => array('list')
        );
    }
    public function validate($req, &$input)
    {
        $this->validated    = true;
        $input->action      = 'list';
    }
    public function display($output)
    {
        $output->addJavascriptFile('js/scriptaculous/lib/prototype.js');
        $output->addJavascriptFile('js/scriptaculous/src/effects.js');
        $output->addJavascriptFile('js/scriptaculous/src/controls.js');
    }
    public function _cmd_list(&$input, &$output)
    {
        $output->template = $this->template;
    }
}

The manager is just needed to tell SGL which JS files and templates to load. The template itself only consists of a little HTML and JS to initialise scriptaculous' Ajax.Autocompleter. At the moment we are good to go with following lines in autocomplete.html:

<style type="text/css">
    div.autocomplete {
        position:absolute;
        width:250px;
        background-color:white;
        border:1px solid #888;
        margin:0px;
        padding:0px;
    }
    div.autocomplete ul {
        list-style-type:none;
        margin:0px;
        padding:0px;
    }
    div.autocomplete ul li.selected { background-color: #ffb;}
    div.autocomplete ul li {
        list-style-type:none;
        display:block;
        margin:0;
        padding:2px;
        cursor:pointer;
    }
    span.indicator img {
        vertical-align: bottom;
    }
</style>
<form>
    <label for="name">Search for names:</label>
    <input type="text" name="name" id="name" autocomplete="off" />
    <span id="indicator" style="display: none" class="indicator">
        <img src="{t.webRoot}/themes/{t.theme}/images/loading.gif" />
    </span>
    <div id="name_choices" class="autocomplete"></div>
    <script type="text/javascript" language="javascript">
            new Ajax.Autocompleter(
                    'name',
                    'name_choices',
                    makeUrl({module: 'autocomplete', action: 'getNames'}),
                    {
                        indicator: 'indicator',
                        paramName: 'string',
                        method: 'get',
                    }
            );
    </script>
</form>

For details about Ajax.Autocompleter please have a look at the scriptaculous website. All you have to know right now is, that it expects an (unordered) list as response, which has to be generated by the server. The indicator is optional, but of importance, as it gives feedback to the client and he or she knows that something is loaded in the background.

The Ajax Provider

Though it is the actual core of SGL's AJAX handling, the provider is everything but complicated. Just have a look at AutocompleteAjaxProvider.php from the classes directory:

require_once SGL_LIB_DIR . '/SGL/AjaxProvider.php';
require_once 'DB/DataObject.php';
class AutocompleteAjaxProvider extends SGL_AjaxProvider
{
    public function getNames()
    {
        $this->responseFormat = SGL_RESPONSEFORMAT_HTML;
        $req    = SGL_Request::singleton();
        $limit  =  10;
        $string = $req->get('string');
        $name   = DB_DataObject::factory($this->conf['table']['name']);
        $name->whereAdd('first_name LIKE \'%'.$string.'%\'');
        $name->whereAdd('last_name LIKE \'%'.$string.'%\'','OR');
        $name->limit(0, $limit);
        $name->find();
        $res    = '';
        while ($name->fetch()) {
            $res .= "<li>{$name->last_name}, {$name->first_name}</li>";
        }
        return '<ul>'.$res.'</ul>';
    }
}

All the provider does, is generating a list from the DB result and returning it. However, for more complex DB requests it might be appropriate to factor out some stuff into an DAO.

That's it. If the module is complete (don't forget conf.ini and data/) you can register it with SGL and are ready to test it.

Attachments