views:

982

answers:

3

In an attempt to keep my scripts maintainable, I'm going to move each into their own file, organised by controller and action:

// scripts which only apply to /views/posts/add.ctp
/app/webroot/js/page/posts/add.js

// scripts which only apply to /view/users/index.ctp
/app/webroot/js/page/users/index.js

That's all cool, however I'd like for these to be automatically added by the Controller, since it obviously knows the name of both the controller and action.

I figure the best place for this is in AppController::beforeRender(). (yes?)

The only problem is that I don't know how to actually add this into the $scripts_for_layout variable. I thought that getting a reference to the javascript helper object would work, but I can't find it from the controller!

class AppController extends Controller {
    var $helpers = array("javascript", "html", "form");

    function beforeRender() {
        // ???
    }
}
+1  A: 

The best way I can think of is to create your own custom AppView and have all your controllers use that:

class myController extends AppController {
  var view = 'AppView';
  ...
}

Then, somewhere in your AppView, you'd want to do something like:

function __construct(&$controller, $register){
  parent::__construct($controller,$register);
  $this->addScript('<script type="text/javascript" src="/path/to/js/' . $this->controller . '/' . $this->action . '.js"></script>');
}

But I'd take a step back and think about a few things, first.

How big are your scripts, on average? Is the overhead of an external script call (before the script is cached by the client) better than adding a few hundred bytes to your main output stream (by just sticking the script into the page, inline)?

Perhaps you'd be better of somewhere in the middle -- split your scripts up by controller, but not action. That way, after the first visit to any action, the client has all scripts for all actions. This way, you avoid a big initial download for all the application's script, but you avoid adding N http round-trips (where N is the number of actions a brand new user visits).

Another way to approach the problem is to do it all in javascript. Just figure out a lazy-loading scheme. So your app just loads a very small loader.js, and that script figures out which other javascript sources to pull in.

Note: I've never tested my extend-the-view hack, but I bet it'll work if you really want to do this.

timdev
+5  A: 

Very easy to do in your default.ctp layout file:

An example to automatically include .css files per controller and/or controller/action (because I had this lying around, easily adaptable to .js files):

<head>
...
<?php
    if (is_file(WWW_ROOT."css".DS.$this->params["controller"].".css")) {
        echo $html->css($this->params["controller"]);
    }
    if (is_file(WWW_ROOT."css".DS.$this->params["controller"].DS.$this->params["action"].".css")) {
        echo $html->css($this->params["controller"]."/".$this->params["action"]);
    }
?>
...
</head>
deceze
+1 for clean approach and understanding. `APP.WEBROOT_DIR.DS` can be replace with the `WWW_ROOT` constant.
deizel
Good point, should be less error prone when changing the directory structure too. Changed it. :)
deceze
deceze... seriously... you are like the only Cake guru on SO, I swear. In the last 3 weeks I must have given you like 400 rep myself. :)
nickf
Lol! All rep points were gladly accepted. ;D
deceze
+1  A: 

Like deceze is saying, we do it using the layout, although I find our solution a bit more elegant :)

In default.ctp:

if(isset($cssIncludes)){
    foreach($cssIncludes as $css){
        echo $html->css($css);
    }
}

if(isset($jsIncludes)){
    foreach($jsIncludes as $js){
        echo $javascript->link($js);
    }
}

Then, in our controller actions, we define these arrays:

$this->set('cssIncludes',array('special')); // this will link to /css/special.css
$this->set('jsIncludes',array('jquery'));   // this will link to /js/jquery.js

For files that need to be loaded in each view, we simply add the same type of link "statically" to the top of the layout, like:

echo $javascript->link('global');
echo $html->css('global');

This works really well for us. Good luck!

inkedmn