Zend Form, jQuery and autocomplete

When a component in a Framework is inflexible and difficult to maintain I believe is time to change way. ZendX_JQuery is powerful but not in the jQuery UI Autocomplete. After a thorough discussion about Autocomplete now we are at a dead end and we can only wait a new release. I prefer to change my way and go ahead.

This is my different solution for Autocomplete UI in Zend Form (without ZendX_JQuery).

First of all I extended the Zend_Form with a general class My_Form where I can set all defaults values of decorators, filters, validators, etc. Then I added my createMyAutoComplete method:

class My_Form extends Zend_Form
{
    // autcomplete extension of idfield
    private $_acExtension = "_ac";
 
    /*
     *  Create My AutoComplete Elements
     */
        public function createMyAutoComplete($id, $source, $params=array())
        {
            $idAc = $id . $this->_acExtension; // support element id for autocomplete
            // Create autocomplete element
            $this->addElement('text', $idAc, $params);
            // Create autocomplete hidden element (for id field)
            $this->addElement('hidden', $id, array('decorators' => $this->_noElementDecorator));
 
            // Add my custom Javascript functions to onLoad
            $view = $this->getView();
            $view->jQuery()->addOnLoad("MyAutocomplete.initialize('".$id."', '".$source."', '".$this->_acExtension."');");
 
            // return id of element Autocomplete to manage in group, add params, ecc...
            return $idAc;
        }

Above you can see the createMyAutoComplete method to generate an autocomplete field and its support field id. The parameters are:

  • $id = the id of the element
  • $source = the source where to read data for autocomplete
  • $params = like the params of the Zend_Form (see below)

As we seen the method creates two elements, for example, if we set $id=”idcity” we will have:

  • idcity, hidden field (our real id)
  • idcity_ac, text field (support field, only for autocomplete feature)

Remember to enable jQuery in the view otherwise the $view->jQuery->addOnLoad() above does not work!

Then you can call this method in the Forms:

class Application_Form_User_City extends My_Form
{
	public function init()
	{
	         // Set the method for the display form to POST
	         $this->setAttrib('id', 'user_city_form')
				->setAttrib('class', 'f1n120');
 
	        // element: Description
	        $this->addElement('text', 'description', array(
	            'label'      => $this->translate('Description'),
	            'attribs'      => array('size' => 35),
	            'required'   => true,
	            'filters'    => array('StringTrim'),
	            'validators' => array('NotEmpty'),
	        ));
 
	       // element: City
               $idAc = $this->createMyAutoComplete( "idcity",
                                         "/utility/search.city",
                                         // set params of element
                                         array(
                                             'label'    => $this->translate('City'),
                                             'attribs'    => array('size' => 25),
                                             'required' => true,
                                             'ignore'   => true, // to ignore when submit
                                             'filters'    => array('StringTrim'),
                                             'validators' => array('NotEmpty'),
                                         )
                );
	}
}

So we can create quickly an AutoComplete element and its hidden field.
Note: Add ignore attrib not submit this field. So we’ll submit directly the id.

To finish I created a custom Javascript to manage the autocomplete element.
Include this one in your Javascript file:

    MyAutocomplete = {
        initialize: function(idelement, mysource, acext)
        {
            eac = '#' + idelement + acext;
            eac_id = '#' + idelement;
 
            $(eac).autocomplete({
                minLength: 0,
                source: mysource,
                focus: function(event, ui) {
                    $(eac).val(ui.item.label);
                    return false;
                },
                select: function(event, ui) {
                    $(eac).val(ui.item.label);
                    $(eac_id).val(ui.item.value);
                    return false;
                }
            })
            .data( "autocomplete" )._renderItem = function( ul, item ) {
                return $( "<li></li>" )
                    .data( "item.autocomplete", item )
                    .append( "<a>" + item.label + "</a>" )
                    .appendTo( ul );
            };
        }
    }

The ajax response from the source must be an array with two fields: label and value.
This js function prepare data for every single item (_renderItem) and set the the value on the hidden field (eac_id). Need 3 parameters:

  • idelement – the real id
  • source – where to read data
  • acext – the extension of the autocomplete field

This parameters are set automatically in the above My_Form class.
Try it!