jQuery autocomplete Helper

by Gemini
I use jQuery becose is fast and concise Javascript library. My helper, jmycake, use jQuery for AJAX autocomplete.

Requirements:


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:

  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... :(

Report

More on Helpers

Advertising

Comments

  • rthakurdas posted on 09/01/10 10:30:27 AM
    i want to call autocomplete in another's view which is related to the current model..
  • ldesert posted on 07/26/10 04:30:28 PM
    Replace the helper code with the code below.

    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;
        }
    }
    ?>

  • stargopy posted on 06/16/10 07:11:42 AM
    jquery-autocomplete-helper,
    cakephp autocomple not working in chrome and ie browsers
  • jozek000 posted on 04/11/10 01:33:45 AM
    See: CakePHP AJAX Helper with jQuery
    Autocomplete is also included. Docs and many examples on site. I think that you should try it.
  • revizyon posted on 02/19/10 07:51:32 AM
    Thanks...
  • laceja posted on 01/23/10 02:00:52 PM
    I'm a very new user to CakePHP and I'm having problems making this autocomplete feature work for me. I'm trying to use it in an edit view, where the user may wish to input some data selectable from an unrelated table. The idea is for them to use the autocomplete to limit the amount of typing required.

    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!
  • larsurilch posted on 10/30/09 06:00:08 AM
    Hello Atta,

    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
  • atap posted on 10/19/09 08:30:58 AM
    For example I have 2 models:
    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?
  • pabloacastillo posted on 09/08/09 10:24:31 PM
    Ok, this bug was a pain in the a** to catch, but solved at last.
    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.
  • pabloacastillo posted on 08/19/09 02:21:10 AM
    If the curly brackets doesnt work for you, cause you have php4 or older, you can change this and it should work again:

    Original:
    $results = $this->{$this->params['form']['model']}->findAll

    Modified:
    $results = ClassRegistry::init($this->params['controller'])->findAll
  • dr.dran posted on 06/22/09 07:46:25 AM
    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
    • dbarowy posted on 07/17/09 10:04:03 AM
      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?
      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('
  • sc0ttman posted on 05/27/09 11:40:58 AM
    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
  • bennychong posted on 02/11/09 07:31:45 AM
    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
  • oliver_sakkam posted on 11/03/08 06:32:42 AM
    can you add the sample database table you have used ?
  • masterkeedu posted on 04/20/08 04:24:24 PM
    Does this have same security issue?

    See comment for thread at -> (bakery.cake.org)
    /articles/view/autocomplete#comment-1059
login to post a comment.