【Rails】【非同期通信】create.js.hamlがうまく機能しない=>create.js.erbにすることで解決(したように見えた初歩的ミス)


実装したかった機能

いいね!機能を実装しようと思い、非同期通信でいいね!を送信して結果を部分テンプレートで更新させようとしました。
具体的な流れとしては、部分テンプレートで作成したいいねボタンをクリックすることで非同期通信によりいいねの作成・削除を行い、結果に基づきいいねボタン(部分テンプレート)を更新させるという手順を想定しました。

開発環境

Ruby: 2.5.1
Rails: 5.2.3

うまくいかなかったコード

モデル

app/models/user.rb
class User < ApplicationRecord
# (中略)
  has_many :likes
  has_many :items
end
app/models/item.rb
class Item < ApplicationRecord
# (中略)
  has_many :likes

  def liked?(user)
    likes.where(user_id: user.id).exists?
  end
end
app/models/like.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :item
  validates :user_id, presence: true
  validates :item_id, presence: true
  validates_uniqueness_of :item_id, scope: :user_id
end

コントローラー

app/controllers/items_controller.rb
class ItemsController < ApplicationController
# (中略)
  def show
    @item = Item.find(params[:id])
    @likes_count = @item.likes.length
  end
end
app/controllers/likes_controller.rb
class LikesController < ApplicationController
  before_action :item_setting

  def create
    @like = current_user.likes.new(item_id: params[:item_id])
    if @like.save
      return_likes_count
    else
      return_error
    end
  end

  def destroy
    if @like = current_user.likes.find_by(item_id: params[:item_id])
      if @like.destroy
        return_likes_count
      else
        return_error
      end
    else
      return_error
    end
  end

  private

  def item_setting
    @item = Item.find(params[:item_id])
  end

  def return_likes_count
    @likes_count = @item.likes.length
  end

  def return_error
    @likes_count = "err"
  end
end

ビュー

app/views/item/show.html.haml
// (前略)
#likes
  = render partial: "likes", locals: {item: @item}
// (後略)
app/views/item/_likes.html.haml
// このファイル内の「@item」は「item」とした方が正確な書き方かもしれません
- if user_signed_in? && @item.liked?(current_user)
  = link_to item_likes_path(@item.id), method: :delete, remote: true do
    %li.items-tag__left--good{id: "good_btn_liked"}
      = icon('far', 'heart')
      %span いいね!
      %span#likes_count
        = @likes_count
- elsif user_signed_in?
  = link_to item_likes_path(@item.id), method: :post, remote: true do
    %li.items-tag__left--good{id: "good_btn"}
      = icon('far', 'heart')
      %span いいね!
      %span#likes_count
        = @likes_count
- else
  %li.items-tag__left--good{id: "#{'good_btn' if user_signed_in?}"+"#{'_liked' if user_signed_in? && current_user.likes.exists?(item_id: @item.id)}"}
    = icon('far', 'heart')
    %span いいね!
    %span#likes_count
      = @likes_count
app/views/likes/create.js.haml、destroy.js.haml
// これらのファイルがエラーの原因でした
$('#likes').html(“#{j(render partial: "items/likes", locals: { item: @item })}");

発生したエラー

いいねボタンをクリックすると、コンソールで以下のエラーが確認できました。

Uncaught SyntaxError: Invalid or unexpected token                  VM3330:1 
    at processResponse (rails-ujs.self-43e81c501e7e36871a34b4b950451cc7cb047af4e846ec742539e64724582452.js?body=1:273)
    at rails-ujs.self-43e81c501e7e36871a34b4b950451cc7cb047af4e846ec742539e64724582452.js?body=1:201
    at XMLHttpRequest.xhr.onreadystatechange (rails-ujs.self-43e81c501e7e36871a34b4b950451cc7cb047af4e846ec742539e64724582452.js?body=1:256)

ここでレスポンスの内容を確認すると、部分テンプレートの更新部分は以下のようになっていたようです。

$('#likes').html(“<a data-remote=\"true\" rel=\"nofollow\" data-method=\"delete\" href=\"/items/5/likes\"><li class=\'items-tag__left--good\' id=\'good_btn_liked\'>\n<i class=\"far fa-heart\"><\/i>\n<span>いいね!<\/span>\n<span id=\'likes_count\'>\n3\n<\/span>\n<\/li>\n<\/a>");
                 ^ よく見るとここの記号がダブルクォート(")じゃない。なにこれ?

エラーが出ている時には気付きませんでしたが、よく見ると.html()内最初のダブルクォートが違う記号になっており、これがエラーの原因となっていました。何故?

修正したコード

app/views/likes/create.js.erb、destroy.js.erb
<%# ファイル形式をhamlからerbに変更しました %>
$('#likes').html("<%= j(render partial: 'items/likes', locals: {item: @item}) %>");

結果

無事にエラーが解消されました。以下はcreate.js.erbから返ってきたレスポンスの内容です。

$('#likes').html("<a data-remote=\"true\" rel=\"nofollow\" data-method=\"delete\" href=\"/items/5/likes\"><li class=\'items-tag__left--good\' id=\'good_btn_liked\'>\n<i class=\"far fa-heart\"><\/i>\n<span>いいね!<\/span>\n<span id=\'likes_count\'>\n3\n<\/span>\n<\/li>\n<\/a>");

ファイル形式をhamlからerbに変更することで、返ってくるコードのダブルクォートが正しいものになり、エラーなく動いてくれる様になったようです。
".js.haml"でググってみたところいくつか記事はヒットしたのですが、参考にしてもどうも上手くいきませんでした。

今までのビューを全てhamlで書いていたため、create.js、destroy.jsもhamlで書こうとしていましたが、どうもこれが良くなかったみたいです。
同じようなコードに思えるのですが、まだまだ分からないことだらけですね。

追記

記事の投稿後に改めて確認すると、元々create.js.hamlで書いていたコードですでにダブルクォートじゃなくなってました。
他のアプリを作成中にhamlで同じことを試したら問題なく動作しました。
他のサイトを見ながらコピペしていたのか、何にせよhamlのせいではないようです。
他人のコードのコピペはいろんな意味でよくないですね。

参考