December 19, 2008

cakephp + ajax + good workflow = happy users.

before you go any further you should read my article:

cakephp isAjax + jquery = funny stuff

for those of you who’ve done any in depth ajax stuff you know it can be a blessing or a nightmare depending on the structures you have in place. here, workflow and semantics become vital so you don’t get lost in the details. one of the really great things is getting cake to throw real server errors that let you do valid things.

theres a concept you have to understand first though to make proper use of this technique and that is the difference between an ajax request being successful and your code responding with code based on the requested function being successful.

the simple way to conceptualize this is there are two levels of errors in reality:

  1. the ajax request failed for some network or server reason and could not complete the request properly
  2. the ajax request went swell, but the request failed programmatically for some reason (missing a passed argument, requesting an invalid blog, whatever).

the shortcut is to build your layouts based the expectation most ajax requests will succeed and render back content in the form of html, which can be plugged into your page. the basic thought is you send error information or success information but it always ends up in the same control (div, li) no matter if cake gave an error or not. so your errors become part of an inline process in the control.

not only is this bad form—which quickly leads all manner of organizational headaches—but it also limits how you can interact with your page. severly.

imagine this scenario:

on the left you have a list of valid images. on the right a list of buttons that can generate images for the ones missing from the list on the left. this is from an actual project i am on right now where the system maintains several image sizes for every image.

if we use the shortcut method we have to wrap our working area in some sort of control, say a div. and then replace the content of that control with updated html from our server. imagine a div around the button for simplicity.

thats our working area. the reason its our working area is because we can’t easily evaluate the content of the incoming html to decide further if cake gave us back an error or a success block of html, so we have to render it there and let the html given back decide what happens next. like so perhaps:

for clarity i’ve outlined our working div in yellow again. Basically the server responded with a crafted html response for a cake error which gives us the original content (so we can try again, presumably) and an error msg. now we might fade the error msg and allow them another whack at the mole. Or we may, on success, have some embedded js to fade the div after showing the success msg. lots of options but we are still bound to that working area which is sort of odd when we have a list of valid images to the left.

what if we wanted then to fade the div on success and push a new list item over to the list. we simply can’t do it working the way we are now.

enter js freaks who insist we could write some fancy js to do it, and we could but it’s not simple, and i think it becomes error prone when you are serving multiple responses and hand crafting an evaluation of them. some js guys might say this is a perfect case for json, and it really is, but that means if you have to make a template for the json response, and a template for the non-ajax enabled response (you do degrade gracefully right?) and that again makes things more complex.

the answer, i think, is to make use of the ajax server error mechanism already in place and usable. it lets you make post ajax-call decisions and influence any portion of the page you like easily. how do we do this?

first step, build a test action on your controller. you want first to ensure header redirects are working for you. try this code (sorry for the image, but tumblr is lame about code blocks).

what we are doing is turning off debugging on the fly. then rendering the page content we want (a generic error page) with an ajax layout, then doing a redirect with an http code of 500, with false to allow our ajax error layout to be rendered.

what should happen now is we get a firebug 500 code when we load that page:

and our content should look like an html snippet:

this particular snippet can contain cake error specific information like invalid blog post requested, or whatever is right in context to your request.

not getting that error code? check all your models and controllers for white space at the end of the file. number one cause of the redirect not working. see how hard it can be to spot sometimes?

that last line number is because there is a CR there and that gets output and breaks php’s ability to send headers correctly. search and search, i’m sure you will find a hidden bit of whitespace if you can’t get $this->redirect() to work right.

still with me? great.

now that we have that in place we have decoupled the working area (and response) from the response itself. if the server throws an error we can maybe pop up an error box with the right msg in it but do nothing to any other part of the page, like so:

I should point out right now if you can make a uniform screen control to handle your loading msgs, and your error msgs you will be much rewarded in programmer heaven. what you see above is a handy box that slides up from the bottom of the screen (which i will write about in detail later). i also have one for loading msgs. but where it gets really slick is using jquery .ajax events. you can attach an event to the popup of the bar that is triggered by any .ajax failure which gives you a uniform interface (and saves you writing error: function() {}; for every .ajax request. see an example of how to do that:

whats going on here is i have a hidden div at the bottom of the screen which is called #pop-error whenever there is an ajaxError triggered it pops up the div, and inserts the response from the call (which one assumes is an error msg from cake in html) into the #pop-error-msg div which is a div inside #pop-error.

its sexy. i know.

whats awesome though is you can do the same with ajaxStart and ajaxComplete to trigger your loading controls to start and stop which means in each jquery .ajax call you write you only need to write the success function, the rest is handled automagically.

so now we have broke our response from our working area, and made sexy error handling. what was the gain we made from a user perspective? now, on success we can just update the list on the left—which i think is what you would expect to happen when naturally.

so we do something like this:

whats going on here is we are writing a jquery .ajax request which on success hides the element that was clicked (a button for generating the image) then appending the response html to a ul and highlighting the list so the user follows the action correctly. it looks like so when complete.

which i think makes a lot of sense. there are now no more images that may need generation, and all the valid images are listed. and round trip we sent one line of html for either a success or a failure rather than passing elements back and forth through the ajax calls.

I think there is one more touch we can do for users who may miss the highlight when it fires and wonder if anything happened. we can callout the newly added list items by giving them a class named .added and monkeying with the .css for them. first we addd the class in jquery (note i swapped my icons a bit so they are uniform):

what we’ve done here is modified our success function a bit. first we find the last li item in the ul with the find command. then we chain an addClass call on it adding the class .added then for clarity we end() which puts us back into the ul, where we call a highlight against the whole ul (rather than the single li).

write a css class for .added and viola.

so a completed call looks like this:

which i think clearly gives the user a understanding that they just created the systhumb image and we didn’t have to muck up our ui at all.

a video of it in action so you can see the entire experience. watch and then ask yourself if this is cleaner, more fluid, and easier for the user.

i think that about wraps up this long one. I hope if you are fuzzy on specifics you are at least clear on the idea of how to use jquery + cake to make some really oustanding user experiences. I will write a separate post, a tutorial, on my loading and error solutions when i get a few minutes :)