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 jsfolder 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')); }
No comments:
Post a Comment