Using Ajax to Populate a Select Box in CakePHP

My apologies for the lack of articles recently, after completing the monster Dvd application I've been pretty busy with regular work and a few freelance gigs. In a project I've been working on I had to dynamically grab some data and then use it in a select box, I thought this would be a great example to go through since its quite a common process in web development.

In this article I'm going to be using jQuery as my Javascript library, I know that CakePHP has built in support for script.aculo.us but its something I've never used and jQuery will do the job just fine.

The Form

The project I'm working on has multiple Users and they each have many Pages on this form there is a select box of Users and when I select a user I want a list of all their pages. I can use jQuery to get the list of Pages in the background by using Ajax.

Below is a standard form with a few extra bits that are required for Ajax. First the form has a class of ajax_page which I'll use to identify the form, I've also added a ajax_loading_image div which will act as a placeholder for the loading image to indicate when Ajax is in use. I've also added an extra class to the Page input so that I can update it.

<?php echo $form->create('Page',array('class'=>'ajax_page'));?>
	<fieldset>
 		<legend>Add Page</legend>
		<div class="ajax_loading_image"></div>
		<?php
		echo $form->input('user_id',array(
			'label'=>'User: <span>*</span>',
			'title'=>'User of Page. (Required)'
		));
		echo $form->input('page_id',array(
			'label'=>'Parent Page: <span>*</span>',
			'title'=>'Parent Page of Page. (Required)',
			'div'=>'input select ajax_page_id'
		));
// more fields...
<?php echo $form->end();?>

The Javascript

Ok so in the Javascript I've attached a change event handler to the User select box, this will fire whenever the select box has changed value. I'm then going to update the ajax_loading_image div with an image, this will indicate that the system is busy to the user and provides some essential feedback.

Using jQuery its really easy to setup an Ajax request, I've setup the URL which will be the controller and action that will be dealing with the Ajax request and passed some variables to the controller in the form of a query string. This will include an "ajax" variable and the "id" of the selected user.

The success function will deal with the result of the controller action, more specifically this will update the form with the new HTML that will be outputted by the controller action. Finally the Ajax loading image is removed to indicate that the ajax request is no longer in use.

// file: /app/webroot/js/common.js

// add page form
$('.ajax_page #PageUserId').change(function(){
	// selected value
	var selected = $(this).val();
	
	// set loading image
	ajax_loading_image('.ajax_loading_image');
	// ajax
	$.ajax({
		type: "POST",
		url: '/pages/ajax_get_user_pages',
		data: "ajax=true&id="+selected,
		success: function(msg){
			//console.log(msg);
			$('.ajax_page_id').html(msg);
			// remove loading image
			ajax_remove_loading_image('.ajax_loading_image');
		}
	});
});
});

// set a loading image
function ajax_loading_image(div) {
	$(div).html('<img src="/img/ajax_loading.gif" alt="Ajax Loading Image"/>');
}

// remove loading image
function ajax_remove_loading_image(div) {
	$(div).html('');
}

The Controller

The controller action is pretty straight forward, it retrieves the "user_id" from the params variable and uses it to get a list of Pages from the database that are assigned to that User. Note that I've set the layout to null so that the View file being outputted is not wrapped in any layout HTML.

// file: /controllers/page_controller.php

// get user pages for form
function ajax_get_user_pages() {
	// init
	$id = $this->params['form']['id'];
	$pages = array();
	$this->layout = null;

	if($id > 0) {
		// get pages
		$pages = $this->Page->find('list',array('conditions'=>'Page.user_id'=>$id));
	}

	// set
	$this->set(compact('pages'));
}

The View for the Ajax Action

The View file is a single call to create a form select box with the list of Pages from the database. It has a few more options to specifically identify it as a select box and to use the $pages variable that has been passed from the controller.

<?php
// file: /views/page/ajax_get_users_pages.ctp

if(!empty($pages)) {
echo $form->input('page_id',array(
	'label'=>'Parent Page: <span>*</span>',
	'title'=>'Parent Page of Page. (Required)',
	'type'=>'select',
	'options'=>$pages,
	'div'=>false,
	'name'=>'data[Page][page_id]'
));
}
?>

Wrapping Up

So whats going on here, well I created a form as usual and added a few extra divs and classes that will identify the form. Using jQuery I've attached a change event handler to the User select box and when a User is selected its "id" is sent to the ajax_get_users_pages() action in the Pages Controller.

Like with any Controller that outputs code you must also create a View and this is simply a single form input to display a list of all the Pages that the User has. The form HTML is then sent back to the Javascript code and I replace the existing form code with the new HTML from the controller and because we're using Ajax the page doesn't need to be refreshed.

Posted on 16th October 2008
on 16/10/08

comments powered by Disqus