Rails6でJavascriptが上手く動かない事象への対処法

はじめに
--
Railsアプリを作成している中で「application.html.erb」ファイルに記載していたJavascriptのコードを別ファイルに移動させたものの、Javascriptファイルが上手く動かなく、苦戦しましたため、備忘をこめて記載致しました。

開発環境
--
- Mac
- Ruby 2.7.2
- Rails 6.1.3.1


今回実装したいこと
--

以下コードに記載されてある２つ目のscriptタグに記載されているコードを新たにJavascriptファイルを作成して、「app/javascript/packs」下に格納することです。そして、新たに作成したJavascriptファイルをビューである「app/views/users/show.html.erb」から読み込むことです。

```javascript:app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>StopSweets</title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <meta name="viewport" content="width=device-width,initial-scale=1">

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

    <%# drawer.css %>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/drawer/3.2.1/css/drawer.min.css">
    <%# jquery & iScroll %>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/iScroll/5.1.3/iscroll.min.js"></script>
    <%# drawer.js %>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/drawer/3.2.1/js/drawer.min.js"></script>
    <link href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" rel="stylesheet">
    <%= favicon_link_tag('favicon.ico') %>
  </head>

  <body class="drawer drawer--left" style="padding-top: 5rem">
    <header role="banner">
      <%= render 'layouts/header' %>
    </header>
    <main role="main">
      <%= render partial: 'layouts/flash' %>
      <%= yield %>
    </main>
    
    <%# ドロワーメニューの利用宣言 %>
    <script>
      $(document).ready(function() {
      $('.drawer').drawer();
    });
    </script>

    <script>
      window.onload = function() {
        const change_color_cols = document.querySelectorAll('.change-color');
        const mypage_message = document.getElementById("flash-message").textContent;
        const regex = new RegExp('本日お菓子を食べたことを申告されました*');
        if (regex.test(mypage_message)){ 
          change_color_cols.forEach(function (change_color_col) {
          change_color_col.style.color = 'red';
          })
        }
      }
    </script>
  </body>
</html>

```

```ruby:app/views/users/show.html.erb
<div class="container-fluid">
  <div class="row justify-content-end">
    <div class="col-lg-4 mb-lg-5 mr-lg-5 mb-5">
      <% if current_user.id == @user.id %>
        <div class="text-right">
          <%=link_to("お菓子を食べた", update_eat_day_user_path(@user), method: :patch, class: "btn btn-primary mt-3")%>
        </div>
      <% end %>  
      <div class="user-image text-center mb-4">
        <%= display_user_icon(@user, "show-image", "rounded-circle") %>
      </div>
      <div class="text-center mb-5">
        <h4><strong><%= @user.name %></strong></h4>
      </div>
      <table class="table table-striped table-bordered">
        <% if current_user.id == @user.id %>
          <tr>
            <th class="h5"><strong>メールアドレス</strong></th>
          </tr>
          <tr>
            <th class="font-weight-normal"><%= @user.email %></th>
          </tr>
        <% end %>
        <tr>
          <th class="h5"><strong>サービス利用開始日</strong></th>
        </tr>
        <tr>
          <th class="font-weight-normal"><%= l @user.created_at %></th>
        </tr>
        <tr>
          <th class="h5"><strong>サービス利用開始からお菓子を止めた日数</strong></th>
        </tr>
        <tr>
          <th class="font-weight-normal change-color"><%= @user.calc_stop_day %>日</th>
        </tr>
        <tr>
          <th class="h5"><strong>サービス利用開始から節約した金額</strong></th>
        </tr>
        <tr>
          <th class="font-weight-normal change-color"><%= @user.save_money %>円</th>
        </tr>
        <tr>
          <th class="h5"><strong>今月のお菓子を止めた日数</strong></th>
        </tr>
        <tr>
          <th class="font-weight-normal change-color"><%= @user.calc_stop_day_month %>日</th>
        </tr>
        <tr>
          <th class="h5"><strong>今月の節約した金額</strong></th>
        </tr>
        <tr>
          <th class="font-weight-normal change-color"><%= @user.save_money_month %>円</th>
        </tr>
      </table>
      <% if current_user.id == @user.id %>
        <div class="mypage-button-info">
          <%=link_to("編集", edit_user_registration_path, class: "btn btn-info mypage-button")%>
          <%=link_to("削除", user_registration_path, method: :delete, class: "btn btn-info mypage-button", data: { confirm: '本当に削除しますか？' } )%>
        </div>
      <% end %>
    </div>
    <div class="col-lg-3 ml-lg-5 mt-5 mb-5">
      <% if @posts.present? %>
        <h5 class=" mt-lg-5"><strong>つぶやき一覧</strong></h5>
        <div class="mypage-content-scroll">
          <%= render partial: 'post', collection: @posts %>
        </div>
      <% end %>
    </div>
  </div>
</div>

```

ビューからJavascriptファイルを読み込むためには、上記の方法以外にも「app/javascript/packs/application.js」に「require("./show)"」を追加する方法もございますが、こちらはJavascriptファイルがページ全体に機能するようになります。今回は「show.html.erb」のみにJavascriptファイルを使用したいため、上記の方法を選びました。参考にした記事は以下です。
[Ruby on RailsにおけるJavascriptファイルの取り扱い(Rails6)](https://nobunobu1717.site/?p=1292)

実装してみた
--

「application.html.erb」からscriptタグに記載されているコードを抜き出して、以下のようにJavascriptファイルを作成いたしました。

```javascript:app/javascript/packs/show.js
window.onload = function() {
  const change_color_cols = document.querySelectorAll('.change-color');
  const mypage_message = document.getElementById("flash-message").textContent;
  const regex = new RegExp('本日お菓子を食べたことを申告されました*');
  if (regex.test(mypage_message)){ 
    change_color_cols.forEach(function (change_color_col) {
    change_color_col.style.color = 'red';
    })
  }
}
```

上記のJavascriptファイルを以下のビューファイルで呼び出すために、末尾に「javascript_pack_tag 'show'」を追加致しました。


```ruby:app/views/users/show.html.erb
<div class="container-fluid">
  <div class="row justify-content-end">
    <div class="col-lg-4 mb-lg-5 mr-lg-5 mb-5">
      <% if current_user.id == @user.id %>
        <div class="text-right">
          <%=link_to("お菓子を食べた", update_eat_day_user_path(@user), method: :patch, class: "btn btn-primary mt-3")%>
        </div>
      <% end %>  
      <div class="user-image text-center mb-4">
        <%= display_user_icon(@user, "show-image", "rounded-circle") %>
      </div>
      <div class="text-center mb-5">
        <h4><strong><%= @user.name %></strong></h4>
      </div>
      <table class="table table-striped table-bordered">
        <% if current_user.id == @user.id %>
          <tr>
            <th class="h5"><strong>メールアドレス</strong></th>
          </tr>
          <tr>
            <th class="font-weight-normal"><%= @user.email %></th>
          </tr>
        <% end %>
        <tr>
          <th class="h5"><strong>サービス利用開始日</strong></th>
        </tr>
        <tr>
          <th class="font-weight-normal"><%= l @user.created_at %></th>
        </tr>
        <tr>
          <th class="h5"><strong>サービス利用開始からお菓子を止めた日数</strong></th>
        </tr>
        <tr>
          <th class="font-weight-normal change-color"><%= @user.calc_stop_day %>日</th>
        </tr>
        <tr>
          <th class="h5"><strong>サービス利用開始から節約した金額</strong></th>
        </tr>
        <tr>
          <th class="font-weight-normal change-color"><%= @user.save_money %>円</th>
        </tr>
        <tr>
          <th class="h5"><strong>今月のお菓子を止めた日数</strong></th>
        </tr>
        <tr>
          <th class="font-weight-normal change-color"><%= @user.calc_stop_day_month %>日</th>
        </tr>
        <tr>
          <th class="h5"><strong>今月の節約した金額</strong></th>
        </tr>
        <tr>
          <th class="font-weight-normal change-color"><%= @user.save_money_month %>円</th>
        </tr>
      </table>
      <% if current_user.id == @user.id %>
        <div class="mypage-button-info">
          <%=link_to("編集", edit_user_registration_path, class: "btn btn-info mypage-button")%>
          <%=link_to("削除", user_registration_path, method: :delete, class: "btn btn-info mypage-button", data: { confirm: '本当に削除しますか？' } )%>
        </div>
      <% end %>
    </div>
    <div class="col-lg-3 ml-lg-5 mt-5 mb-5">
      <% if @posts.present? %>
        <h5 class=" mt-lg-5"><strong>つぶやき一覧</strong></h5>
        <div class="mypage-content-scroll">
          <%= render partial: 'post', collection: @posts %>
        </div>
      <% end %>
    </div>
  </div>
</div>
<%= javascript_pack_tag 'show' %>

```

そして、vscodeのターミナルで「rails s」コマンドでプログラムを起動させてみたところ、以下のログがターミナルから出力されました。

```
Rendering 500 with exception: Webpacker can't find show.js in /Users/XXXXXXX/Desktop/stop_sweets/public/packs/manifest.json. Possible causes:
1. You want to set webpacker.yml value of compile to true for your environment
   unless you are using the `webpack -w` or the webpack-dev-server.
2. webpack has not yet re-run to reflect updates.
3. You have misconfigured Webpacker's config/webpacker.yml file.
4. Your webpack configuration is not creating a manifest.
Your manifest contains:
{
  "application.js": "/packs/js/application-f14bbff28690fa69ca58.js",
  "application.js.map": "/packs/js/application-f14bbff28690fa69ca58.js.map",
  "entrypoints": {
    "application": {
      "js": [
        "/packs/js/application-f14bbff28690fa69ca58.js"
      ],
      "js.map": [
        "/packs/js/application-f14bbff28690fa69ca58.js.map"
      ]
    }
  }
}

  Rendering public/500.html
  Rendered public/500.html (Duration: 0.8ms | Allocations: 34)
Completed 500 Internal Server Error in 276ms (Views: 3.6ms | ActiveRecord: 24.0ms | Allocations: 62437)
```

上記のエラーに記載されている通り「public/packs/manifest.json」に今回追加した「show.js」が存在しませんでした。原因はwebpackerが必要なファイルをビルドできなかったと思われます。「manifest.json」はwebpackerがビルドした結果、生成されるファイルですので、そのファイルが想定通りに出力されていないということはwebpackerでのビルドが上手く動いていなかったと思います。以下の記事を参考にしました。

- [Webpacker の基本的な仕組み](https://numb86-tech.hatenablog.com/entry/2019/01/09/215714)



以下記事を参考にvscodeのターミナルでコマンド「rails webpacker:compile」を叩くことによって、webpackerがビルドしていただけるとのことですので、実施してみたところ…

- [Rails6】エラーの中身と対処法 Webpacker::Manifest::MissingEntryError in](https://qiita.com/shizen-shin/items/38fd774ffc87bc8838c9)

```:
rails webpacker:compile
dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.68.dylib
  Referenced from: /usr/local/bin/node
  Reason: image not found
sh: line 1:  2905 Abort trap: 6           node -v
sh: nodejs: command not found
Node.js not installed. Please download and install Node.js https://nodejs.org/en/download/
Exiting!
```

「Node.js」がインストールされていないと出ました。そもそも「Node.js」が何かを調べてみましたところ、以下の記事にJavascriptをサーバサイドでも実行できるようにプラットフォームを提供してくれるものと理解しました。
[初心者向け！3分で理解するNode.jsとは何か?](https://eng-entrance.com/what-is-nodejs)

Node.jsのインストールは以下記事がわかりやすかったため、以下の通り実施しました。

[MacにNode.jsをインストール](https://qiita.com/kyosuke5_20/items/c5f68fc9d89b84c0df09)

もう一度コマンド「rails webpacker:compile」を試してみたところ…

```:
rails webpacker:compile
Compiling...
Compiled all packs in /Users/XXXXXX/Desktop/rails/crud_sample/public/packs
Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

Why you should do it regularly:
https://github.com/browserslist/browserslist#browsers-data-updating

Hash: df0d9ef9e3c08806765e
Version: webpack 4.46.0
Time: 3233ms
Built at: 2021/12/08 22:37:45
                                        Asset       Size  Chunks                         Chunk Names
       js/application-fca18d45955b29c6f4a6.js   31.6 KiB       0  [emitted] [immutable]  application
    js/application-fca18d45955b29c6f4a6.js.br   8.12 KiB          [emitted]              
    js/application-fca18d45955b29c6f4a6.js.gz    9.3 KiB          [emitted]              
   js/application-fca18d45955b29c6f4a6.js.map    111 KiB       0  [emitted] [dev]        application
js/application-fca18d45955b29c6f4a6.js.map.br   24.3 KiB          [emitted]              
js/application-fca18d45955b29c6f4a6.js.map.gz   28.5 KiB          [emitted]              
              js/test-bf67694f049ab9aacf93.js   1.02 KiB       1  [emitted] [immutable]  test
           js/test-bf67694f049ab9aacf93.js.br  464 bytes          [emitted]              
           js/test-bf67694f049ab9aacf93.js.gz  536 bytes          [emitted]              
          js/test-bf67694f049ab9aacf93.js.map   4.77 KiB       1  [emitted] [dev]        test
       js/test-bf67694f049ab9aacf93.js.map.br   1.62 KiB          [emitted]              
       js/test-bf67694f049ab9aacf93.js.map.gz   1.81 KiB          [emitted]              
                                manifest.json  654 bytes          [emitted]              
                             manifest.json.br  162 bytes          [emitted]              
                             manifest.json.gz  194 bytes          [emitted]              
Entrypoint application = js/application-fca18d45955b29c6f4a6.js js/application-fca18d45955b29c6f4a6.js.map
Entrypoint test = js/test-bf67694f049ab9aacf93.js js/test-bf67694f049ab9aacf93.js.map
[2] ./app/javascript/packs/application.js 504 bytes {0} [built]
[3] (webpack)/buildin/module.js 552 bytes {0} [built]
[4] ./app/javascript/channels/index.js 205 bytes {0} [built]
[5] ./app/javascript/channels sync _channel\.js$ 160 bytes {0} [built]
[6] ./app/javascript/packs/test.js 106 bytes {1} [built]
    + 2 hidden modules
```

上手く動きました。その後、「rails s」でアプリを起動して、ブラウザでもJavascriptが想定通りに機能していることを確認できました。


今回学んだこと
--
- 言語のバージョンおよび仕組みを意識することの大切さを学びました。今回の実装を通して、Rails5とRails6でJavascriptファイルを格納するディレクトリーも変わっていることを知りました。また、Railsで自分が使用しているのはWebpackerなのか、Sprocketsなのかを抑えることが大切だと思いました。自分が使用するバージョンや仕組みを抑えていない状態でQiitaのようなブログ記事を読んでしまうと必要な情報を上手く取捨選択ができないと思いました。
- エラーメッセージをよく読むことが改めて大切だと思いました。エラーメッセージにエラーを解決するためのヒントが入っているものの、焦ってちゃんと読み込まずに解決しようとしてしまい、時間がかかってしまうことを今回の経験を通じて学びました。

参考資料
--
- [Webpacker の概要](https://railsguides.jp/webpacker.html)
- [Rails6】エラーの中身と対処法 Webpacker::Manifest::MissingEntryError in](https://qiita.com/shizen-shin/items/38fd774ffc87bc8838c9)
- [Webpacker の基本的な仕組み](https://numb86-tech.hatenablog.com/entry/2019/01/09/215714)
- [初心者向け！3分で理解するNode.jsとは何か?](https://eng-entrance.com/what-is-nodejs)
- [Ruby on RailsにおけるJavascriptファイルの取り扱い(Rails6)](https://nobunobu1717.site/?p=1292)