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:
- attribute=>'user_id' property, set this to your actual field name in Database.
- 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.
There is problem with your MyAutoComplete.php file...
Nothing major but problem with your comment...
This post should have more "likes" - thank you!
That was VERY helpful - exactly what I needed!
Indeed as was mentioned - theres problem with comments on lines 30,31.
Don't actually fully understand what should be uncommented and why. But uncommenting line 30 gives good result ))))
Add function to AccessRules
If you have defined accessRules() in your controller add the newly created function to it like,
array('allow', // allow authenticated user to perform 'actions' => array('usersAutocomplete'), 'users' => array('@'), ),
Reset id and label on change
I added this :
if (!isset($this->options['change'])) { $this->options['change'] = 'js:function(event, ui) { if (!ui.item) { $("#' . $id . '").val(""); $("#' . $attr_id . '").val(""); } }'; }
Can't store ID
I follow your example but I get an error:
Where is the problem? Please help...
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.