ボクココ

個人開発に関するテックブログ

Rails assets 内の JavaScript のメソッドを View 内から呼び出す方法

ども、@kimihom です。

Rails で開発していると、assets/javascripts 内で定義したメソッドを View 内に書かれた <script>タグから呼び出したい時がある。Rails は基本的に assets/javascript 内のコードを全てひとまとめにして一つのapplication.jsってのを作るから、特定のページのロード完了後に特定の JavaScript コードを呼び出したい時にちょっと困ることになる。そんな時は View 内に <script>タグを入れる必要が出てくるだろう。

View 内と Assets 内の JavaScript の混在による課題

だが、View と Assets 内に JavaScript を混在させるといろいろな問題が出てくる。そう簡単にはいかない問題だ。

外部ライブラリの読み込みの前にメソッドを呼んでしまう

jQuery のような JavaScript ライブラリは、より早く HTML を表示させるために <head>タグではなく、<body>の一番下に書くことがあると思う。そうなると、レイアウト内の yield の後に jQuery が読み込まれることになる。つまり、View 内でベタ書きした <script> タグの方がライブラリよりも先に呼ばれてしまうことになる。これはよろしくない。


app/views/layouts/application.html.erb

<html>
<head>
  <title>Sample App</title>
  <%= stylesheet_link_tag    'application', media: 'all' %>
  <%= csrf_meta_tags %>
</head>
<body>
   <%= yield %>
   <%= javascript_include_tag 'application' %>
</body>
</html>


app/views/users/index.html.erb

<div id="users">
  .....
</div>
<script>
  $("#users")....   // <- "$" が呼び出せない!!
</script>

ソースコードのカオス化

View 内に <script> タグをどんどん入れて行ってしまったり、ロジックをその中にベタ書きしてしまうと、assets/javascripts にある JavaScript ファイルとごちゃごちゃになってメンテナンス困難なソースコードが出来上がってしまう。

だからできれば assets/javascripts 内に JavaScript のコードを集約させたい。

View から Assets 内の JavaScript を呼び出せない

かといって、assets/javascripts 内で functions を定義しても、Rails の AssetPipeline による JavaScript の Minify によってメソッド名を指定することができない。

app/assets/javascripts/users.js

function setUser() { 
  ......
}


app/views/users/index.html.erb

<div id="users">
  .....
</div>
<script>
$(function() {
  setUser(); // <- "setUser" が呼び出せない!!
});
</script>

content_fortrigger によって解決しよう

さて、本記事のメインテーマである解決策を掲示しよう。まずは1点目の <script>タグの読み込み順について。これは content_for を使うことで解決できる。

app/views/layouts/application.html.erb

<html>
<head>
  <title>Sample App</title>
  <%= stylesheet_link_tag    'application', media: 'all' %>
  <%= csrf_meta_tags %>
</head>
<body>
   <%= yield %>
   <%= javascript_include_tag 'application' %>
   <%= yield :footer %>
</body>
</html>


app/views/users/index.html.erb

<div id="users">
  .....
</div>

<%= content_for :footer do %>
<script>
  $("#users")....   // <- "$" が呼び出せる!
</script>
<% end %>

これで、 jQuery が読み込まれた後に、 <script> タグを読み込ませることができる。

続いて JavaScript の Minify によって指定のメソッドが呼び出せない問題。これは trigger を定義してあげよう。

app/assets/javascripts/users.js

$(function() {
  $(document).on("users:loaded", "#users", function() {
      //.....
  });
});


app/views/users/index.html.erb

<div id="users">
  .....
</div>

<script>
<%= content_for :footer do %>
<script>
  $("#users").trigger("users:loaded");
</script>
<% end %>

trigger には引数を与えることもできるので、とても柔軟に View と Assets の JavaScript を通信することが可能だ。

Rails の content_for と jQuery の triggerによって、個別 View のロード時に特定の JavaScript を実行することができるようになった。

終わりに

trigger はいろいろと応用が利くとても便利な jQuery メソッドだ。より詳しく知りたい方は以下の記事も参照されるといいだろう。

今更ながら jQuery の trigger の魅力について語らせてもらう

Rails のレールに乗った美しいコーディングを目指していこう。