Everyone who has worked with Ajax probably knows how annoying it used to be sometimes to get the code working in some browsers (*coughiecough*)
There’s another thing too, though. A lot of people don’t realize it, but often making a website which needs to work both without Ajax and with it, can have some nuisances. The biggest of them all is that you often have to write code specifically to handle situations, where the request was made with XMLHttpRequest.
I don’t want to write my controllers and actions twice, so I’ve been trying to come up with a nice solution for it…
Now, as mentioned above, I want to write as little extra code in my controllers as possible for handling Ajax requests.
Let’s take an example: A controller with actions for displaying a form, validating POST from the form and displaying a message on success.
So in the traditional approach, the user would get the form. They fill the form and send it to the server. The validate action looks at the data and if something is wrong, it assigns some variables to the view for filling in error messages in the form. If the data is OK, it redirects the browser to the success message action.
Now, take the same with Ajax…
User gets the form, fills it and submits it. Ajax kicks in, sends the data to the server, gets a response and calls a function for parsing the response. The parser script then can display the errors or redirects the user if the action was succesful.
The typical way to implement the validation would be something like this:
Check data validity, get possible error messages and add them to the view. If there were no errors, redirect to success page. But, if the request was made with XHR, the data has to be converted into some format that can be then parsed in the client-side script, such as JSON. So the errors have to be converted to JSON and if there were no errors, send something to indicate success.
As you can see, there’s a fair amount of extra code for the case where the request was made with Ajax. In addition, you would need to write the client-side parser script to place the messages in the correct elements or redirect the user etc.
I realize that adding some extra code like this is not necessarily a bad thing. If you’re just doing one or two actions which may use Ajax, it may be just fine. If you have many many different actions with stuff like this, then it can get problematic.
A better solution
So what could we do to save ourselves from the extra code?
Well, firstly, we could quite easily make the JSON-conversion of errors automatic:
As we are assigning the variables to the view, what if the view was not a view, but a JSON-converter? If you have looked at the Svn repository in the Code section of this site, you may have noticed something called CU_View_Json. That is basically what I’m talking about here: a class, which works like a view, but doesn’t need a template/viewscript.
As the class works like a view, you can just replace the default view with it. Check if the request is Ajax and replace the default view (and disable layout if you’re using that). Nothing else is needed. Your actions will still work like they always did, but instead of HTML, the output will be JSON (or some other format).
How does it decide what to put in the JSON? – Simple! It will just create JSON from all the view variables.
The above is improved more by having smaller actions in your controllers. At least in Zend Framework now with Zend_Layout, you can construct your big views from more than one action. This way you can group some functionality to an action and only call that action with Ajax instead of calling the “main” action which does more processing. Even though the small action would normally produce HTML in the layout, calling it with Ajax and using the JSON view, it will produce JSON.
The approach described above is not completely without problems. There are special cases in the code and view which you will need to find a way to handle.
The two special cases I can think of are redirects and loops:
Your controller might perform a redirection after a specific action, and Ajax calls cannot detect this: They will simply return you the data from the redirected URL. In ZF it’s possible to “intercept” redirections, so you can stop it and return something else to indicate a redirect.
I’m currently working on some code which does most of the above. It can handle redirects in a quite nice way etc. but the code is still pretty “raw”. Any opinions on the idea and the solution are welcome, as usual.