Page specific, Dom-Ready Javascript Execution in Rails

An extension of an extension of Paul Irish’s Dom-Ready Execution

Page specific bindings in rails can be achieved in many different ways. Recently I came across this post by @brandonhilkert and it inspired me to post about the solution that I have been using for awhile.

Now it is important to note that I did not come up with what I currently use on my own. It all started with a solution that @paul_irish had posted in 2009 here and then was extended to be even more awesome by @jgarber here. With all of the creds handed out appropriately, I’ll show how a colleague @mtrpcic and I were able to extend @jgarber’s solution even further.

So alike @jgarber and @paul_irish’s solution the first thing required is to use the rails controller_name and action_name helper as data attributes on the body.

<body data-controller=“<%= controller_name %>” data-action=“<%= action_name %>” >

With that in place, the next thing I do is define my Application Object. This object will hold the controllers as well as properly dispatch javascript according to the controller and the action rendered. I usually keep this file in with the rest of my javascript libraries.

// Kept in /app/assets/javascripts/framework.js

function Application(config){
  this.name = config.name || "Application";
  this.version = config.version || "1.0";
  this.onError = config.onError || function(){};

  this.controllers = {};
}

Application.prototype = {
  dispatch: function(){
    // Get the current controller and action
    var controller = $("body").data("controller"); 
    var action = $("body").data("action");

    // If the controller and action are defined execute that js
    if(this.controllers.hasOwnProperty(controller)){
      var controller = this.controllers[controller];

      if(controller.hasOwnProperty(action)){
        controller[action]();
        return;
      }
    }
    // Otherwise trigger the specified onError function
    this.onError();
  },

  // Add a controller to the application object
  controller: function(controller){
    this.controllers[controller.name] = controller.actions;
  },

  // Initialize the Application Object
  init: function(){
    // Bind controller dispatch events
    var fn = function(){ window.app.dispatch(); }
    $(document).ready(fn); //full refresh
    $(document).on("page:load", fn); //turbolinks page change
  }
};

Once all of this is set up, you’re ready to define your application object.

  // Kept in /app/assets/javascripts/init.js
  window.app = new Application({
    name: "Sample App",
    version: "0.1"
  });

And lastly your controllers. For the sake of the example, I’ll stick with Brandon Hilkert’s example of running an alert on Pages#contact action.

// Kept in /app/assets/javascripts/controllers/pages_controller.js

app.controller({
    "name": "pages",
    "actions": {
      "contact": function(){
        alert("You're on the contact page!");
      }
    }
});

Then in my manifest file, after everything is included, I initialize the app object.

//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree 

app.init();

I like this approach for a few reasons. First, it lends itself to the readable and organized javascript within your rails app, as well as adds the modularity of storing each controller in it’s own controller file in a controllers folder amongst your assets. This provides a nice quick way to access the javascript that is being run on a specific page within your rails app. It also minimizes globals by namespacing your controllers to the application object.

And there you have it, my preferred solution to page specific, dom-ready javascript execution in rails. Again, thanks to @mtrpcic, @jgarber, @paul_irish and @brandonhilkert for inspiring this post.

 
49
Kudos
 
49
Kudos