Simple way to use AutoComplete using different ID and Display Value

Usually when we use AutoComplete in a project, We need to show "title" or "name" in the list, while when the form is posted, we need some sort of integer ID referring to the selected value. Out of the Box, CJuiAutoComplete widget doesn't provides different display text and post values.

Here's the simplest way to do it:

Step 1: Extend CJuiAutoComplete

Create a new file /protected/extensions/MyAutoComplete.php and put the following content in it:

<?php
Yii::import("zii.widgets.jui.CJuiAutoComplete");
class myAutoComplete extends CJuiAutoComplete
{

	/**
	 * Run this widget.
	 * This method registers necessary javascript and renders the needed HTML code.
	 */
	public function run()
	{
		list($name,$id)=$this->resolveNameID();
    
    // Get ID Attribute of actual hidden field containing selected value
    $attr_id = get_class($this->model).'_'.$this->attribute;

		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::textField($name,$this->value,$this->htmlOptions);		  
      echo CHtml::activeHiddenField($this->model, $this->attribute);
		}else {
			echo //CHtml::textField($name,$this->value,$this->htmlOptions);		  
//CHtml::hiddenField($name,$this->value,$this->htmlOptions);		  
		}

		if($this->sourceUrl!==null)
			$this->options['source']=CHtml::normalizeUrl($this->sourceUrl); 
		else
			$this->options['source']=$this->source;

    // Modify Focus Event to show label in text field instead of value
    if (!isset($this->options['focus'])) {
      $this->options['focus'] = 'js:function(event, ui) {
          $("#'.$id.'").val(ui.item.label);
          return false;
        }';    
    }

    if (!isset($this->options['select'])) {
      $this->options['select'] = 'js:function(event, ui) {
            $("#'.$id.'").val(ui.item.label);
            $("#'.$attr_id.'").val(ui.item.id);
        }';
    }
    
    $options=CJavaScript::encode($this->options);
    //$options = $this->options;
    
		$js = "jQuery('#{$id}').autocomplete($options);";

		$cs = Yii::app()->getClientScript();
		$cs->registerScript(__CLASS__.'#'.$id, $js);
	}
}
Step 2: Add method to return list in Model
  • Open the appropriate Model, for e.g. if your AutoComplete shows a list of Users, Open protected/models/users.php, if it shows products, open up models/products.php
  • Add the following method to your Model
// Important: The Method below must return each record with id, label keys. See how i've wrote the query "title AS label" below.

/* Result should be in this format
  array(
    'id'=>4,
    'label'=>'John',
  ),
  array(
    'id'=>3,
    'label'=>'Grace',
  ),
  array(
    'id'=>5,
    'label'=>'Matt',
  ),

*/
	public static function usersAutoComplete($name='') {

		// Recommended: Secure Way to Write SQL in Yii 
    $sql= 'SELECT id ,title AS label FROM users WHERE title LIKE :name';
		$name = $name.'%';
		return Yii::app()->db->createCommand($sql)->queryAll(true,array(':name'=>$name));

		// Not Recommended: Simple Way for those who can't understand the above way.
    // Uncomment the below and comment out above 3 lines 
    /*
    $sql= "SELECT id ,title AS label FROM users WHERE title LIKE '$name%'";
		return Yii::app()->db->createCommand($sql)->queryAll();
    */
    
	}
Step 3: Add autocomplete action to controller

Create an action in your controller to return list of suggestions in JSON Format, as follows:

public function actionUsersAutocomplete() {
		$term = trim($_GET['term']) ;

		if($term !='') {
			// Note: Users::usersAutoComplete is the function you created in Step 2
      $users =  Users::usersAutoComplete($term);
			echo CJSON::encode($users);
			Yii::app()->end();
    }
  }
Step 4: Add widget to view

Finally, Open the view file and add the following widget where you need the AutoComplete Text Field. Notes:

  1. attribute=>'user_id' property, set this to your actual field name in Database.
  2. name=>'user_autocomplete', try to make a habit of naming the autocomplete field ending with _autocomplete or use your own convention.
// Note: ext.MyAutoComplete is equivalent/shortcut to application.extensions.MyAutoComplete
// This means Look for protected/extensions/MyAutoComplete.php file
$this->widget('ext.myAutoComplete', array(
    'model'=>$model,
    'attribute'=>'user_id',
    'name'=>'user_autocomplete',
    'source'=>$this->createUrl('orders/usersAutoComplete'),  // Controller/Action path for action we created in step 4.
    // additional javascript options for the autocomplete plugin
    'options'=>array(
        'minLength'=>'0',
    ),
    'htmlOptions'=>array(
        'style'=>'height:20px;',
    ),        
));

Done!

Now when you post the form, you get the ID of selected value. Let me know if someone is still unable to follow this and i'll be glad to help. Also please comment if you think there's a better and simpler way to do this.

6 0
13 followers
Viewed: 63 568 times
Version: 1.1
Category: Tutorials
Written by: raheelk2k
Last updated by: raheelk2k
Created on: Aug 1, 2012
Last updated: 12 years ago
Update Article

Revisions

View all history

Related Articles