Rails Lazy Loading of Scripts

The catch with using the Rails 3 Asset Pipeline is that all scripts (javascripts and coffeescripts) in app/assets/javascripts are compiled, merged, and minified into a single file: application.js. But, what if you don’t want a particular script dynamically (lazily) loaded only when necessary? Where do you store it? How do you reference it?

Where do you store it?
You can still keep all of the scripts tidy in the app/assets/javascripts folder. However, you should create subfolders for your dynamically loaded scripts. Once you move a script to a subfolder in the app/assets/javascripts folder, the manifest within application.js won’t find the new script; hence, the file won’t be merged and minified into application.js! Let’s see an example.

Let’s say that I want to lazily load a script: foo.js.coffee. Why Coffeescript? Because it’s amazing. I would then create a subfolder within app/assets/javascripts such as foo/. Note, you do not have to name it anything close to the script’s name.

Now that our script is in the right place, we have to figure out how to reference it within a view.

How do you reference it?
In your view (HAML, HTML, or otherwise), you can use JQuery’s getScript() function to retrieve the script. This, of course, requires that you have a reference to JQuery in your project.

In a view (app/views/bar.html.haml, for example), you can embed a call to the getScript() function to grab foo.js.coffee as follows:

   $.getScript("assets/foo/foo.js", function(){
       console.log("Foo found! Yay!");

Notice that we don’t include the .coffee extension. This gets removed after an automatic compilation of the coffeescript into pure javascript when the script is served. Also notice how we didn’t have to include the /javascripts/ folder in the assets filepath.

What’s really awesome about getScript() is that there’s a handler that can be defined when the script is successfully retrieved! In the code above, we’re defining an anonymous function that prints to the console indicating that the file was found.

It’s really important to define what happens when the file is successfully retrieved. Let’s say you’re defining some objects in foo.js. If you try to use those objects while the file is asynchronously being fetched (i.e., not using the handler and just writing operations after the call to getScript()), you’ll run into errors. Hence, you can be sure that any objects in foo.js exist by waiting for the file to be fetched and then carrying on with your operations.

Hope it helps!