Railsにかぎらず,ある程度のフレームワークに乗っかって開発していると「できることはできるんだけど,ちょっと好みに合わせたい」みたいな要望が発生してくる.
今回のも,そんな「気を使えばできるけど頑張りたくない」ときに便利なことです.
Railsはjavascriptをassetsというフォルダの配下においている.
この辺の活用方法はSprocketsやAssetPipelineを理解してもらうとして,とにかくそういう構成になっている.
Railsは,何もせずそのまま"application"
のレイアウトを使用していると,このassetsフォルダ配下にあるjsをすべて読み込んでいる.
なんでこうなっているかというと,デフォルトで用意されているapplication.jsが原因だ.
application.js内には
//= require .
という記述がある.
そしてこのapplication.jsはapp/views/layouts/application.html.erb
内で呼び出されている.
ということは,レイアウトとして"application"
を使うページでは,app/assets/javascripts
内のすべてのファイルが読み込まれていることになる.
jsが複数読み込まれると困る問題
時と場合によるのだが,それなりに大きなアプリケーションになってくると,jsをすべて読み込むというのが非常に困ることがよくある.
例えば,
app/assets/javascripts/statics.js.coffee
$ -> $(".sample").click ()-> $(@).hide()
と,
app/assets/javascripts/homes.js.coffee
$ -> $(".sample").click ()-> alert("test")
こんなファイルがあったとしよう.
実はこの2つ,まったく別のリソースで,StaticsController
とHomesController
でそれぞれ使う記述だとする.
しかし,同じクラスにバインドしているのだから,当然どちらのページでもsample
クラスが付いたhtml要素に対するクリックイベントで発火してしまう.
これだけの規模であれば全く問題ないが,そもそも「どこでどのクラスをつけていたか」なんてことをすべて頭に入れながらなんて,常人のやるべき手法ではない.
というか,そもそも関係ないページのjsが実行されている事自体,まったくもってスマートとは言い難い.
というわけで分けよう
そんなわけで,呼び出すjsをコントローラ単位で分けていこう,というのが一番まっとうなやり方だと思います.
あくまで読み込みは全ファイル対象に行います.
呼び出すものをわけます.
とりあえず,js側からcontroller名,action名が取得できるようにしましょう.
app/views/layouts/application.html.erb
<body data-controller="<%= params[:controller] %>" data-action="<%= params[:action] %>"> <%= yield %> </body>
としておきます.
で,
app/assets/javascripts/application.js
//= require .
ここはそのまま.
新たに,_initial.js.coffeeというファイルを作ります.
app/assets/javascripts/_initial.js.coffee
#= require underscore.string $ -> $body = $("body") controller = _.str.classify($body.data("controller") + "-controller") action = $body.data("action") if window[controller] activeController = new window[controller] if activeController && $.isFunction(activeController[action]) activeController[action]()
underscore.stringを使って名前を加工します.
導入は,以下を参照.
epeli/underscore.string · GitHub
rails-assetsを使うのもアリです.
_initial.js.coffeeは実行されるたびに,controller名,action名が見合った形のクラスを呼び出します.
対象となるクラスは以下のような形で作っておきます.
app/assets/javascripts/statics.js.coffee
class @StaticsController index: -> $(".sample").click ()-> $(@).hide()
見事ここだけ呼び出されます.
turbolinksどうする問題
ここまででうまく呼び出すクラスを分けられた気がしますが,Rails4.0以降にはturbolinksというものが標準搭載されています.
Rails - Turbolinksをオフしないためにやった事 - Qiita
これが有効になっているため,_initial.js.coffeeの$ ->
が,初回のページロード時しか発動しません.
つまり,ページ遷移した場合には,$ ->
が発火しないので,新たに関数呼び出しも行ってくれません.
jquery-turbolinksを使おう
素直にjquery-turbolinksを使いましょう.
kossnocorp/jquery.turbolinks · GitHub
これでturbolinksを使った状態で,該当するコントローラの該当するアクションに相当するjsだけ呼び出すことができます.
何が嬉しいって,application.jsではすべてのjsファイルを読み込んでいる上で,該当するクラスを実行しているだけなので,turbolinksと合わせると本当にページ遷移時のロードがなくなります.
それでいて,これだけきっぱりjsを分離できるので,jsが多めになってしまっても非常にすっきりしていて開発しやすい.