Jcontroller Gem Version Build Status Code Climate

Rails controller based javascript to keep your javascript outside of your views.

Based off Paul Irish's DOM-based Routing (or Garber-Irish Implementation). Works with turbolinks.

How it works

Jcontroller.create('users', {
    html: {
        // executes whenever users#index with html format is executed
        index: function() {}
    }
});

No other code is needed.

Installation

Add gem 'jcontroller' to your application's Gemfile and run the bundle command, then add this to your app/assets/javascripts/application.js

//= require jcontroller

Controllers

Namespaces

Jcontroller creation and finding are based off the controller path.

// for Admin::UsersController
Jcontroller.create('admin/users', {});

Filters

Jcontrollers can be created with before and after filters like so:

Jcontroller.create('users', {
    html: {
        // executes for all html format responses for UsersController, before the specific action
        before: function() {}
        // executes whenever users#index with html format is executed
        index: function() {}
        // executes for all html format responses for UsersController, after the specific action
        after: function() {}
    }
});

Inheritance

By default, jcontrollers inherit from the application jcontroller and will execute it if it exists, such as:

Jcontroller.create('application', {
    html: {
        before: function() {}
        index: function() {}
        after: function() {}
    }
});

So with the jcontrollers above the order of execution is:

  • application.before
  • users.before
  • application.index
  • users.index
  • application.after
  • users.after

You can also set your own inhertance chain:

Jcontroller.create('users', { 
    parent_path: 'users_base',
    ...
});

API

  • Parameters are accessed from this.params or as the first parameter
  • The request state (controller_path, action_name, jcontroller, etc.) are also given in this.state or the second parameter
  • And other methods to work with jcontrollers

    Jcontroller.create('users', {
    html: {
        index: function(params, state) {
            //this.params === params
            console.log(this.params);
            //this.state === state
            console.log(this.state);
    
            var jcontroller = JController.find('application');
            self.parent(); // === jcontroller
    
            //excute application_jcontroller for this state and params again
            jcontroller.execute_jaction(this.params, this.state);
            //execute application_jcontroller html.index function
            jcontroller.html.index();
        }
    }
    });
    

    Organization

    I like having a jcontrollers directory and calling my files as jcontroller files (ex. users_jcontroller.js).

    Parameters

    Manual

    Use the js method with the params option.

    class UsersController < ApplicationController
    def show
        @user = User.find(params[:id])
        js { :params => { :id => @user.id } }
    end
    end
    

    From view template

    You can also create parameters using a JSON DSL (such as jbuilder) by creating a template named <action_name>_params.js.<DSL suffix>:

    # app/views/users/show_params.js.jbuilder
    json.id @user.id
    

Controlling javascript execution

Stop

Stop all execution of all filters and methods for the action:

class UsersController < ApplicationController
    def index
        js false
    end
end

Different jcontroller

Execute a different jcontroller:

class UsersController < ApplicationController
    def index
        # same as "users#index.html", parameters and options are optional
        js "users/show.html", { :params => { ... } }
    end
end

HTML view

Execute all filters and actions related to a action:

<!-- same as "users#index.html", parameters and options are optional -->
<%= execute_jaction "users/show.html", { :params => { ... } } %>

Manually filter in Javascript

You can use the given state to stop execution of functions:

Jcontroller.create('application', {
    html: {
        before: function() {
            if (this.state.action_name === 'destroy') { }
        }
    }
});

Redirect

You can execute all filters and functions of the current action before the redirected action using:

class UsersController < ApplicationController
    def index
        js { :redirect => true }
        redirect_to user_path(User.first)
    end
end

So users/index.html will be executed before users/show.html.

Ajax

You can optionally execute jcontrollers for ajax instead of writing javascript in views by turning it in config/application.rb:

Jcontroller.ajax = true

Jcontrollers will automatically execute with parameters given by the template with a JSON DSL:

# app/views/users/show.js.jbuilder
json.id @user.id

Credits

Extracted out of Placemark. Originally called dom_routes. An alternative is paloma.