4
0

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-08-15

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

エラー解決のタイミング

  1. RailsにjQeuryを導入する時
  2. 部分テンプレート作成時

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

1. RailsにjQeuryを導入する時

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

導入の手順

  1. Gemfileにjquery-railsを追加する
Gemfile
# 最終行に追加しましょう!
gem 'jquery-rails'

2.bundle install
3.javascript/packs/application.jsimport "jquery"を追記

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に出力されていることがあります。
そんな時は以下のコマンドを実行しましょう!

ターミナル
yarn add jquery

(railsがbundle installでnode_modulesを取ってくるのですが
なぜか取得できていない時に起こるエラーです)

2. 部分テンプレート作成時

よくある非同期いいねでは、以下のような構成です。

  1. likes/
    1. _like.html.erb
    2. create.js.erb
    3. destroy.js.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 %>
_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 %>
create.js.erb
$('#likes_buttons_<%= post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: post}) %>");
destroy.js.erb
$('#likes_buttons_<%= post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: post}) %>");
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 %>'のところで@がない
  2. redirect_backをコメントアウト(削除)していない
  3. likes_controller@postを取得していない
    上記のトラブルポイントについて、実際のエラーとともに見ていきましょう!

1. ~.js.erbのlocals'#likes_buttons_<%= post.id %>'のところで@がない

↓↓↓このようなエラーが出てきます↓↓↓
image.png

ターミナルのログ
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で使ってるからいいんじゃないの?」と思ったかもしれませんが、ダメです😇
ちゃんと@をつけましょうね!
正しいファイルたち↓

create.js.erb
$('#likes_buttons_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");
destroy.js.erb
$('#likes_buttons_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");

2. redirect_backをコメントアウト(削除)していない

これは単純でエラーは出ませんが、likes_controllerでリダイレクトを削除していないと、ページ遷移しちゃうよって話です。せっかく非同期(ページ遷移なしでいいね)するので、ちゃんと消しましょう!

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
こんなエラーが出ます。そして、こいつのタチが悪いところは、現在表示しているブラウザ備え付きの検証ツールにあるConsoleを見ただけでは、解決策が見えてこない点です。
なぜかという理由の前に、まずは以下のRailsくんのログをご覧ください。

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を取得するだけで終わります。

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を使っていますが、参考になると思います!

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0