How to organize your CakePHP App's Javascript?

Posted by Felix Geisendörfer, on Oct 10, 2006 - in JavaScript & jQuery

Deprecated post

The authors of this post have marked it as deprecated. This means the information displayed is most likely outdated, inaccurate, boring or a combination of all three.

Policy: We never delete deprecated posts, but they are not listed in our categories or show up in the search anymore.

Comments: You can continue to leave comments on this post, but please consult Google or our search first if you want to get an answer ; ).

Ok, I'm no expert on Javascript but I know enough to have some fun with it. Unlike lot's of folks, I'm not into using all those bloated JS libs, frameworks and other things around. However, I'm in love with the lightweight jQuery library, since it allows to write amazingly elegant code that's easy for others to read, and fun to work with. One of the coolest aspects about jQuery is that it doesn't use prototypes to turn Javascript into a new language (like the Prototype library does), but rather encapsulates all functionality into the $ object that is used as a wrapper/selector for the elements you want to manipulate. So if your app includes more then 100 kbyte of JS bloat right now, do yourself a favor and switch to the fresh jQuery 1.0.2. It will not only reduce your JS dependencies to 15kb, but also give you the chance to reduce your own code considerably (2-3x for me).

I didn't mean to go off topic, but when talking about JS I just *have* to mention jQuery these days ; ). To me it's the frontend counterpart for RoR/CakePHP in terms of allowing Rapid Application Development. I also highly recommend it for prototyping web applications (I used it a little bit for my previous redesign attempt).

But what I actually want to start a discussion about, is how one should go about organizing his custom JS code, especially when working with CakePHP. In my early JS days I used to have one or more JS files with a plain list of functions. Most of the time I ended up with ugly spagetti code, so I decided to change my approach. Right now I use the JS version of a globally available Singleton that always has a sub-object called Behaviors that contains a variable amount of functions to be executed when the DOM is ready. The main advantage of this approach is that you can organize your applications functionality in hierarchies, while not having to worry about scoping issues.

But talk is cheap, so let's take a look at a simple example taking out of my Caketaster project that is supposed to enhance the User interface.

javascript
  1. var Caketaster = new function()
  2. {
  3.     this.initialize = function()
  4.     {
  5.         Caketaster.Behaviors();
  6.     }
  7.    
  8.     /**
  9.      * All custom behaviors we want to add to our elements are handled in this
  10.      * part of the Caketaster Site Controller
  11.      **/
  12.     this.Behaviors = function()
  13.     {
  14.         for (var fct in Caketaster.Behaviors)
  15.         {
  16.             if (typeof(Caketaster.Behaviors[fct])=='function' && !(Function[fct]))
  17.             {
  18.                 Caketaster.Behaviors[fct]();
  19.             }
  20.         }
  21.     }
  22.    
  23.     this.Behaviors.collapseAllTestCases = function()
  24.     {
  25.         $('ul ul').hide();
  26.     }
  27.    
  28.     this.Behaviors.toggleTriggers = function()
  29.     {
  30.         $('h2').click(function(){$('ul', this.parentNode).toggle();});
  31.     }
  32.    
  33.     this.Behaviors.addJsControls = function()
  34.     {
  35.         $('form').after('<div id="js-control"><a href="#">Expand All</a> <a href="#">Expand All Failed</a> <a href="#">Expand All Passed</a></div>');
  36.         $('a', '#js-control').click(Caketaster.Tests.jsControl);
  37.     }
  38.    
  39.     this.Tests = function(){};
  40.     this.Tests.jsControl = function(e)
  41.     {
  42.         e.preventDefault();
  43.        
  44.         var commands = this.innerHTML.split(' ');
  45.        
  46.         if (!commands[2])
  47.             var selector = 'ul ul';
  48.         else
  49.             var selector = 'ul .test-'+commands[2].toLowerCase()+' ul';
  50.  
  51.         if (commands.shift()=='Expand')
  52.         {
  53.             $(selector).show();
  54.             this.innerHTML = 'Collapse '+commands.join(' ');
  55.         }
  56.         else
  57.         {
  58.             $(selector).hide();
  59.             this.innerHTML = 'Expand '+commands.join(' ');
  60.         }
  61.        
  62.         return false;
  63.     }
  64. }
  65.  
  66. $(document).ready(Caketaster.initialize);

This code essentially only adds some JS controls to the test suites user interface allowing to expand & collapse the test cases that were executed.

The name of the Singleton is always the name of the project I use it in. But I guess it would be better to name it SiteController or something like this since I always have to replace all occurences of the Project name when reusing it right now. But besides that, the concept has worked very nicely for me so far, keeping in mind that I'm not surfing the web 2.0 wave too hard, having only small JS needs.

But I know a lot of you folks are writing as much JS as PHP for their applications, so my question is, how do you organize your CakePHP's applications JS? Is somebody using a more advanced M(html), C(js), V(css) pattern for his front end? I think making some CakePHP variables like webroot, here and base is a cool thing to do, but I guess there is more. I'm not asking for a super complex framework, but just some best practices / conventions you guys are using to streamline your JS workflow and maybe some comments on my current approach.

--Felix Geisendörfer aka the_undefined