Skip Navigation

CakePHP: Update a select box using ajax

I noticed a lot of new comers asking this question: I want a select box to be updated using ajax when I change the current item in another select box .

So this is a quick tutorial on how to do exactly that.

Here is the scenario: We have a select box filled with categories, when we select a category, another select box is updated with article titles. Fair enough ?

First thing to do is create the DB tables:

  1.  
  2. CREATE TABLE categories (
  3. id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
  4. name VARCHAR( 30 ) NOT NULL
  5. );
  6.  
  7. CREATE TABLE articles (
  8. id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
  9. title VARCHAR( 30 ) NOT NULL ,
  10. category_id INT UNSIGNED NOT NULL ,
  11. INDEX ( category_id )
  12. );
  13.  

Ok next create the models, put them in app/models of course.

  1.  
  2. <?php
  3. // category.php
  4. class Category extends AppModel {
  5. var $name = ‘Category’;
  6. }
  7. ?>
  8. <?php
  9. // article.php
  10. class Article extends AppModel {
  11. var $name = ‘Article’;
  12. var $belongsTo = array(‘Category’);
  13. }
  14. ?>
  15.  

now comes the articles’ controller, we’ll have an action index where we’ll display the select boxes.

  1.  
  2. <?php
  3. // app/controllers/articles_controller.php
  4. class ArticlesController extends AppController {
  5.  
  6.   var $name = ‘Articles’;
  7.   var $helpers = array(‘Html’,‘Form’,‘Javascript’,‘Ajax’);
  8.   var $components = array(‘RequestHandler’);
  9.  
  10.   function index() {
  11.     $this->set(‘categories’, $this->Article->Category->generateList());
  12.   }
  13.  
  14. }
  15. ?>
  16.  

Nothing fancy for the moment, we add the Javascript and Ajax helpers to the list of helpers that will be available in the view domain ( layout + views ).
We use the RequestHandler component, it will detect ajax requests and set proper header and layout for us, sweet isn’t it!

Our next step is to create a layout, we could just use the default layout but we need to include the prototype and scriptaculous libraries. make sure you downloaded the files and put them in webroot/js

Add these two lines in the head section of your layout:

  1.  
  2. echo $javascript->link(‘prototype’);
  3. echo $javascript->link(’scriptaculous’);
  4.  

If you’re using 1.2 ( not sure it’s available in 1.1 ) you can write it in one line of code:

  1.  
  2. echo $javascript->link(array(‘prototype’,’scriptaculous’));
  3.  

To be honest, you don’t need scriptaculous here, we’re not going to use it, just include prototype.

All right, all this above was basically just preparation, now here’s the view file of the index action. Create a folder named articles under app/views. Create a file in it, name it index.ctp if you’re using 1.2 or index.thtml if you’re using 1.1 (thtml works in 1.2 too but it’s depreciated).
Here is its content:

  1.  
  2. <?php
  3. // app/views/articles/index.(thtml|ctp)
  4. echo $html->selectTag(‘Category/id’, $categories, null, array(‘id’ => ‘categories’));
  5.  
  6. echo $html->selectTag(‘Article/id’,array(), null,array(‘id’ =>‘articles’));
  7.  
  8. $options = array(‘url’ => ‘update_select’,‘update’ => ‘articles’);
  9.  
  10. echo $ajax->observeField(‘categories’,$options);
  11. ?>
  12.  

In 1.2, selectTag() is depreciated, change it to select().

  1.  
  2. echo $form->select(‘Category.id’, array(‘options’=>$categories), null, array(‘id’ => ‘categories’));
  3.  
  4. echo $form->select(‘Article.id’,array(), null, array(‘id’ =>‘articles’));
  5.  

All right, nothing fancy either, we create two select boxes, we populate one with category names set from the action. We give both selects an id and we call observeField. This method of the ajax helper basically observes a DOM element for a change, if it occurs, it calls an url and updates a DOM element with the content returned from that url.
Let’s move on to create the action that we’ll be requested by observeField.

In the ArticlesController add this code:

  1.  
  2. function update_select() {
  3.   if(!empty($this->data[‘Category’][‘id’])) {
  4.     $cat_id = (int)$this->data[‘Category’][‘id’];
  5.     $options = $this->Article->generateList(array(‘category_id’=>$cat_id));
  6.     $this->set(‘options’,$options);
  7.   }
  8. }
  9.  

and it’s view..

  1.  
  2. <?php
  3. // update_select.(ctp|thtml)
  4. if(!empty($options)) {
  5.   foreach($options as $k => $v) {
  6.     echo "<option value=’$k’>$v</option>";
  7.   }
  8. }
  9. ?>
  10.  

Fun fun..now populate the db tables with some dummy data and test it out.

This was just one way of doing it, we could return JSON, XML or a script that’ll get executed.

Easier than that you die.

39 Responses to “CakePHP: Update a select box using ajax”

  1. [...] Ouahbi has a very nice example on his blog on creating automatic updating select-boxes with Ajax. If you change the first select-box, it updates the next selectbox accordingly. Filed [...]

  2. 1. It doesnt work on IE
    2. echo $html->selectTag(‘Category/id’, $categories, array(‘id’ => ‘categories’)); in line 4 of 1.1 version, it didnt work i tried with null as 3th param and it worked

  3. [...] http://api.cakephp.org/class_ajax_helper.html This tutorial shows how to update a select box using ajax call. Very useful [...]

  4. This will work IE with one minor modification. Rather than updating the select tag element, put the select tag element that you want to update in it’s own div tag, give it a name (id=…) and then update that div tag. This will mean that your update_selected action must return the entire selecttag, but it will work on IE.

  5. Hi,

    I used the steps given in this article to make my first ajax application(using cake). But I’m not able to work it out. It is giving some javascript error in the “prototype.js” file. I don’t know what’s happening but the articles are not populated in the second combo.. Can somebody here help me ???

    Nikhil

  6. first selectTag should be
    echo $html->selectTag(’Category/id’, $categories, null, array(’id’ => ‘categories’));
    it’s not working without it

  7. Hi,
    It populates the article dropdown box, but when i try to fetch its (value of article dropdown box) value to store in database, it gives null value and when i view source of this page then it doesn’t show the options items.

    Please give me any idea.

    Thanks,
    Pankaj

  8. Hey. I figured how to make this work in IE by myself but this brings me to another problem: If I update the whole select instead of just the options, I can’t figure how to observe it for onchange events anymore.. (I guess it’s because the observing is bound to the object rather than to the element name)

    Any tips on this would be appreciated!

  9. Thanks for this post, it really helped me out!

  10. it doesn’t work! i need help maybe i didn’t put the scripts downloaded in the appropriate field i work with wamp thanks

  11. Since i have tried with this example. I can able
    to see the two combo and when i select one combo value it will not reflected to another one.

    What will be the problem in this ?

  12. There might some problem with this ajax code.
    because it will not reflecting the values in another combo.

    please reply me solution. that is working.

  13. Yes , it is running on firefox 2 , but not running on IE 6 onwards. what could be the problem ?

  14. Hi
    Thanks for your fantastic tutorial, I is doing the job very well but we all have the IE problem with your code, and the solution that Case provided didn’t work for me, could you please be kind and tell us how should we make it work on IE? It’s better to update the tutorial code to work with IE.
    Thanks again.

  15. for ie/6/7 you can use
    And update the div, it’s work for iexplorer and firefox

  16. Works fine ;) Thanks!

  17. hello,

    sorry to say but for those who know the solution and not sharing it, bad bad boy :)

    here is my share :

    selectTag(’Category/id’, $categories, null, array(’id’ => ‘categories’));
    //echo ”.$html->selectTag(’Article/id’,array(), null,array(’id’ =>’articles’)).”;

    //cake 1.2
    echo $form->select(’Category.id’, array(’options’=>$categories), null, array(’id’ => ‘categories’));
    echo ”.$form->select(’Article.id’,array(), null, array(’id’ =>’articles’)).”;

    $options = array(’url’ => ‘update_select’,'update’ => ‘articles’);
    echo $ajax->observeField(’categories’,$options);
    ?>

    this modification makes sure that it will work under IE

    peace

  18. sorry people,
    i try again

    so the idea is to wrap the $form->select with a DIV id=articles, and close it ofcourse.

    echo ‘[div id=”articles”>’.$form->select(’Article.id’,array(), null, array(’id’ =>’articles’)).’[/div>’;

    in update_select.ctp update with a SELECT-tag:

    echo “”;

    if(!empty($options)) {

    foreach($options as $k => $v) {
    echo “$v”;
    }

    }
    echo “”;

  19. this tutorial works only with 1.1, with 1.2, form me, didn’t work . . . anyone had the same problem? anyone can help me?

  20. hello Vanquish

    email me and i can help you out

    jorenchong@gmail.com

    Joren

  21. Is there a requirement that the model relation ships are setup a certain way?

    I’ve got your code working but I’m trying to adapt it to a scenario were I have 3 select boxes. One select box being a drop list and the other 2 being select multiple. Depending on what I select in the drop list I want the other two selects to populate. The table relationship is HABTM.

  22. Ok I was having problems with this using Cake 1.2 pre beta. I thought it was my HABTM table. It turns out that if I put the slave select into a table, I get all of the debug sql in the select.
    How can I avoid this and still keep the slave select in a table?

    select(’Category.id’, array(’options’=>$categories), null, array(’id’ => ‘categories’));

    ?>

    select(’Article.id’,array(), null, array(’id’ =>’articles’ ));
    ?>

    pickListSimple( null, null );
    //$options = array(’url’ => ‘update_select’,'update’ => ‘SelectLeft’);
    $options = array(’url’ => ‘update_select’,'update’ => ‘articles’);

    echo $ajax->observeField(’categories’,$options);

    ?>

  23. Oops, try this code:

    select(’Category.id’, array(’options’=>$categories), null, array(’id’ => ‘categories’));

    ?>

    select(’Article.id’,array(), null, array(’id’ =>’articles’ ));
    ?>

    ‘update_select’,'update’ => ‘articles’);

    echo $ajax->observeField(’categories’,$options);

    ?>

  24. Ok my table tags are getting eaten up but there are table tr and td tags around the slave select.

    Could that be a CSS problem?

  25. I found one solution. You can add

    Configure::write(’debug’,0);

    to the update_select.ctp/thmtl file.

  26. I am using cakephp 1.2 but Not work at all

  27. Hi

    Your model work fine for me, but I need update 2 selects box when one is changed.

    Ex: When Category is changed, the combos articles and Employ must be updated. It’s possible?

    Best Regards,

    Cássio

  28. Hi again

    With some work I can do 2 combos updated when other is changed.

    I create 2 methods update_select and 2 update_select.thtml with distincts names and this work without any problems, thanks anyway.

    Very good tutorial.

  29. This code:

    $this->set(‘categories’, $this->Article->Category->generateList());

    Is deprecated. What is the correct code in CakePHP 1.2 ? Seems generateList no longer exists.

  30. After insert data, when I edit the information of articles I don’t know how populate the articles combo without change the category combo.

    In insert form the articles combo is updated when the category combo is changed. $ajax->observeField(’categories’,$options);

    But in edit form is necessary do that when the form is loaded.

    Somebody know how I can do that?

  31. generateList() is deprecated in cake 1.2 that is correct, use

    $this->Article->Category->find(’list’);

    instead! You also have to change the line in update_select:

    $this->Article->generateList(array(‘category_id’=>$cat_id));

    to

    $this->Article->find(’list’, array(’conditions’ => array(’Article.category_id’ => $cat_id)));

  32. very nice tutorial, i want to ask how about we don’t update to selectbox but we put return into inputbox,
    the return value automatic one result…,
    thanks for you kind

  33. Thanks, still a useful tutorial after so much time :-)

  34. anyone else having issues with it just populating the first select in cake 1.2 ? great tutorial, just cant seem to get it to generate the second drop down. any help is appreciated

  35. You write, that we can return a script and execute it.
    “This was just one way of doing it, we could return JSON, XML or a script that’ll get executed”

    Do you have any example, how can I return a script and execute it?

  36. Appreciate the info guys, thanks

  37. when I use $ajax->observeField it works to upload the the articles-div, but it stays empty. the function update_select is not even called. What am I doing wrong?!

  38. Here’s another tutorial to accomplish the same thing with Cakephp 1.2

    http://j4vk.com/wordpress/2008/08/07/cakephp-dynamic-select-boxes-ajax/

  39. Hi all, if u have still IE issue .There is a solution for u.
    Don’t use select name and id same
    ex..
    select(’city’,array($cities),null,array(’id’=>’city_id’,'empty’=>’Select One’,”class”=>”search_dropfield”),array(’empty’=>’Select One’),false);?>
    Here i am using name=city
    id=city_id

Leave a Reply


Login Method

OpenID

Anonymous