Using CAutoComplete to display one value and submit another

Have you ever wanted to use an auto-complete field to look up a user or some other data, but want the database ID of that user or data returned as well so that you can more easily perform some function when the form is submitted? Thanks to Yii's CAutoComplete widget and jQuery's Autocomplete plugin, it's really quite simple.

In overview, assuming that you are looking up and retrieving your data set for the autocomplete widget from a database, there are basically three steps:

  1. write action code to query and produce a properly-formatted list of autocomplete items
  2. embed the CAutoComplete widget into your view file and configure it to reference a controller's action
  3. chain an extra "result" method onto the end of the autocomplete widget to tell it to put your ID value into a hidden field when an autocomplete item is selected.

While not all features and options are explored, here is a sample of how to implement the above steps.

In your controller, create a new action to handle autocomplete lookup queries, like so:

<?php
    //Action
    public function actionAutoCompleteLookup()
    {
       if(Yii::app()->request->isAjaxRequest && isset($_GET['q']))
       {
            /* q is the default GET variable name that is used by
            / the autocomplete widget to pass in user input
            */
          $name = $_GET['q']; 
                    // this was set with the "max" attribute of the CAutoComplete widget
          $limit = min($_GET['limit'], 50); 
          $criteria = new CDbCriteria;
          $criteria->condition = "name LIKE :sterm";
          $criteria->params = array(":sterm"=>"%$name%");
          $criteria->limit = $limit;
          $userArray = User::model()->findAll($criteria);
          $returnVal = '';
          foreach($userArray as $userAccount)
          {
             $returnVal .= $userAccount->getAttribute('name').'|'
                                         .$userAccount->getAttribute('user_id')."\n";
          }
          echo $returnVal;
       }
    }

There are a few things to notice here:

  1. A pipe "|" delimiter is used and natively recognized by jQuery to split a returned item into parts
  2. Each autocomplete item is terminated by a newline character, or "\n"; note that the newline char must be enclosed in double quotes (") not single quotes(')
  3. The resulting items are "returned" to the javascript autocomplete code through an `echo` statement; using `return` will not work

Now, in your view file, embed the following code within your form:

<?php $this->widget('CAutoComplete',
          array(
                         //name of the html field that will be generated
             'name'=>'user_name', 
                         //replace controller/action with real ids
             'url'=>array('controller/action'), 
             'max'=>10, //specifies the max number of items to display
 
                         //specifies the number of chars that must be entered 
                         //before autocomplete initiates a lookup
             'minChars'=>2, 
             'delay'=>500, //number of milliseconds before lookup occurs
             'matchCase'=>false, //match case when performing a lookup?
 
                         //any additional html attributes that go inside of 
                         //the input field can be defined here
             'htmlOptions'=>array('size'=>'40'), 
 
             'methodChain'=>".result(function(event,item){\$(\"#user_id\").val(item[1]);})",
             ));
    ?>
    <?php echo CHtml::hiddenField('user_id'); ?>

Take note of the methodChain attribute being used. MethodChain essentially appends (or chains) a javascript method to the end of the AutoComplete javascript code that will be generated. This particular method is the result method, which fires when an autocomplete item is selected. The code inside of the result function basically references the hidden field that was defined and assigns the 2nd part (part after the pipe) of the selected autocomplete data to that field.

Now that the basics are out of the way, review the documentation for Yii's CAutoComplete widget and jQuery's Autocomplete plugin to find more ways to customize and use autocomplete functionality in your Yii project.

Links

Chinese version

Total 11 comments:

#138
die()
by KJedi at 3:21am on March 25, 2009.

Using die() is incorrect because you terminate the application, it will not be finalized correctly. Don't use it in actions. It's nearly the same as switching computer off by switching power :)

#141
Re: die()
by luoshiben at 3:52am on March 25, 2009.

It is true that in a normal scenario you would almost never use die() in an action, except maybe to halt the program on some fatal error. However, in this case as the action is being used in a somewhat unorthodox way to actually echo content to the browser from an asynchronous request, using die() ensures that no further operations "mess up" the content returned from an Ajax request. It is probably unnecessary in truth, but better safe than sorry.

#163
Re: die()
by WilsonC at 4:46am on April 2, 2009.

To avoid using die():

Create a view for the actual output parsing and use renderPartial() to render the view.

#310
Object of class CAutoComplete could not be converted to string
by foe#1 at 5:05pm on May 22, 2009.

To rid rid if the error use: remove echo as in code <?php $this->widget('CAutoComplete', array( ...) /code

#616
Tip for users of RBAC
by horacio.segura at 8:16am on August 26, 2009.

add the action at filter code

public function accessRules() { return array( array('allow', // allow all users to perform 'autoComplete' 'actions'=>array('autoCompleteLookup'), 'users'=>array('*'), ), );

}

/code

#621
easy
by emix at 8:50am on August 28, 2009.

one can end action execution by typing just

return;

instead of end();

#855
Clearing a value when no result is returned
by gsatir at 12:52am on December 2, 2009.

There is currently no way to get result() to be called if the lookup returns no results at all. Version 1.1 of the autocomplete plugin allows this -- but Yii is using 1.0.2. This means currently you cannot show "no result returned" without a page refresh or clear your hidden ID field.

Here is a hack that will approximate the clearing of a hidden ID field when no result is returned. The trick is to use "extraParams" to clear the field before each lookup. Simply add this to your widget array: 'extraParams'=>array( 'xparam'=>'js:function(){ $("#user_id").val(0); }' ),

When the page is submitted, you can check user_id to see if there is a zero there and know that the autocomplete failed to find anything.

#1033
How to display "complex" data when showing results in the dropdownbox?
by isidro at 1:11am on January 22, 2010.

I would like to have a small image in the dropdown so I added it in the actionAutoCompleteLookup returnVal, but when user clicks the line he wants to select the text box shows the entire Html of the selected row. I would like to have some sort of a template to show in the dropdownlist and a single text field to display in the textbox when the user selects one option. Is this possible as is? What's the best procedure to override the autocomplete js script?

#1040
Made it!
by isidro at 1:26am on January 23, 2010.

My dear friends, width a couple more of study hours I find out how to it, I mean how to format the dropdown list of results. It turns out that … her … it is quite easy!

Just for the sake of letting you understand what I want, I'll drop a line or two about it. I would like that the result terms in the autocomplete dropdown list would show a small image along side with some text (searched for).

My first attempt was to add a small image to the $returnVal variable in the actionAutoCompleteLookup action in my controller like this:

foreach($isssArray as $iss) { $returnVal .= 'myImage.jpg'.' '.$iss->getAttribute('name').'|'.$iss->getAttribute('id')."\n"; }

This worked exactly the way I wanted but when the user selected one item in the drop down list it would show in the textbox with all the html of the image. So clearly I would need some sort of a way to separate the image from the searchable text.

It turns out that the widget already as this concept developed is just a matter of knowing how to use it!

There is a small change needed to be made in the returnVal shown above:

foreach($isssArray as $iss) { $returnVal .= $iss->getAttribute('name').'|'.$iss->getAttribute('id').'|'.'cristiano-ronaldo_normal.jpg'."\n"; }

This way the returned variable will be an array with 3 positions: 0=name (the field searched); 1=id of the register; 2=the image

Now is just a matter of telling the widget how to use this 3 elements of the result array!

This is done using the function formatItem (what a clever name, hugh!?).

'formatItem'=>"function(result, i, num, term) {return result2 +' '+ result0; }", 'methodChain'=>".result(function(event,item){\$(\"#Project_charitableOrganizationId\").val(item1);})",

As you can see, formatItem uses the position 2 and 0 of the returnValue (image folloed by the text) and methodChain defines the Id of the user selected row in the dropdown list.

Sorry for the long post, hope this will eventually help someone out there.

Best regards, Rui Isidro

#1601
Thx!
by gawronzo at 6:47am on June 21, 2010.

Thank you for helpful solution.

#1613
Not possible to use .live with this widget?
by mastawong at 6:59pm on June 22, 2010.

I have a form with a clone row function, I haven't done extensive experiments, but as far as I know, the only way to attach .live to autocomplete is to use the legacyautocomplete. If somebody has a better solution please do share

Your Comment:

You may enter comment using Markdown syntax.

Please login with your forum account.
Note: you must have at least ONE forum post with your account.