jQuery autocomplete Helper

By Gianluca Gentile (Gemini)
I use jQuery becose is fast and concise Javascript library. My helper, jmycake, use jQuery for AJAX autocomplete.

Requirements:


Helper:

Helper Class:

Download code <?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:

Download code
    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:

Download code
<?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:

Download code
var $helpers = array('Html','Form','Javascript','Jmycake');

CSS:

Include this CSS style sheet in your layout:

Download code
@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:

Download code
<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:

Download code
<?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:

  1. $numResult: number of result that show in the view
  2. $strlen: minimum length of the insert text in the html input

It's all!.

Sorry for my english... :(

 

Comments 647

CakePHP Team Comments Author Comments
 

Question

1 Security Issue

Does this have same security issue?

See comment for thread at -> (bakery.cake.org)
/articles/view/autocomplete#comment-1059
Posted Apr 20, 2008 by Eddie
 

Comment

2 doesnotwork

can you add the sample database table you have used ?
Posted Nov 3, 2008 by oliver
 

Comment

3 it works

it works beautifully and just what i wanted. take note that you need to include jquery in your layout

the only thing i need to find out is that the popup does not align properly with the textbox
Posted Feb 11, 2009 by Benny Chong
 

Comment

4 Doesn't work for Safari/IE

I have tweaked the code to add the event properly for IE and Safari.
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.'(txte.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:

<?php 
function query_'.$idInput.'(txt,code) {
...
pressedKey code || -1//changed this line
...
?>

Hope this helps.
-S
Posted May 27, 2009 by scott
 

Question

5 Problem with two or more autocomplete field

Hi! I'm a newbe of ajax, and I tried this autocomplete helper thats make my work very fine, but now I have realized that if I utilize this to make two o more field with autocomplete features thats doesn't work. Does, anyone have noticed this problem? There is a solution?

Best Regards

Franco Tampieri
Posted Jun 22, 2009 by Franco Tampieri