Ajax Delete with CakePHP and jQuery

In this post I'm going to describe how you can use Ajax using the jQuery javascript library to delete items without a refresh. This is quite an easy enhancement to achieve and will help with the usability of your applications. It isn't CakePHP specific but in this example I'm using the framework to delete items from the main index.

I'm a big fan of progressive enhancement and so the delete action will still work without Javascript enabled but this will improve the user experience a little bit by keeping the user on the same page and will display a smooth fade animation to get rid of the table row.

The View

Usually your index.ctp will display a table of all the items for that Controller with edit/delete links for each one. What I usually do is add a class to each of the delete links so that I can target these with jQuery. In this case I've added the confirm_delete class, on a usabilty side of things I popup a javascript confirm box asking whether they really want to delete the item in case they hit the link by accident.

<?php
// simple HTML link with a class of 'confirm_delete'
echo $html->link('Delete',array('action'=>'delete',$item['Item']['id']),array('class'=>'confirm_delete'));
?>

Include jQuery

Download jQuery and create another file which will hold all your custom Javascript code in the js folder in the webroot. Once done include these files in your default layout file. On new projects I copy the default from /cake/libs/view/default.ctp to /app/views/layouts/default.ctp and then start to modifiy the copied one.

// include the javascript in default.ctp
<script type="text/javascript" src="/js/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="/js/jquery-common.js"></script>

Javascript

Just a quick explanation of what the below code, the source is fairly well commented so you shouldn't have a problem understanding what each bit does. First assign a click handler to anything with a "confirm_delete" class, ask the user whether they are sure they want to delete the Item. If they want to go ahead with the delete send an Ajax request to the link "href". The dataType is set to "json" so we'll be expecting the returned data to be in that format. If the delete was a success the script simply fades out the table row and displays a message to the user.

Just a quick note incase the user doesn't have Javascript or has it disabled. The delete action will function normally but without all the Javascript goodness so from their perspective they're not missing anything. Progressive enhancement is usually the best way to proceed. Get the script working without Javascript and then use it to enhance the user experience.

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

// on dom ready
$(document).ready(function(){
// class exists
if($('.confirm_delete').length) {
        // add click handler
	$('.confirm_delete').click(function(){
		// ask for confirmation
		var result = confirm('Are you sure you want to delete this?');
		
		// show loading image
		$('.ajax_loader').show();
		$('#flashMessage').fadeOut();
		
		// get parent row
		var row = $(this).parents('tr');
		
		// do ajax request
		if(result) {
			$.ajax({
				type:"POST",
				url:$(this).attr('href'),
				data:"ajax=1",
				dataType: "json",
				success:function(response){
					// hide loading image
					$('.ajax_loader').hide();
					
					// hide table row on success
					if(response.success == true) {
						row.fadeOut();
					}
					
					// show respsonse message
					if( response.msg ) {
						$('#ajax_msg').html( response.msg ).show();
					} else {
						$('#ajax_msg').html( "<p id='flashMessage' class='flash_bad'>An unexpected error has occured, please refresh and try again</p>" ).show();
					}
				}
			});
		}
	return false;
	});
}
});

Controller

In your Controller don't forget to include the RequestHandler component, it will help us ascertain if an Ajax request is being used. Have a look at the "admin_delete" method below. The default message and class is setup so that it can used in either the data sent via JSON or by the "setFlash" method. A quick check is done to ensure that the Item id has been passed and that it's numeric. The Item is then retrieved from the database to ensure it exists and then the Item is deleted. The message and class variables are then updated with the result of the delete action.

The magic really happens in the next part, we check to see if the request being sent is an Ajax request, if it is we set the Controller's "autoRender" and "layout" variables to false. This ensures that the method doesn't automatically output anything other than the JSON response. Data is encoded using "json_encode" function that will say if the action was successful along with a message. The script is then stopped using "exit" and then the Javascript code will take over and parse the response.

If the request to the action wasn't Ajax then we can assume that the person doesn't have Javascript and we have to deal with it via the old school method, a la simply refreshing the page and set a flash message with the result of the delete operation.

// include the RequestHandler component at the top of your Controller
var $components = array('RequestHandler');

/**
 * Delete a List
 * @param int $id
 */
function admin_delete($id=null) {
	// set default class & message for setFlash
	$class = 'flash_bad';
	$msg   = 'Invalid List Id';
	
	// check id is valid
	if($id!=null && is_numeric($id)) {
		// get the Item
		$item = $this->Item->read(null,$id);
		
		// check Item is valid
		if(!empty($item)) {
			// try deleting the item
			if($this->Item->delete($id)) {
				$class = 'flash_good';
				$msg   = 'Your Item was successfully deleted';
			} else {
				$msg = 'There was a problem deleting your Item, please try again';
			}
		}
	}
	
	// output JSON on AJAX request
	if($this->RequestHandler->isAjax()) {
		$this->autoRender = $this->layout = false;
		echo json_encode(array('success'=>($class=='flash_bad') ? FALSE : TRUE,'msg'=>"<p id='flashMessage' class='{$class}'>{$msg}</p>"));
	exit;
	}
	
	// set flash message & redirect
	$this->Session->setFlash($msg,'default',array('class'=>$class));
	$this->redirect(array('action'=>'index'));
}

Wrapping Up

Hopefully everything here should make sense, using jQuery with CakePHP is pretty straight forward but may just take a while getting to grips with everything. If you have any questions or comments just drop me an email or leave your feedback using the form at the bottom.

Posted on 11th February 2010
4 years, 10 months, 6 days ago

comments powered by Disqus