TL;DR(使いたかっただけ)
Railsで非同期処理を実装するための、Ajax(AsynchronousJAvascript+Xml)対応。
それを超絶シンプルな形で実装/解説してみようと思う。
今回最終的に実装したいのは超簡単ないいねボタン。
(非同期の意味がないとは言わない。まあツイッターの「いいね」も非同期だし…)
ボタンを押すと
黒くなる。
また押したら白くなる、というボタンを作ります。
早速!
今回のファイル構成
app/
controllers/
study_controller.rb
views/
study/
_dislike.html.erb
_like.html.erb
ajax.html.erb
dislike.js.erb
like.js.erb
config/
routes.rb
正直今回の機能でルーティングを設定する意味はあまりないんだけど、
俺みたいにprogateで学んでいざやってみたらハマったみたいな人のためにあえてroutes.rb
を介してやっていこうと思う。
基本的なHTML、Routes、Controller
<h1>いいねボタン</h1>
<h1><div id="likeBtn"><%= link_to "◯", "/study/like" , remote: true %></div></h1>
Ajax対応させるためには、link_to
メソッドにremote: true
を追記するだけで良い。
これだけで「非同期処理」自体は実装できるから、このリンクを押した場合は画面の更新がされない。
Rails.application.routes.draw do
#TopPage
root "study#ajax"
#For Ajax
get "study/like" => "study#like"
get "study/dislike" => "study#dislike"
end
まずトップページとしてajax.html.erb
にリンクさせておく。
link_to
メソッドの"/study/like"
でstudy_controller.rb
のlike
アクションにリンクを飛ばすように設定する。
dislikeは後述。
class StudyController < ApplicationController
#TopPage
def ajax
end
#For Ajax
def like
end
def dislike
end
end
今回は超絶単純なAjaxによる非同期処理を実装/解説するため、無駄な処理はすべて不要!
Ajax機能特有の部分
ここからが本題のAjax機能となるから、詳しく理解していきたい。
まず処理のイメージについて。
この白い丸のボタン(リンク)を押したら
ajax.html.erb
の<%= link_to "◯", "/study/like" , remote: true %>
が実行される。
/study/like
に飛ぶとroutes.rb
の通り
study_controller.rb
のlike
アクションを実行する。
get "study/like" => "study#like"
def like
end
非同期処理でなければ(remote: true
がなければ)、通常通りlike.html.erb
が呼び出されるのだが、今回はそうしない。というかlike.html.erb
がないから呼び出されるものがない。
railsのAjax機能は、呼び出されたactionと同名のJavaScriptを実行する。
よって、ここで呼び出されるのはcontrollerでもhtmlでもなく、以下のファイルとなる。
(もちろん、controllerに処理があればそれも呼び出される)
$('#likeBtn').html(
"<%= escape_javascript(render'like') %>"
);
具体的な処理については(いくらでもサンプルがあるので)割愛するが、ここではid="likeBtn"
のグループをrender
で再レンダリングしている。
render'like'
で呼び出されるのはメソッドの引数であるlike
と同名のhtmlだが、render
メソッドの場合はそのhtml名の頭に_
(アンダースコア)をつけたものになる。
それが以下のファイルである。
<%= link_to "●", "/study/dislike" , remote: true %>
render
メソッドにより、_like.html.erb
が呼び出され、ajax.html.erb
(トップページ)の<div id="likeBtn">
の中身が_like.html.erb
に書き換えられる。
この状態でとりあえずトップページにアクセスして、白いボタンを押してみると……
黒くなった!
このとき、ajax.html.erb
は裏で以下のようになっている。
<h1>いいねボタン</h1>
<h1><div id="likeBtn"><%= link_to "●", "/study/dislike" , remote: true %></div></h1>
黒を白に戻す(前項と同じやり方)
ここまでくれば後は同じ手順で黒いボタンを押したときに白くするようにすればいい。
<%= link_to "●", "/study/dislike" , remote: true %>
画面の黒いボタンを押すと_like.html.erb
の通りroutes.rb
の/study/dislike
を見に行く。
Rails.application.routes.draw do
#TopPage
root "study#ajax"
#For Ajax
get "study/like" => "study#like"
get "study/dislike" => "study#dislike"
end
そしてアクションdislike
と同名のJavaScriptのdislike.js.erb
を実行する。
$('#likeBtn').html(
"<%= escape_javascript(render'dislike') %>"
);
render
メソッドにて_dislike.html.erb
をajax.html.erb
(トップページ)のlikeBtn
グループにレンダリングする。
<%= link_to "◯", "/study/like" , remote: true %>
これで黒いボタンを押してみると、白に戻ったはずだ。
その上、白のボタンをもう一度押してみると黒くなるし、黒のボタンを押してみると(ry
これにて、たぶんこれが一番単純だと思うAjax機能を用いた実装ができたかなと思います。
まとめ
個人的に最もハマったポイント
railsのAjax機能は、呼び出されたactionと同名のJavaScriptを実行するところ。
色々調べてたんだけど、どれもroutes.rbを仲介しない書き方を使っていてよくわからなかった。
まあ正直今回みたいにcontrollerを介する必要がない(処理がない)場合は、たとえば以下のようにも書くことができる。(というか書いた方がベスト)
<h1>いいねボタン</h1>
<h1><div id="likeBtn"><%= link_to "●", {action: :dislike} , remote: true %></div></h1>
<%= link_to "●", {action: :dislike} , remote: true %>
<%= link_to "◯", {action: :like} , remote: true %>
注意点としては、これだけだと実行されるJavaScriptファイルは上記3ファイルと同じディレクトリにないといけない。
今回の例で言えば/views/study/like.js.erb
みたいに。
逆にroutes.rb
でstudy_controller.rb
以外のコントローラーを指定した場合は、その別のコントローラーに対応するディレクトリにJavaScriptファイルを置いてあげる必要がある。
俺はここでめちゃくちゃハマって、時間を無限に溶かしてしまった。
感想
初めてWeb系の技術で書いてみたけど、案の定Too Longになってしまった。(楽しかった)
非同期処理を実装してみたいけどなにもわからない!っていう人がこれをみて、理解の一助になれればと思いました。
以上、ここまで読んでくれてありがとうございました!さらば!