JavaScript Discreto (UJS) e AJAX no Ruby on Rails
E ai pessoal, hoje estou aqui para ensinar como utilizar JavaScript discreto (unobtrusive JavaScript - UJS) no Ruby on Rails para implementação AJAX.
Vamos supor que temos um scaffold simples gerado pelo Rails e desejamos implantar requisições assíncronas via JavaScript.
Poderiamos criar um arquivo JavaScript e com a ajuda de algum framework como jQuery fazer uso das funções apropriadas para isso..
Mas nosso objetivo aqui é utilizar o JavaScript discreto que vem por padrão com o Rails desde a versão 3.
JavaScript Discreto
As versões antigas do Rails, utilizavam-se de um JavaScript inline (junto com HTML) e dependente de recursos do framework Prototype para implementação de funcionalidades como submissão de formulários de maneira assíncrona. Isso deixava o código mais sujo e não muito manutenível.
Para resolver esse problema passou-se a utilizar uma biblioteca chamada Unobtrusive JavaScript que permite a geração de código mais limpo e bem organizado utilizando recursos de HTML 5. Existem versões para vários frameworks JavaScripts (como jQuery, Prototype e MooTools), assim o desenvolvedor não precisa ficar preso há um framework como era antes.
Utilizando UJS para AJAX
Agora vamos ver como utilizar a biblioteca que citamos acima para AJAX.
Primeiro de tudo, precisamos garantir duas coisas:
- O layout da aplicação está em HTML 5: seu HTML deve começar com
<!DOCTYPE html>
. - Temos incluso o jQuery e o UJS para jQuery no layout: se você tiver
<%= javascript_include_tag "application" %>
no layout já estará sendo incluso pelo Rails.
Já temos o que precisamos para começar, que tal agora passarmos o método de exclusão do scaffold para AJAX?
Na listagem de registros temos o seguinte código gerado pelo Rails:
Irei dizer ao Rails para que o método de exclusão seja chamado de forma assíncrona, apenas adicionando o trecho remote: true
, veja:
1
2
3
4
5
6
7
8
9
10
<% @posts.each do |post| %>
<tr class="<%= post.id %>">
<td><%= post.name %></td>
<td><%= post.content %></td>
<td><%= post.date %></td>
<td><%= link_to 'Show', post %></td>
<td><%= link_to 'Edit', edit_post_path(post) %></td>
<td><%= link_to 'Destroy', post, method: :delete, remote: true, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
tr
de cada registro com o id, isso será usado para remover a linha com JavaScript depois que o registro for excluído.Olhando o código fonte, veremos que o Rails adiciona um atributo data-remote
com o valor true
em nosso link, isso é o que a biblioteca UJS precisa saber para fazer a requisição via AJAX.
Se você for na listagem de registros e clicar no link Destroy
verá que a requisição já está sendo de forma assíncrona, só que como o Rails ainda não sabe como manipular ela, obtemos um erro de rota.
Para resolver isso, basta dizermos ao Rails como responder as chamadas feitas via JavaScript adicionando format.js
no respond_to
da action que está sendo requisitada. Com isso nossa action destroy
ficará assim:
1
2
3
4
5
6
7
8
9
10
def destroy
@post = Post.find(params[:id])
@post.destroy
respond_to do |format|
format.html { redirect_to posts_url }
format.json { head :no_content }
format.js
end
end
Esse trecho adicionado fará com que o Rails procure por um arquivo com o nome da action e com a extensão .js.erb
na pasta de views, nesse caso, ele procurará por destroy.js.erb
. Dentro desse arquivo podemos colocar código JavaScript e Ruby que serão executados depois da chamada assíncrona ter sido executada.
Em meu arquivo destroy.js.erb
eu adicionei o código abaixo responsável por excluir a linha do registro excluído:
Callbacks AJAX via Eventos Customizados
Imagina que desejamos aplicar exclusão via AJAX para todos CRUDs de nossa aplicação, existe uma maneira mais fácil de executarmos algo no callback da requisição assíncrona invés de ter que criar um arquivo .js.erb
para cada action.
As chamadas AJAX do UJS nos fornece os seguintes callbacks para as requisições assíncronas:
- ajax:before: antes da chamada AJAX
- ajax:loading: antes da chamada AJAX, mas depois da criação do objeto
XmlHttpRequest
- ajax:success: chamada AJAX executada com sucesso
- ajax:failure: chamada AJAX que falhou
- ajax:complete: chamada AJAX terminada (executada depois do
ajax:success
eajax:failure
) - ajax:after: depois da chamada AJAX ter sido feita (antes do retorno)
Na action podemos definir que não queremos nenhuma renderização quando a requisição vier por JavaScript:
1
2
3
4
5
6
7
8
9
10
def destroy
@post = Post.find(params[:id])
@post.destroy
respond_to do |format|
format.html { redirect_to posts_url }
format.json { head :no_content }
format.js { render nothing: true}
end
end
Então criamos um bloco JavaScript no application.js
responsável por tratar todos callbacks de chamadas AJAX com sucesso por exemplo:
Nesse exemplo, para cada link de exclusão que for via AJAX será necessário adicionar a classe delete-link
para passar pelo callback que removerá a linha do registro removido.
Essa foi a dica de hoje. O que vocês acharam?
Até mais.