Rails 非同期いいね　エラー解決集

以前、非同期いいねで大量に困った経験があるので、Railsの非同期いいね（Railsだったら他の非同期にも使えるかも？）なエラー解決集をメモ代わりに作っておきます！

# エラー解決のタイミング
1. [RailsにjQeuryを導入する時](#1-railsにjqeuryを導入する時)
1. [部分テンプレート作成時](#2-部分テンプレート作成時)

（若干エラー吐かない内容もありますが、非同期のアクシデント解決の関係で入れてます）

## 1. RailsにjQeuryを導入する時
Qiitaでよく調べている方はご存知かもしれませんが、Railsの記事はRails6以前のものが多く、役に立たないことがあります。そこで、Rails6でのjQuery導入方法をお伝えします！

### 導入の手順

1. Gemfileに**jquery-rails**を追加する
```ruby:Gemfile
# 最終行に追加しましょう！
gem 'jquery-rails'
```
2.bundle install
3.`javascript/packs/application.js`に`import "jquery"`を追記
```js:application.js
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

import "jquery" // ←ここに挿入です！
import Rails from "@rails/ujs"
import Turbolinks from "turbolinks"
import * as ActiveStorage from "@rails/activestorage"
import "channels"

Rails.start()
Turbolinks.start()
ActiveStorage.start()
```
これでRails6でjQueryを使えるようになりました🎉
（＊CDNで代用することもできます）

### たまに出てくるエラー：webpackMissingModule
`webpackMissingModule`の内容が入ったエラーが
検証ツールのconsoleに出力されていることがあります。
そんな時は以下のコマンドを実行しましょう！
```zsh:ターミナル
yarn add jquery
```
（railsがbundle installでnode_modulesを取ってくるのですが
なぜか取得できていない時に起こるエラーです）

## 2. 部分テンプレート作成時
よくある非同期いいねでは、以下のような構成です。
1. likes/
    1. _like.html.erb
    1. create.js.erb
    1. destroy.js.erb

また、ソースコードは以下のように仮定します。
```erb:views/posts/index.html.erb
<%# 省略 %>
<% @posts.each do |post| %>
        <div class="card col-md-4" style="width: 18rem;">
            <%= image_tag post.image_url, class: "card-img-top" if post.image? %>
            <div class="card-body">
                <div class="card-text">
                    <%= post.body %>
                </div>
                <div id="likes_buttons_<%= post.id %>">
                    <%= render partial: 'likes/like', locals: {post: post} %>
                </div>
                <%= link_to '投稿詳細', post_path(post.id), class: "card-li" %>
                <% if current_user.id == post.user_id %>
                    <%= link_to '編集', edit_post_path(post.id), class: "card-li" %>
                    <%= link_to '削除', post_path(post.id), method: :delete, class: "card-li" %>
                <% end %><br />
                <%= post.created_at.to_s(:datetime_jp) %>
            </div>
        </div>
    <% end %>
```
```erb:_likes.html.erb
<% if user_signed_in? %>
    <% if current_user.already_liked?(post) %>
        <%= link_to post_like_path(id: post.id, post_id: post.id), method: :delete, remote: true do %>
            <i class="fas fa-heart"></i><%= post.likes.count %>
        <% end %>
    <% else %>
        <%= link_to post_likes_path(id: post.id, post_id: post.id), method: :post, remote: true do %>
            <i class="fas fa-heart"></i><%= post.likes.count %>
        <% end %>
    <% end %>
<% else %>
    <i class="fas fa-heart"></i><%= post.likes.count %>
<% end %>
```
```erb:create.js.erb
$('#likes_buttons_<%= post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: post}) %>");
```
```erb:destroy.js.erb
$('#likes_buttons_<%= post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: post}) %>");
```
```rb:likes_controller
class LikesController < ApplicationController
    def create
        like = current_user.likes.create(post_id: params[:post_id]) #user_idとpost_idの二つを代入
        redirect_back(fallback_location: root_path)
    end
    
    def destroy
        like = Like.find_by(post_id: params[:post_id], user_id: current_user.id)
        like.destroy
        redirect_back(fallback_location: root_path)
    end
end
```
### エラー解決集
1. [~.js.erbの`locals`と`'#likes_buttons_<%= post.id %>'`のところで`@`がない](#1-jserbのlocalsとlikes_buttons_-postid-のところでがない)
1. [`redirect_back`をコメントアウト（削除）していない](#2-redirect_backをコメントアウト削除していない)
1. [`likes_controller`で`@post`を取得していない](#3-likes_controllerでpostを取得していない)
上記のトラブルポイントについて、実際のエラーとともに見ていきましょう！

### 1. ~.js.erbの`locals`と`'#likes_buttons_<%= post.id %>'`のところで`@`がない
↓↓↓このようなエラーが出てきます↓↓↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2017749/bff63dac-bc95-7ec2-8c30-0104b92299b3.png)
```zsh:ターミナルのログ
Started DELETE "/posts/3/likes/3" for 127.0.0.1 at 2022-08-16 00:32:28 +0900
Processing by LikesController#destroy as JS
  Parameters: {"post_id"=>"3", "id"=>"3"}
  Post Load (0.4ms)  SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
  ↳ app/controllers/likes_controller.rb:9:in `destroy'
  User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ↳ app/controllers/likes_controller.rb:10:in `destroy'
  Like Load (0.1ms)  SELECT "likes".* FROM "likes" WHERE "likes"."post_id" = ? AND "likes"."user_id" = ? LIMIT ?  [["post_id", 3], ["user_id", 1], ["LIMIT", 1]]
  ↳ app/controllers/likes_controller.rb:10:in `destroy'
  TRANSACTION (0.0ms)  begin transaction
  ↳ app/controllers/likes_controller.rb:11:in `destroy'
  Like Destroy (0.7ms)  DELETE FROM "likes" WHERE "likes"."id" = ?  [["id", 17]]
  ↳ app/controllers/likes_controller.rb:11:in `destroy'
  TRANSACTION (0.9ms)  commit transaction
  ↳ app/controllers/likes_controller.rb:11:in `destroy'
  Rendering likes/destroy.js.erb
  Rendered likes/destroy.js.erb (Duration: 6.2ms | Allocations: 5010)
Completed 500 Internal Server Error in 22ms (ActiveRecord: 2.2ms | Allocations: 13515)


  
ActionView::Template::Error (undefined local variable or method `post' for #<ActionView::Base:0x00000000039008>
Did you mean?  @post):
    1: $('#likes_buttons_<%= post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: post}) %>");
  
app/views/likes/destroy.js.erb:1
```
「**`_like.html.erb`では`post`で使ってるからいいんじゃないの？**」と思ったかもしれませんが、ダメです😇
ちゃんと`@`をつけましょうね！
正しいファイルたち↓
```erb:create.js.erb
$('#likes_buttons_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");
```
```erb:destroy.js.erb
$('#likes_buttons_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");
```

### 2. `redirect_back`をコメントアウト（削除）していない
これは単純でエラーは出ませんが、`likes_controller`でリダイレクトを削除していないと、ページ遷移しちゃうよって話です。せっかく非同期（ページ遷移なしでいいね）するので、ちゃんと消しましょう！
```rb:likes_controller.rb
class LikesController < ApplicationController
    def create
        like = current_user.likes.create(post_id: params[:post_id]) #user_idとpost_idの二つを代入
        # redirect_back(fallback_location: root_path)
    end
    
    def destroy
        like = Like.find_by(post_id: params[:post_id], user_id: current_user.id)
        like.destroy
        # redirect_back(fallback_location: root_path)
    end
end
```

### 3. `likes_controller`で`@post`を取得していない
コイツァ事案です。さっきと違ってちゃんとエラー吐きます😱
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2017749/33006d7d-e5f5-2aea-efdc-47626295a6f4.png)
こんなエラーが出ます。そして、こいつのタチが悪いところは、現在表示しているブラウザ備え付きの検証ツールにあるConsoleを見ただけでは、解決策が見えてこない点です。
なぜかという理由の前に、まずは以下のRailsくんのログをご覧ください。

```zsh:Railsくんの非同期いいねログ
Started POST "/posts/3/likes?id=3" for 127.0.0.1 at 2022-08-16 00:21:26 +0900
Processing by LikesController#create as JS
  Parameters: {"id"=>"3", "post_id"=>"3"}
   (0.0ms)  SELECT sqlite_version(*)
  ↳ app/controllers/likes_controller.rb:4:in `create'
  User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ↳ app/controllers/likes_controller.rb:4:in `create' 
  TRANSACTION (0.0ms)  begin transaction
  ↳ app/controllers/likes_controller.rb:4:in `create'
  Post Load (0.0ms)  SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
  ↳ app/controllers/likes_controller.rb:4:in `create'
  Like Exists? (0.1ms)  SELECT 1 AS one FROM "likes" WHERE "likes"."post_id" = ? AND "likes"."user_id" = ? LIMIT ?  [["post_id", 3], ["user_id", 1], ["LIMIT", 1]]
  ↳ app/controllers/likes_controller.rb:4:in `create'
  Like Create (0.7ms)  INSERT INTO "likes" ("post_id", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["post_id", 3], ["user_id", 1], ["created_at", "2022-08-15 15:21:26.609127"], ["updated_at", "2022-08-15 15:21:26.609127"]]
  ↳ app/controllers/likes_controller.rb:4:in `create'
  TRANSACTION (1.0ms)  commit transaction 
  ↳ app/controllers/likes_controller.rb:4:in `create'
  Rendering likes/create.js.erb
  Rendered likes/create.js.erb (Duration: 1.1ms | Allocations: 1531)
Completed 500 Internal Server Error in 36ms (ActiveRecord: 2.8ms | Allocations: 27304)


  
ActionView::Template::Error (undefined method `id' for nil:NilClass):
    1: $('#likes_buttons_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");
  
app/views/likes/create.js.erb:1
```
よくよく見ると、`commit transaction`（DB上での処理は完了の意味）とあり、いいねを作るDBくんはしっかりと仕事をしてくれたようです。しかし、``undefined method `id' for nil:NilClass``とあり、idをうまく処理できず、Viewに反映できていないようです😇
こんなダルそうなエラーですが、実はControllerにとある記述を行い、`@post`を取得するだけで終わります。
```rb:likes_controller
class LikesController < ApplicationController
    def create
        @post = Post.find(params[:post_id]) # ←ここで@postをしっかり取得
        like = current_user.likes.create(post_id: params[:post_id]) #user_idとpost_idの二つを代入
        # redirect_back(fallback_location: root_path)
    end
    
    def destroy
        @post = Post.find(params[:post_id]) # 削除時も同様に
        like = Like.find_by(post_id: params[:post_id], user_id: current_user.id)
        like.destroy
        # redirect_back(fallback_location: root_path)
    end
end
```

---
今回は以上になります！
Railsの非同期通信関係でエラー出たり、エラー吐かないけど明らかにうまくいっていない時はこの記事を見返してくださいね⭐️
それでは〜！

# 非同期いいねで使える記事↓
こちらはCDNでjQueryを使っていますが、参考になると思います！

https://qiita.com/Fu990628/items/a6fffea6e5fedfffb4e3