Custom Autocomplete Display and Value Submission

 

Introduction

How many of us has wondered how to create an autocomplete that will display the names of a related models but do require the id of that selected name to be submitted for model creation/update?

I was looking around wiki and found that was no approach as the one I did so I guessed this is worth to write.

Requirements

For our example, I want to be able to:

  • Have an autocomplete field in our form
  • Once user selects an item in the dropdown list and fill a hidden box with the id of the selected item for submission

Making the right choice

To setup the autocomplete was a very straight forward operation, but I couldn’t figure out how to get values from a custom JSON response and then fill the correspondent hidden fields.

CAutoComplete does has a way to do it, but I wanted to use CJuiAutoComplete to get all the cool features of its JQuery Ui and by looking at his code there was no method chain, something that is required to work with custom JSON responses as we need to override some methods.

My Solution

After doing some research I decided to:

  1. extend from CJuiAutoComplete
  2. include the required property for method chain and modify its ‘run’ function
  3. then initialize the newly created property with the javascript functions that handle my custom JSON

Extending from CJuiAutoComplete and make required modifications

Very simple, we are going to add a methodChain property and modify the run function to include it (zii is not a major concern to Yii, but main developers should think about this minor change).

class myAutoComplete extends CJuiAutoComplete
{
    /**
     * @var string the chain of method calls that would be appended at the end of the autocomplete constructor.
     * For example, ".result(function(...){})" would cause the specified js function to execute
     * when the user selects an option.
     */
    public $methodChain;
    /**
     * Run this widget.
     * This method registers necessary javascript and renders the needed HTML code.
     */
    public function run()
    {
        list($name,$id)=$this->resolveNameID();
        if(isset($this->htmlOptions['id']))
            $id=$this->htmlOptions['id'];
        else
            $this->htmlOptions['id']=$id;
        if(isset($this->htmlOptions['name']))
            $name=$this->htmlOptions['name'];
        if($this->hasModel())
            echo CHtml::activeTextField($this->model,$this->attribute,$this->htmlOptions);
        else
            echo CHtml::textField($name,$this->value,$this->htmlOptions);
        if($this->sourceUrl!==null)
            $this->options['source']=CHtml::normalizeUrl($this->sourceUrl);
        else
            $this->options['source']=$this->source;
        $options=CJavaScript::encode($this->options);
        $js = "jQuery('#{$id}').autocomplete($options){$this->methodChain};";
        $cs = Yii::app()->getClientScript();
        $cs->registerScript(__CLASS__.'#'.$id, $js);
    }
}

Using our widget

Now that we have our beautiful widget that handles method chain in our Autocomplete, let’s assume a couple of things:

  • We saved our class onto a folder in our application -ie protected/extensions
  • We have a hidden INPUT HTML element with model’s attribute_id
  • We have created an action on our testController named autocomplete that returns a JSON object on the following format:
// This function will echo a JSON object
// on this format:
// [{id:id, name: 'name'}]
public function actionAutocomplete(){
      $res = array();
      $term = Yii::app()->getRequest()->getParam('term', false);
      if ($term)
      {
         // test table is for the sake of this example
         $sql = 'SELECT id, name FROM {{test}} where LCASE(name) LIKE :name';
         $cmd = Yii::app()->db->createCommand($sql);
         $cmd->bindValue(":name","%".strtolower($term)."%", PDO::PARAM_STR);
         $res = $cmd->queryAll();
      }
      echo CJSON::encode($res);
      Yii::app()->end();
}

We have everything, let’s use our widget in our view:

// REMEMBER, we have a hidden
// input HTML element with model's attribute_id
<?php echo $form->hiddenField($model, 'attribute_id'); ?>
<?php
// ext is a shortcut for application.extensions
$this->widget('ext.myAutoComplete', array(
    'name' => 'test_autocomplete',
    'source' => $this->createUrl('test/autocomplete'),
// attribute_value is a custom property that returns the
// name of our related object -ie return $model->related_model->name
    'value' => $model->isNewRecord ? '': $model->attribute_value,
    'options' => array(
        'minChars'=>3,
        'autoFill'=>false,
        'focus'=> 'js:function( event, ui ) {
            $( "#test_autocomplete" ).val( ui.item.name );
            return false;
        }',
        'select'=>'js:function( event, ui ) {
            $("#'.CHtml::activeId($model,'attribute_id').'")
            .val(ui.item.id);
            return false;
        }'
     ),
    'htmlOptions'=>array('class'=>'input-1', 'autocomplete'=>'off'),
    'methodChain'=>'.data( "autocomplete" )._renderItem = function( ul, item ) {
        return $( "<li></li>" )
            .data( "item.autocomplete", item )
            .append( "<a>" + item.name +  "</a>" )
            .appendTo( ul );
    };'
));
?>

Done! Just make sure that when you do submit your form, you get the value from the hidden field instead of the autocomplete element :)

Final Notes

I do not know if there are other ways of doing the same thing (apart from pure Javascript) to have the same results. If you know, with CJuiAutoComplete widget, let us know here.

Hope you find this useful.

Cheers

7 comments

  1. Antonio Ramirez   •     Author

    Very good one! and very good site…

    Thanks for sharing

  2. SurenD   •  

    Hey Thanks very much for this Post. I used this successfully in my development work. :) keep up the good work and wish you all the best.

  3. JC   •  

    Hi, this is very helpful and exactly what I was looking for. However, when I try to implement it, I get undefined variable: model. I had it working fine with the regular CJuiAutocomplete though. Where do I define the $model variable? Thanks – I’m a newbie to yii/php.

  4. raelo   •  

    Great tutorial. It worked for me!

  5. chansana   •  

    It has an error! as soon as i type any string then its display undefined value in textfield. when i debug its, it seem working coz, it show ID and Vaule pairs of the attribute. plz help!

  6. Antonio Ramirez   •     Author

    The Autocomplete is pretty long… I am not longer supporting it… apologies

Add Comment Register



Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>