AutoComplete
Many AJAX javascript libraries offer functionality for providing
autocompletion whereby a selectlist of potentially matching results is
displayed as the user types. The AutoComplete helper aims
to simplify returning acceptable responses to such methods.
Since not all JS libraries implement autocompletion in the same way, the
AutoComplete helper provides some abstract base
functionality necessary to many libraries, and concrete implementations
for individual libraries. Return types are generally either JSON arrays
of strings, JSON arrays of arrays (with each member array being an
associative array of metadata used to create the selectlist), or HTML.
Basic usage for each implementation is the same:
_helper->autoCompleteDojo($data);
// Or explicitly:
$response = $this->_helper->autoCompleteDojo
->sendAutoCompletion($data);
// Or simply prepare autocompletion response:
$response = $this->_helper->autoCompleteDojo
->prepareAutoCompletion($data);
}
}
]]>
By default, autocompletion does the following:
Disables layouts and ViewRenderer.
Sets appropriate response headers.
Sets response body with encoded or formatted autocompletion data.
Sends response.
Available methods of the helper include:
disableLayouts() can be used to disable layouts and
the ViewRenderer. Typically, this is called within
prepareAutoCompletion().
encodeJson($data, $keepLayouts = false) will encode
data to JSON, optionally enabling or disabling layouts.
Typically, this is called within
prepareAutoCompletion().
prepareAutoCompletion($data, $keepLayouts = false)
is used to prepare data in the response format necessary for the
concrete implementation, optionally enabling or disabling
layouts. The return value will vary based on the implementation.
sendAutoCompletion($data, $keepLayouts = false)
is used to send data in the response format necessary for the
concrete implementation. It calls
prepareAutoCompletion(), and then sends the
response.
direct($data, $sendNow = true, $keepLayouts =
false) is used when calling the helper as a method of
the helper broker. The $sendNow flag is used to
determine whether to call sendAutoCompletion() or
prepareAutoCompletion(), respectively.
Currently, AutoComplete supports the Dojo and Scriptaculous
AJAX libraries.
AutoCompletion with Dojo
Dojo does not have an AutoCompletion widget per se, but has two
widgets that can perform AutoCompletion: ComboBox and
FilteringSelect. In both cases, they require a data store that
implements the QueryReadStore; for more information on these topics, see the dojo.data
documentation.
In Zend Framework, you can pass a simple indexed array to the
AutoCompleteDojo helper, and it will return a JSON response suitable
for use with such a store:
_helper->autoCompleteDojo($data);
]]>
AutoCompletion with Dojo Using Zend MVC
AutoCompletion with Dojo via the Zend MVC requires several
things: generating a form object for the ComboBox on which you
want AutoCompletion, a controller action for serving the
AutoCompletion results, creating a custom QueryReadStore to connect
to the AutoCompletion action, and generation of the javascript
to use to initialize AutoCompletion on the server side.
First, let's look at the javascript necessary. Dojo offers a
complete framework for creating OOP javascript, much as Zend
Framework does for PHP. Part of that is the ability to create
pseudo-namespaces using the directory hierarchy. We'll create a
'custom' directory at the same level as the Dojo directory
that's part of the Dojo distribution. Inside that directory,
we'll create a javascript file, TestNameReadStore.js, with the
following contents:
This class is simply an extension of Dojo's own QueryReadStore,
which is itself an abstract class. We simply define a method by
which to request, and assigning it to the 'test' element.
Next, let's create the form element for which we want
AutoCompletion:
_form) {
$this->_form = new Zend_Form();
$this->_form->setMethod('get')
->setAction(
$this->getRequest()->getBaseUrl() . '/test/process'
)
->addElements(array(
'test' => array('type' => 'text', 'options' => array(
'filters' => array('StringTrim'),
'dojoType' => array('dijit.form.ComboBox'),
'store' => 'testStore',
'autoComplete' => 'false',
'hasDownArrow' => 'true',
'label' => 'Your input:',
)),
'go' => array('type' => 'submit',
'options' => array('label' => 'Go!'))
));
}
return $this->_form;
}
}
]]>
Here, we simply create a form with 'test' and 'go' methods. The
'test' method adds several special, Dojo-specific attributes:
dojoType, store, autoComplete, and hasDownArrow. The dojoType is
used to indicate that we are creating a ComboBox, and we will
link it to a data store (key 'store') of 'testStore' -- more on
that later. Specifying 'autoComplete' as FALSE tells
Dojo not to automatically select the first match, but instead show a list of
matches. Finally, 'hasDownArrow' creates a down arrow similar to
a select box so we can show and hide the matches.
Let's add a method to display the form, as well as an end point
for processing AutoCompletion:
view->form = $this->getForm();
}
public function autocompleteAction()
{
if ('ajax' != $this->_getParam('format', false)) {
return $this->_helper->redirector('index');
}
if ($this->getRequest()->isPost()) {
return $this->_helper->redirector('index');
}
$match = trim($this->getRequest()->getQuery('test', ''));
$matches = array();
foreach ($this->getData() as $datum) {
if (0 === strpos($datum, $match)) {
$matches[] = $datum;
}
}
$this->_helper->autoCompleteDojo($matches);
}
}
]]>
In our autocompleteAction() we do a number of
things. First, we look to make sure we have a post request, and
that there is a 'format' parameter set to the value 'ajax';
these are simply to help reduce spurious queries to the action.
Next, we check for a 'test' parameter, and compare it against
our data. (I purposely leave out the implementation of
getData() here -- it could be any sort of data
source.) Finally, we send our matches to our AutoCompletion
helper.
Now that we have all the pieces on the backend, let's look at
what we need to deliver in our view script for the landing page.
First, we need to setup our data store, then render our form,
and finally ensure that the appropriate Dojo libraries --
including our custom data store -- get loaded. Let's look at the
view script, which comments the steps:
form ?>
headStyle()->captureStart() ?>
@import "baseUrl()
?>/javascript/dijit/themes/tundra/tundra.css";
@import "baseUrl() ?>/javascript/dojo/resources/dojo.css";
headStyle()->captureEnd() ?>
headScript()
->setAllowArbitraryAttributes(true)
->appendFile($this->baseUrl() . '/javascript/dojo/dojo.js',
'text/javascript',
array('djConfig' => 'parseOnLoad: true'))
->captureStart() ?>
djConfig.usePlainJson=true;
dojo.registerModulePath("custom","../custom");
dojo.require("dojo.parser");
dojo.require("dojox.data.QueryReadStore");
dojo.require("dijit.form.ComboBox");
dojo.require("custom.TestNameReadStore");
headScript()->captureEnd() ?>
]]>
Note the calls to view helpers such as headStyle and headScript;
these are placeholders, which we can then render in the HTML
head section of our layout view script.
We now have all the pieces to get Dojo AutoCompletion working.
AutoCompletion with Scriptaculous
Scriptaculous
expects an HTML response in a specific format.
The helper to use with this library is 'AutoCompleteScriptaculous'.
Simply provide it an array of data, and the helper will create an
HTML response compatible with Ajax.Autocompleter.