jQuery autocomplete Helper
I use jQuery becose is fast and concise Javascript library. My helper, jmycake, use jQuery for AJAX autocomplete.
Requirements:
- jQuery library (download from http://docs.jquery.com/Downloading_jQuery
- CakePHP 1.2 beta
- The helper code (see below)
Helper:
Helper Class:
<?php
<?php
class JmycakeHelper extends AppHelper {
var $helpers = array('Javascript');
var $output ='';
/*
* $idInput = ID dell'input text su cui fare l'autocomplete
* $modelSearch = Modello/NomeCampo nel quale cercare al stringa inserita nell'input
* $other = Array che contiene l'id del campo da aggiornare ed il nome del campo da prendere dal db
* $numResult = Numero di risultati da mostrare nella lista
* $strlen = Numero di caratteri dopo i quali iniziare le richieste dell'autocomplete
*/
function autocomplete($idInput,$modelSearch,$other=null,$numResult=7,$strlen=1) {
$fields= "";
$setBody = "";
$search = explode("/",$modelSearch);
if (is_array($other)) {
foreach ($other As $k => $v) {
$fields .= $v.',';
$setBody .= "$('#".$k."').val(".$v.");";
}
}
$fields .= $search[1];
$this->output.=$this->Javascript->codeBlock('
$("#'.$idInput.'").ready(function(){
$("#'.$idInput.'").attr("onkeyup","query_'.$idInput.'(this.value)");
$("#'.$idInput.'").attr("autocomplete","off");
$("#'.$idInput.'").after("<span id=\"span_'.$idInput.'\" class=\"autocomplete_live\"></span>");
});
function query_'.$idInput.'(txt) {
if(txt.length >= '.$strlen.') {
$.post("'.$this->webroot.$this->params["controller"].'/autocomplete", {query: txt, fields: "'.$fields.'", search: "'.$search[1].'", model: "'.$search[0].'", numresult: "'.$numResult.'", rand: "'.$idInput.'"}, function(data){
$("#span_'.$idInput.'").html("<ul id=\'ul_'.$idInput.'\' class=\'autocomplete_live\'>"+data+"</ul>");
$("#ul_'.$idInput.'").width($("#'.$idInput.'").width());
$("#span_'.$idInput.'>ul>li>a").keypress(function(e) {
pressedKey = e.charCode || e.keyCode || -1;
switch(pressedKey) {
case 38://up
position=position-1;
if (position<0) {
position=dimensione-1;
}
$("#span_'.$idInput.'>ul>li>a").eq(position).focus();
return false;
break;
case 40://down
position=position+1;
if (position>=dimensione) {position=0;}
$("#span_'.$idInput.'>ul>li>a").eq(position).focus();
return false;
break;
}
});
});
}
}
$("#'.$idInput.'").keypress(function(e) {
pressedKey = e.charCode || e.keyCode || -1;
dimensione=$("#span_'.$idInput.'>ul>li").size();
switch(pressedKey) {
case 38://up
$("#span_'.$idInput.'>ul>li>a").eq($("#span_'.$idInput.'>ul>li").size()-1).focus();
position = $("#span_'.$idInput.'>ul>li").size()-1;
break;
case 40://down
$("#span_'.$idInput.'>ul>li>a").eq(0).focus();
position=0;
break;
}
});
function set_'.$idInput.'('.$fields.') {
'.$setBody.'
$("#'.$idInput.'").val('.$search[1].');
$("#span_'.$idInput.'").html("");
}
');
return $this->output;
}
}
?>
?>
App Controller:
In app_controller.php add this function:
function autocomplete() {
if ($this->RequestHandler->isAjax() && $this->RequestHandler->isPost()) {
$fields = explode(",",$this->params['form']['fields']);
$results = $this->{$this->params['form']['model']}->findAll($this->params['form']['search'].' LIKE \'%'.$this->params['form']['query'].'%\'',$fields,$this->params['form']['search'].' ASC',$this->params['form']['numresult']);
$this->set('results',$results);
$this->set('fields',$fields);
$this->set('model',$this->params['form']['model']);
$this->set('input_id',$this->params['form']['rand']);
$this->set('search',$this->params['form']['search']);
$this->render('autocomplete','ajax','/common/autocomplete');
}
}
Autocomplete view:
And create the common view:View Template:
<?php
foreach ($results As $k=>$v) {
$value='';
foreach ($fields As $i =>$j) {
$value .= '"'.$v[$model][$j].'",';
}
echo "<li onclick='set_".$input_id."(".substr($value,0,strlen($value)-1).")'><a href='#'>".$v[$model][$search]."</a></li>";
}
?>
Save this view in view/common/autocomplete.ctp.
In you controller:
Add my herper in $helper array:
var $helpers = array('Html','Form','Javascript','Jmycake');
CSS:
Include this CSS style sheet in your layout:
@CHARSET "UTF-8";
.autocomplete_live {
background:#F0F0F0 none repeat scroll 0%;
clear:both;
cursor:pointer;
display:block;
margin:0px;
padding:0px;
z-index:9999;
}
.autocomplete_live ul {
clear:both;
display:block;
list-style-type:none;
margin:0px;
padding:0px;
position:absolute;
width:100%;
}
.autocomplete_live li {
background:#F0F0F0 none repeat scroll 0%;
border-bottom:1px solid #C0C0C0;
display:block;
height:25px;
list-style-type:none;
margin:0px;
padding:0px;
}
How-to insert autocomplete in you view:
If you have, for example, this view:
View Template:
<div class="Nation">
<h2>Nazioni</h2>
<?php echo $form->create('Nation', array('action' => 'test')); ?>
<?php echo $form->input('id',array("type"=>"hidden")); ?>
<?php echo $form->input('nation'); ?>
<?php echo $form->input('iso'); ?>
<?php echo $form->submit('GO'); ?>
<?php echo $form->end(); ?>
</div>
For add autocomplete input is easy and fast, add this code in you view:
<?php echo $jmycake->autocomplete('NationNation','Nation/nation',array('NationId'=>'id','NationIso'=>'iso')); ?>
The helper add on input id "NationNation" the autocomplete that make AJAX request on table "Nation" column name "nation" and, when you select the result, the helper set the value of input NationId with the table "id" and the input NationIso with the "iso" value found.
It's possibile to pass other 2 variable on my helper:
- $numResult: number of result that show in the view
- $strlen: minimum length of the insert text in the html input
It's all!.
Sorry for my english... :(

Now jQuery manages the onkeypress events => works in all tested browsers and no more pb with multiple instances.
<?php
class JmycakeHelper extends AppHelper {
var $helpers = array('Javascript');
var $output ='';
/*
* $idInput = ID dell'input text su cui fare l'autocomplete
* $modelSearch = Modello/NomeCampo nel quale cercare al stringa inserita nell'input
* $other = Array che contiene l'id del campo da aggiornare ed il nome del campo da prendere dal db
* $numResult = Numero di risultati da mostrare nella lista
* $strlen = Numero di caratteri dopo i quali iniziare le richieste dell'autocomplete
*/
function autocomplete($idInput,$modelSearch,$other=null,$numResult=7,$strlen=1) {
$fields= "";
$setBody = "";
$search = explode("/",$modelSearch);
if (is_array($other)) {
foreach ($other As $k => $v) {
$fields .= $v.',';
$setBody .= "$('#".$k."').val(".$v.");";
}
}
$fields .= $search[1];
$this->output.=$this->Javascript->codeBlock('
$("#'.$idInput.'").ready(function(){
$("#'.$idInput.'").keypress(query_'.$idInput.');
$("#'.$idInput.'").attr("autocomplete","off");
$("#'.$idInput.'").after("<span id=\"span_'.$idInput.'\" class=\"autocomplete_live\"></span>");
});
function query_'.$idInput.'() {
var txt=$("#'.$idInput.'").val();
if(txt.length >= '.$strlen.') {
$.post("'.$this->webroot.$this->params["controller"].'/autocomplete", {query: txt, fields: "'.$fields.'", search: "'.$search[1].'", model: "'.$search[0].'", numresult: "'.$numResult.'", rand: "'.$idInput.'"}, function(data){
$("#span_'.$idInput.'").html("<ul id=\'ul_'.$idInput.'\' class=\'autocomplete_live\'>"+data+"</ul>");
$("#ul_'.$idInput.'").width($("#'.$idInput.'").width());
$("#span_'.$idInput.'>ul>li>a").keypress(function(e) {
pressedKey = e.charCode || e.keyCode || -1;
switch(pressedKey) {
case 38://up
position=position-1;
if (position<0) {
position=dimensione-1;
}
$("#span_'.$idInput.'>ul>li>a").eq(position).focus();
return false;
break;
case 40://down
position=position+1;
if (position>=dimensione) {position=0;}
$("#span_'.$idInput.'>ul>li>a").eq(position).focus();
return false;
break;
}
});
});
}
}
$("#'.$idInput.'").keypress(function(e) {
pressedKey = e.charCode || e.keyCode || -1;
dimensione=$("#span_'.$idInput.'>ul>li").size();
switch(pressedKey) {
case 38://up
$("#span_'.$idInput.'>ul>li>a").eq($("#span_'.$idInput.'>ul>li").size()-1).focus();
position = $("#span_'.$idInput.'>ul>li").size()-1;
break;
case 40://down
$("#span_'.$idInput.'>ul>li>a").eq(0).focus();
position=0;
break;
}
});
function set_'.$idInput.'('.$fields.') {
'.$setBody.'
$("#'.$idInput.'").val('.$search[1].');
$("#span_'.$idInput.'").html("");
}
');
return $this->output;
}
}
?>
cakephp autocomple not working in chrome and ie browsers
Autocomplete is also included. Docs and many examples on site. I think that you should try it.
When I select an item from the list to edit, I get a screen, which has the entire screen filled with the background color of the header. That background is displayed for one or two seconds and then I get a 404 page not found. This all worked before I added the code for the autocomplete.
Here is what I have:
1. JmycakeHelper.php has been downloaded and put into the helpers folder of my app.
2. var $helpers = array('Html', 'Form', 'Javascript', 'Ajax', 'Jmycake'); has been added to my controller.
3. My default layout includes:
echo $html->css('jmy'); // the css from jmycake
echo $javascript ->link('jquery-1.4.js');
4. I've added this to my view:
echo $jmycake->autocomplete('opcode', 'Furnaceoperation/opcode', array('Id' => 'id', 'opcode' => 'opcode', 'value' => $furnarray[opcode],));
// 'value' => $furnarray[opcode] was added because the original data, which may be changed by user, came from calculations in the controller.
The part I'm not sure about is the "View Template" from the example. I created the following, but I'm not certain where to save it or what to name it:
<div class="Furnaceoperation">
<h2>Furnop</h2>
<?php echo $form->create('Furnaceoperation', array('action' => 'test')); ?>
<?php echo $form->input('id',array("type"=>"hidden")); ?>
<?php echo $form->input('furnaceid'); ?>
<?php echo $form->input('opcode'); ?>
<?php echo $form->submit('GO'); ?>
<?php echo $form->end(); ?>
</div>
"Furnaceoperation" is the name of the model where the data will be retrieved, "furnaceid" is a text field in the table (which I don't really need), and "opcode" is the field to appear and be selected from the list.
Help!
I have a similar problem.
in view/companies/add.ctp
echo $form->input('City.name',array('label'=>'City'));echo $jmycake->autocomplete('CityName','City/name',array('CityId'=>'id','CityName'=>'name'));
But gives the following error
Undefined property: TblconcessionariasController::$Tblmunicipio [APP/app_controller.php, line 34]
Line 34
$results = $this->{$this->params['form']['model']}->findAll($this->params['form']['search'].' LIKE \'%'.$this->params['form']['query'].'%\'',$fields,$this->params['form']['search'].' ASC',$this->params['form']['numresult']);Anyone have any idea?
Thank you
Model City, hasMany Company
Model Company, belongsTo City (Table Company have city_id attribute)
How can I make an auto-complete field city name for Company model, witch update company.city_id properly?
In the helper you must replace this
$("#'.$idInput.'").attr("onkeyup","query_'.$idInput.'(this.value)");
and use this instead
$("#'.$idInput.'").keyup(function(){ query_'.$idInput.'(this.value) });
dear god, was this painfull to fix.
Original:
$results = $this->{$this->params['form']['model']}->findAll
Modified:
$results = ClassRegistry::init($this->params['controller'])->findAll
Best Regards
Franco Tampieri
Franco, there is a typo in the Helper class code listed above.
The line:
$this->output.=$this->Javascript->codeBlock('Should be:
$this->output=$this->Javascript->codeBlock('Change:
Helper Class:
<?php$("#'.$idInput.'").attr("onkeypress","query_'.$idInput.'(this.value)");
?>
to:
Helper Class:
<?php$("#'.$idInput.'").keypress(function (e) {
txt = $("#'.$idInput.'").val()+String.fromCharCode(e.which);//current value + the new character
query_'.$idInput.'(txt, e.which);
});
?>
then you just have to change the function to accept the new code parameter and use it instead of pressedKey = e.charCode:
Helper Class:
<?phpfunction query_'.$idInput.'(txt,code) {
...
pressedKey = code || -1; //changed this line
...
?>
Hope this helps.
-S
the only thing i need to find out is that the popup does not align properly with the textbox
See comment for thread at -> (bakery.cake.org)
/articles/view/autocomplete#comment-1059