Serving up actions as AJAX with jQuery in a few simple steps
How to quickly setup AJAX to call Cake actions using jQuery. (For the people who keep wondering when there will be a jQuery helper)
Since this tutorial was missing and some people on IRC have been asking questions about when there will be jQuery support in the ajax/javascript helper I decided to write this. I find that helpers are used for generating HTML markup, you don't see a helper for CSS as it shouldn't be mixed with inline markup and frankly, the same goes for Javascript.
I like to apply Javascript (especially through the power of jQuery CSS selectors) as a stylesheet to my work to improve versatility. But even if you just want to figure out how to do a basic ajax call with jQuery and Cake here's the steps you need:
Download code
Download code
Download code
Download code
Download code
Want to have a close button on an overlay window on all ajax? You should probably put it in the layout.
Download code
I like to apply Javascript (especially through the power of jQuery CSS selectors) as a stylesheet to my work to improve versatility. But even if you just want to figure out how to do a basic ajax call with jQuery and Cake here's the steps you need:
- Attach jQuery Library
- Turn on the RequestHandler Component
- Create and style a div (overlay) in the view to use for ajax populating
- Choose an action to trigger the AJAX
- Throw in the jQuery code
Step 1
Throw this in your layout:Download code
echo $javascript->link('http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js');
Step 2
Throw this in your controller of question (or app_controller for all-around ajaxy goodness):Download code
var $components = array('RequestHandler');
Cake will now automatically detect the AJAX headers sent by jQuery and will return the actions with the same views you coded for normal use except now with the ajax layout (by default empty). Step 3
Add this to your layout or the action you plan to use AJAX in for overlays or population content with:Download code
<div id="overlayer" style="display:none"></div>
Preferably without the initial inline CSS of courseStep 4
The html link we will magically transform with the 'js-ajax' class because Javascript just plain shouldn't be inline:Download code
<?php echo $html->link('Demo', array('controller'=>'posts','action'=>'view',$id), array('class'=>'js-ajax')); ?>
Step 5
Notice how we simply use our class to hijack a perfectly normally working link with it's already present information to serve up as AJAX? In addition we have the overlayer div fade in on callback so it runs after the ajax loads instead of at the same time. You may want to make some thinking icon appear first as this occasionally takes a second or two.Download code
// $() is a css selector. First I choose what triggers the event. In this case when
// you CLICK on a LINK with the js-ajax CLASS. I changed the example to use the live()
// function so that any javascript-generated html will be binded too instead of click().
$('a.js-ajax').live('click', function() {
// Now we simply make the ajax call. load($url) will pull the url's VIEW and put it
// into ther innerhtml of whatever tag you called load on. In this case, I want to fill
// up my #overlayer div with the results of the ajax.
$('#overlayer').load(
// Here is the tricky part. Instead of hard-coding a url to pass, I just had jquery
// go look at what the link (from the outside scope, .click() part) was already going
// to (href) and used that as the argument.
$(this).attr('href')
, function () {
// This is a callback, after the ajax gets loaded, the #overlayer div gets faded in at 300 miliseconds.
$(this).fadeIn(300);
});
// And finally to prevent actually making the link go anywhere
return false;
});
Want to have a close button on an overlay window on all ajax? You should probably put it in the layout.
Download code
LAYOUT:
<a href="#close" title="Close Overaly" class="close">Close</a>
jQuery:
// Create the event trigger, in this case when a link with the class CLOSE inside the overlayer div is clicked
$('#overlayer a.close').live('click', function () {
// Fade out the overlayer
$('#overlayer').fadeOut(300);
return false;
});
Comments
Comment
1 Great article!
Comment
2 Use jFrame!
link('Demo', array('controller'=>'posts','action'=>'view',$id), array('target'=>'overlayer')); ?>
Easy and simple!
Comment
3 Agree
I agree
Comment
4 Jquery
I found a great plugin jqGrid
and integrate it with cakephp
at
www.the-di-lab.com/?p=28
Comment
5 Cant get js-ajax to trigger link in dynamic content
. Each of these in turn should load up a further subset of options when they are clicked (again replacing the list in place).
The problem is, using the above method, everything works fine for the first set of options. But when I load the subsets, even though they are all of class js-ajax, when I click on them it does not trigger an ajax call, instead they act as standard links.
Is there a reason why the dynamically generated links dont trigger the
$('a.js-ajax').click(function() {
........
});
Thanks in advance if you can help
Comment
6 url problems
using the $.live to bind to override all clicks and submits within a div, and to load the response into the div.
when clicking on a link, it is a simple XHR GET request that is processed (nearly) perfectly by the server.
The problem is with the form submit (I use JQuery' form plugin in the venet handler), when posting data: once the data is processed on the server side and the record is successfully updated, the server side action launches a redirect. The result returned for such an XHR is not the same page that the XHR was sent to. Normally, this shouldnot be a problem and You receive an apparantly correct answer... except the links generated with the HTML helper. Instead of getting the link as You would like to have it ie: http://hostname/app_name/controller/action. You will get http://hostname/app_name/app_name/controller/action, which leads nowhere, because the application name (base url) is duplicated within the link.
Does anybody have this problem? Any solution?
Comment
7 Cant get js-ajax to trigger link in dynamic content
Yes. When you 'bind' the triggers, in this case the CLICK events, jquery binds them to existing DOM (html). When you create new html, since it did not exist it has not been binded. You must rebind in the callback, which is the second argument of load() and already contains the fadeIn(300).
However in the latest build of JQuery they have some new live feature (http://docs.jquery.com/Events/live) that will bind any existing and potential future DOM. I think this may be a proper solution because putting the bind into the callback sounds like some sorta endless loop lol.
The live bind would look like this:
// The first argument is what event should be the trigger (CLICK), the second is the function to trigger (just like normal)
$('a.js-ajax').live('click', function() {
// Instead of the original line:
$('a.js-ajax').click(function() {
I suppose the proper way is throw the whole thing into a function, and then just call the function in the callback.
Hope this makes sense.
Also I recommend to anyone with problems to go look at the JQuery documentation or join #jquery on freenode.
Comment
8 Fantastic
This can of course be achieved (though I am not suggesting the OP didn't know this) with prototype using Event.observe, $$ and Ajax.Request. Alhough I am not sure prototype has live binding, am I wrong?
Comment
9 The article is called serving ajax with jQuery.
I'm aware that every JS / framework out there can do this. My example highlights how to do this in JQuery though, otherwise I'd call it how to do it in javascript or in a JS framework.
However I don't list others because I'm unfamiliar with mootools and prototype, if you want to build up test cases I will be happy to add them to the article and modify the title, might as well make it a 1 stop shop article.
Comment
10 jQuery ajax pagination
Comment
11 excellent and simple write up