JQuery autocomplete in CakePHP

Update to this post is here. (New libraries and new techniques are discussed).

This is a quick example on how to setup an autocomplete field using JQuery and CakePHP.

Let’s assume that we have a Product model and products controller. Our goal is to allow the user to type in a few characters and then to display a matching list of products from the DB by using autocomplete.

First things first, be sure that you’ve included the JQuery library. If you are using $scripts_for_layout, then in your view file you’d probably do something like:

$javascript->link('jquery/jquery.min', false);

Next, you have to include the autocomplete plugin… There are a few of them out there.
Well, this one does the trick for me: http://www.pengoworks.com/workshop/jquery/autocomplete.htm

(Please refer to the documentation at the link above for details on how the JQuery autocomplete works and for an example of what it looks like. You can also grab the relevant CSS and the loader image from there).

After you save it somewhere in your JS directory (perhaps js/jquery/plugins) you should include it in the view just like this: $javascript->link(‘jquery/plugins/autocomplete’, false);

Now you need the form field that will be used as an autocomplete field. The plugin, by default, will expect a field with an id = “autoComplete”. So, as part of your form, you’ll probably do something like

<?php
echo $form->text('Product.name', array(
    'size'=>'30',
    'id'=>'autoComplete'));
?>

(Remember that we are assuming the Product model here)

Not too bad, so far. Alright, now we have to trigger the autocomplete by creating some JavaScript that will actually attach the autocomplete action to your form element.
Make a new JS file and call it autocompleteAction.js (don’t forget to also include it in the view).

Here’s what it should look like:

$(document).ready(function(){
$("#autoComplete").autocomplete("/products/autoComplete",
{
minChars: 2,
cacheLength: 10,
onItemSelect: selectItem,
onFindValue: findValue,
formatItem: formatItem,
 autoFill: false
 });
});

function selectItem(li) {
  findValue(li);
}

function findValue(li) {
  if( li == null ) return alert("No match!");

// if coming from an AJAX call, let's use the product id as the value
  if( !!li.extra ) var sValue = li.extra[0];

  // otherwise, let's just display the value in the text box
else var sValue = li.selectValue;

alert("The value you selected was: " + sValue);
}

function formatItem(row) {
  if(row[1] == undefined) {
    return row[0];
  }
  else {
    return row[0] + " (id: " + row[1] + ")";
  }
}

Just to cover the basics, the above action will tell the autocomplete to kick-in after you’ve typed two characters into your form field (minChars: 2). The lineSeparator: “^” tells autocomplete to jump onto the next line whenever it encounters the ^ character. I will explain this a little more later. It is worth noting that the default new line character is “\n”, but I could not get it to work with CakePHP so I’ve decided to use a somewhat obscure character instead, which is unlikely to appear in a product name. This is not a very good solution by any means and someday I will come back to figuring it out. (Update: this works just fine now, so no longer such trickery as I’ve used before is required).

So what’s going on here? Well, the autocomplete needs to get the list of available items (products in our case) somewhere, and this code tells it exactly where to look: autocomplete(“/products/autoComplete”… You’ve probably guessed that in terms of CakePHP you need to have a products controller and an autoComplete action. Now is a a good time to create one.

It should look something like:

function autoComplete() {

 Configure::write('debug', 0);
 $this->layout = 'ajax';

 $products = $this->Product->find('all', array(
   'conditions'=>array('Product.name LIKE'=>$this->params['url']['q'].'%'),
   'fields'=>array('name', 'id')));

$this->set('products', $products);
}

So basically we are looking for all products who’s name matches (LIKE) the two characters that we’ve typed in the form field. We are returning a product name and product id fields from the DB. Note the: $this->params[‘url’][‘q’]. The autocomplete plugin uses the GET method to pass the form data to your controller and it expects to have a q= as the query string, and that’s how you read that query string in CakePHP. Now we’ve found some products and set the resulting array for the view. The view will actually be the list that pops under the text field once you start typing. Let’s create one (auto_complete.ctp):

if(!empty($products)) {
foreach($products as $product) {
  echo $product['Product']['name']. '|' .$product['Product']['id'] . "\n";
 }
}
else {
 echo 'No results';
}

Here we are looping through the products array and displaying the name and the id, of course if the array happens to be empty we will display ‘No results’.

That’s all there is to it. With some clever copy/paste and possibly a few quick changes this thing should be working like a charm in just less than twenty minutes.

Of course you’ll probably want to come up with some better usage for it than doing a JS alert…

m4s0n501
  • http://www.bensnider.com ben

    Just correcting a typo: line 3 of the auto_complete.ctp template should have the id enclosed in quotes whereas it’s [‘id] currently.

    Thanks for the code though!

  • teknoid

    @ben

    Thanks. I’ve fixed it up.

  • http://www.ministryofwebdevelopment.nl primeminister

    Nice article! thnxs Teknoid!

  • http://www.nexti.com.tw seanlee

    good autocomplete !
    thank you very much

  • teknoid

    @seanlee

    You’re welcome

  • Abdulla

    first thanks for nice article
    but i’m beggener in this field
    and it doesn’n work with me

    if u can support me with additoinal detail

    !!!

  • teknoid

    @Abdulla

    It’s hard to troubleshoot problems like that, especially when I have no idea what’s happening on your side.

    The best place to get help would be at the cakephp google group or the IRC channel.

  • Imran

    hi, it is really helpfull specially when there is very few articles are for cakephp…
    Thanks for the good work…

  • http://teknoid.wordpress.com teknoid

    @Imran

    Good to hear it helped, you’re welcome.

  • Chris

    Hi teknoid, this has been really helpful and it works nicely, but you say “you’ll probably want to come up with some better usage for it than doing a JS alert…” and of course I do!

    Can you explain how I might take the Product.id value and send that back to the form (as a hidden field I suppose) so that it can be submitted? As it stands the Product.name is visible to the user in the field which is what I desire but I also need the selected Product.id to be submitted with the form.

    Many thanks for the great tut, I hope you can help solve this final riddle for me!

    Chris

    • SREE

      CAN U PLZ TEL ME HOW TO GET THE PRODUCT ID WHILE SUBMITTING?

  • Chris

    Nvmd I figured it out… sorry to have bothered you. For anyone else having a dumb moment like me the answer is (obviously, duh) to put this in autocompleteAction.js:

    document.FormName.FieldName.value = sValue;

    Thanks again teknoid… love the blog, it is invaluable to me as a cake noob! Keep up the great work!

    • johnemel

      Yo teknoid thanks for that, it’s worked a dream!

      Chris though, I’m running into the same problem as you – i need the id rather than the name to post to $this->data.

      Where exactly in autocompleteAction.js do you put your:

      document.FormName.FieldName.value = sValue;

      ??

      I don’t have a clue about jquery unfortunately!

      Any help appreciated.

  • http://teknoid.wordpress.com teknoid

    @Chris

    Thank you. Glad you’ve got it sorted out.

  • siva

    its not working..will anybody help me?

  • Ashraf

    Thanks So much Teknoid… :)

  • http://teknoid.wordpress.com teknoid

    @Ashraf

    Thanks, glad it worked ;)

    @siva

    “Not working”, doesn’t describe the problem. Please ask at the google group or IRC.

  • jpsirois

    Thanks you again teknoid for your awesome tutorial. Continu Keeping it simple like this =)

  • http://teknoid.wordpress.com teknoid

    @jpsirois

    Nice, glad to hear it helped ;)

  • Blur

    Thanks for this article Teknoid !

    It runs, but I have a problem with url’s, my project runs on wamp server (don’t know if it’s the problem). I included this script in a search form wich use url parameters, and if i ‘submit my form’ and redirect on the same page, the url will be composed of values from the previous search, and the autocomplete will not work a second time, like it doesn’t know where is the auto_complete.ctp.

    Or if my url is

    root/models it doesn’t work

    root/models/ this one works

    Any idea ? Thanks

    Sorry for my english

  • http://munna.freetzi.com/munnasite Munna

    Hey:

    Thanks!!!!!!!!!!

  • http://teknoid.wordpress.com teknoid

    @Munna

    You are welcome :) !!!!!!

  • Anil

    it not wotking.I have created the following pages
    controller,model and view and js pages.

    My doubt is how many view page should I create this to work ? what is auto_complete.ctp ?

  • http://teknoid.wordpress.com teknoid

    @Anil

    auto_complete.ctp – is what shows you the suggestion results

  • Andrew Christensen

    Your implementation is great, but I am running into one small problem.

    I am using Firefox and Firebug to test this. I am getting the following message in firebug:

    $(“#autoComplete”) is null
    [Break on this error] $(“#autoComplete”).autocomplete(“/patients/autoComplete”,\n

    I am lost. I can’t get it to work. Here’s the html for the form page:

    Patient Search

    Gender

    Male
    Female

    There are NO other references to “autoComplete” as an id and you can clearly see that id=”autoComplete” is in the html.

    Any ideas?
    Thanks! Andrew

  • Andrew Christensen

    Wow. I am tired. Sorry for the multiple posts. Here’s the right html:

    Patient Search


  • http://teknoid.wordpress.com teknoid

    @Andrew Christensen

    That’s a strange error message.
    Did you include jquery and autocomplete properly?

  • Laceja

    This is great! Is there a way to make it work on multiple fields of the same view? I have more than 20 fields on the same view, where I would like to use autocomplete.

  • http://teknoid.wordpress.com teknoid

    @Laceja

    Yeah that should be pretty easy:

    $(‘#field-one’).autocomplete(… settings …);
    $(‘#field-two’).autocomplete(… settings …);

    If the settings are similar (i.e. maybe all post to the URL to get data), you can probably get away with using the class selector:

    $(‘.all-auto-complete-fields’).autocomplete(… settings …);

  • jiru

    can u help me in jquery search , name with photo from mysql db.

    can u provide the view, controller and script for that ?

    Thanking you,
    jiru

  • http://teknoid.wordpress.com teknoid

    @jiru

    Sorry, not time to write apps for other people, got a ton of my own work to do ;)

  • http://www.asecondsystem.com will

    Thanks, This worked nicely. Plus made the jquery plugin choice easier :)

    I have 2pennies to contribute!

    $(‘#fLocationId’).val(sValue);

    Since we are JQuerying, why not use their handy val method to set a hidden location_id field ready to store the id value. I just put it in place of the alert.

    Thanks again!
    Will

  • http://teknoid.wordpress.com teknoid

    @will

    Certainly you’d want to do something “more fun” than an alert().
    This is only for demo purposes.

  • Alquama zulquarnain

    hello sir,i am trying to implement autocomplete in cakephp & getting this error Notice (8): Undefined index: q [APP\controllers\propertynames_controller.php, line 26] when applying autocomplete going through your tutorials and the second thing,could you plz tell me the exact location of jquery.min.js,autocompleteAction.js and autocomplete.js file.i would be thankful to you.

  • teknoid

    @Alquama zulquarnain

    This post is somewhat outdated, as jQuery UI now has an auto-complete widget.

    pr($this->params); to see what indexes you have available and why “q” is not there.

    The placement of the files is up to you, but make sure they are all visible by the app. In general any JS files should go under webroot/js/…

  • Ruben

    Thnx @teknoid rulez !!!

  • http://www.asecondsystem.com will

    Not obsolete! handy to have something simple and easy to modify. Also great that it dosnt clear the box if nothing found, good for when you want to add new items.

    I’m having trouble applying it to inputs added via ajax. So it works for an initial two inputs, but when i add anouther row to my table of products to add, it ignores the new ones. Preumably because the connectCombos() function was done at docReady and so hasn’t “comboified” the new rows added.

    Anyone know where / how i can tell jquery my new row has appeared and wants to be combo’ed?

    Here is the code:

    $(document).ready(function(){
    connectCombos();
    });

    function connectCombos(){
    $(“.autoComplete”).autocomplete(“/items/autoComplete”,
    {
    minChars: 2,
    cacheLength: 10,
    onItemSelect: selectItem,
    onFindValue: findValue,
    formatItem: formatItem,
    autoFill: false
    });
    }

    Thanks for this awesome tutorial!

  • Johannes

    This tutorial works great for one textbox. But how can I dynamically add unlimited textboxes with autocomplete for the same content? For example I want to add several ingredients to a recipe.

  • Ojo

    Now, how do i related my auto_copmlete.ctp to the form where my input which requires auto complete?

  • teknoid

    @Ojo

    It doesn’t have anything to do with the form, this is the auto-complete suggestion list, which will show up under the text input.
    The DOM ID is what ties the whole thing together, which specified in your input() tag.

  • Julia

    Thanks for this nice tutorial! I might have the same problem as Ojo: The autocomplete JS kicks in (the background of the input field changes its color and I can see the loading indicator), but no list is displayed under the input field. I don’t understand how or when the auto_complete.ctp is called? Where to I put the auto_complete.ctp? Right now, it’s in app/View/Places/auto_complete.ctp (I’m using places instead of products).

  • teknoid

    @Julia

    The auto-complete.ctp gets called when you make an ajax request to /products/autoComplete/

    Just one note here, is that looking at your path it seems like you are using Cake 2.0 and this article was written using Cake 1.2 … things work similarly, but can be improved a bit for 2.0. However the generics should still work.

    p.s. your auto_complete.ctp would go with the rest of the Product views (using this example).

  • Chris

    I’m trying to use the autocomplete and it works when I do not add this part: $(document).ready(function(){
    $(“#autoComplete”).autocomplete(“/products/autoComplete”,
    {
    minChars: 2,
    cacheLength: 10,
    onItemSelect: selectItem,
    onFindValue: findValue,
    formatItem: formatItem,
    autoFill: false
    });
    });

    If I add that part as its own JS file, into the view, or the layout file nothing happens. Also if I go into the Autocomplete.js and make changes to the defaults at the bottom, they don’t work either. I’m completely baffled by these errors.

  • http://galadcreative.blogspot.com.es/ Omar

    hi,

    just when i put $(“#autoComplete”).autocomplete(“/products/autoComplete”…
    do not detected action autoComplete.

    * the script jquery and jquery.autodetected is place into webroot/js
    * into my index put the code script js –> $(“#autoComplete”) and the view for auto_complete is place into view index too
    *im use cakephp 2.0, my question is, how javascript requests to /products/autoComplete???

    i make one debug into action autoComplete but no show me nothing..
    any suggestions

    thanks