Go to English Blog

Disparando JavaScript por página no Rails

Leia em 2 minutos

Executar JavaScript por escopo de página é algo que todo mundo precisa. Existem diversas soluções disponíveis, mas faz algum tempo que uso uma biblioteca que criei chamada Dispatcher. Com ela é possível especificar callbacks before e after, além de ter executar automaticamente o JavaScript de uma página.

Neste artigo vou mostrar como você pode usar esta biblioteca, integrando-a inclusive com o Turbolinks.

Instalação

Para instalar o Dispatcher, você pode copiar os arquivos da biblioteca para o diretório da sua aplicação. Claro que isso é pedir para que os arquivos fiquem desatualizados, então vou mostrar como usar o Bower.

Crie o arquivo .bowerrc e configure o diretório de instalações para vendor/assets/components.

{
  "directory": "vendor/assets/components"
}

Agora, crie o arquivo bower.json, que irá armazenar suas dependências.

{
  "name": "myapp",
  "version": "0.0.1",
  "private": true
}

Finalmente, instale a biblioteca.

$ bower install dispatcher --save
bower dispatcher#*          not-cached git://github.com/fnando/dispatcher-js.git#*
bower dispatcher#*             resolve git://github.com/fnando/dispatcher-js.git#*
bower dispatcher#*            download https://github.com/fnando/dispatcher-js/archive/v0.1.0.tar.gz
bower dispatcher#*             extract archive.tar.gz
bower dispatcher#*            resolved git://github.com/fnando/dispatcher-js.git#0.1.0
bower dispatcher#~0.1.0        install dispatcher#0.1.0

dispatcher#0.1.0 vendor/assets/components/dispatcher

Se você quer saber mais sobre o Bower, veja este artigo que escrevi sobre o assunto.

No arquivo app/assets/javascript/application.js, adicione as linhas abaixo:

//= require jquery
//= require dispatcher

Pronto! Agora você já pode escrever o código do seu roteador.

Definindo suas rotas

A ideia do Dispatcher se baseia em ter um controller e action. A definição da rota pode ser feita em um objeto, como no exemplo abaixo.

var MyApp = {};

MyApp.site = {
  index: function(){
    console.log("Welcome to the home page");
  }
};

Para executar esta rota, você deve executar a função Dispatcher.run(), como no exemplo abaixo:

Dispatcher.run(MyApp, "site#index");

Agora que você já sabe como é o conceito por trás da execução, vamos automatizar o processo em uma aplicação Rails.

Para definir a rota que deve ser executada no JavaScript, vamos criar um helper chamado dispatcher_route. Ele irá compor a rota no formato controller#action.

module ApplicationHelper
  def dispatcher_route
    controller_name = controller_path.gsub(/\//, "_")
    "#{controller_name}##{action_name}"
  end
end

Adicione um atributo data-route no elemento <body> com o valor retornado pelo este método.

<body data-route="<%= dispatcher_route %>"></body>

Crie um arquivo app/assets/javascripts/boot.js que irá executar a função Dispatcher.run quando a página estiver pronta para ser manipulada. Você precisará informar o aplicativo e a rota.

$(function(){
    Dispatcher.run(MyApp, $("body").data("route"));
});

No entanto, você não precisa fazer esta configuração manualmente; Se você quiser usar a sugestão acima através do atributo data-route, execute apenas a função Dispatcher.init. Note que você não precisa envelopar a chamada no wrapper do jQuery, já que isso é feito internamente.

Dispatcher.init(MyApp);

Pronto! O Dispatcher irá executar o JavaScript específico para aquela página, caso ele tenha sido definido.

Executando coisas antes e depois das ações

O Dispatcher permite executar coisas antes e depois de uma ação. Você pode definir callbacks globais ou específicos de um controller.

Para definir um callback global, crie os atributos before e after na raíz do objeto que representa o aplicativo.

var MyApp = {
    before: function() {
      console.log("[before] Executed in every single page!");
    }

  , after: function() {
      console.log("[after] Executed in every single page!");
    }
};

Você também pode definir esses mesmos callbacks por controller.

var MyApp = {};

MyApp.site = {
    before: function() {
      console.log("[before] Executed before each site's actions!");
    }

  , after: function() {
      console.log("[after] Executed after each site's actions!");
    }
};

Integrando com o Turbolinks

Se você usa Turbolinks, saiba que o Dispatcher possui suporte para ele. A configuração é a mesma que a versão sem o Turbolinks; basta executar a função Dispatcher.init, passando o aplicativo. A rota é retirada do atributo data-route do elemento <body>.

var MyApp = {};
Dispatcher.init(MyApp); // Turbolinks

O Turbolinks dispara o evento page:load sempre que uma página for carregada. O Dispatcher ouve este evento e também o load; por isso não é preciso fazer nada especial para que o Dispatcher funcione com ou sem Turbolinks.

Finalizando

Como você pode perceber, executar JavaScript específico de uma página pode ser bastante simples, sem ter que recorrer ao truque do content_for.

Lembre-se que a ideia é usar o Dispatcher como um roteador. Por isso, escreva o seu código de forma modular e apenas inicialize os objetos necessários para que a mágica aconteça!