【Ajax】いいね機能を非同期通信で実装したいよ


# はじめに
ある投稿に対していいね機能を実装する際にAjaxを使って一部だけ情報更新を行いたい。
その際、とても苦労した部分について下記の記事にまとめたので、参照していただきたい。

https://qiita.com/vaza__ta/items/ef0cc701eabe863dfe91

下記テーブルは作成済み。
userテーブル
postテーブル
| No |    項目     |   内容    |
|---:|-------------|------------|
| 1  |      OS       |   Mac     |
| 2  |       Ruby      |   2.6.3   |
| 3  |       rails      |      6.0.4 |


# いいね機能にAjaxを実装
早速実装を開始する。
**完成図**
![完成図.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2684787/06042a76-75c1-eb67-bd45-f409906ffbf3.gif)

まず、routesを設定する。
```routes.rb
  resources :posts do
    resource :favorites, only: [:create, :destroy]
  end
```
postモデルにfavorite_usersメソッドを追加する。
```post.rb
has_many :favorites, dependent: :destroy
# 下記を追加
has_many :favorite_users, through: :favorites, source: :user
```
いいねした人を表示するために、`@favorite_users`を追加する。
```posts_controller.rb
def show
    @post = Post.find(params[:id])
#　下記を追加
    @favorite_users = @post.favorite_users
```
postモデルのshowページからいいねをする機能をつける。
```html:app/views/posts/show.html.erb
<%# いいね機能 %>
<span id="favorite_icon_<%= @post.id %>">
  <% unless current_user?(@user) %>
    <% if @post.favorited?(current_user) %>
      <%= render 'favorites/favorited_state', post: @post %>
    <% else %>
      <%= render 'favorites/unfavorited_state', post: @post %>
    <% end %>
  <% end %>
</span>
  <span id="favorite_count" class="middle-size"><%= @post.favorites.count %></span>
<p class="margin-top">いいねしたユーザー</p>
<ol id="favorite_user">
  <%= render partial: 'favorites/favorite_user', favorite_users: @favorite_users %>
</ol>
```
いいねアイコン(favorite_icon)を押されると、favoriteコントローラーのcreateアクションまたはdestroyアクションが反応する。
```favorites_controller.rb
class FavoritesController < ApplicationController
  def create
    @post = Post.find(params[:post_id])
    favorite = current_user.favorites.create(post_id: @post.id)
    favorite.save
    # redirect_to post_path(params[:post_id])  <=Ajaxを使用するためにここを削除
    @favorite_users = @post.favorite_users
    respond_to do |format|
      format.html { redirect_to @post }
      format.js # XHLリクエストが来たら、特定のjsを反応させる
      # => default: app/views/favorites/create.js.erb
    end
  end 

  def destroy
    @post = Post.find(params[:post_id])
    favorite = current_user.favorites.find_by(post_id: @post.id)
    favorite.destroy
    # redirect_to post_path(params[:post_id]) <=Ajaxを使用するためにここを削除
    @favorite_users = @post.favorite_users
    respond_to do |format|
      format.html { redirect_to @post }
      format.js 
      # => default: app/views/favorites/destroy.js.erb
    end
  end
end

```
アクションの実行内容を部分的に更新するために、create.jsとdestroy.jsファイルを作成する。
それは、いいねボタン（favorite_icon）と、いいね数(favorite_count)と、いいねした人(favorite_user)を更新する。

```js:app/views/favorites/create.js.erb
$("#favorite_icon_<%= @post.id %>").html("<%= escape_javascript(render('favorites/favorited_state', post: @post)) %>");
$("#favorite_count").html('<%= @post.favorites.count %>');
$("#favorite_user").html('<%= escape_javascript(render('favorites/favorite_user', favorite_user: @favorite_users)) %>');
```
```js:app/views/favorites/destroy.js.erb
$("#favorite_icon_<%= @post.id %>").html("<%= escape_javascript(render('favorites/unfavorited_state', post: @post)) %>");
$("#favorite_count").html('<%= @post.favorites.count %>');
$("#favorite_user").html('<%= escape_javascript(render('favorites/favorite_user', favorite_users: @favorite_users)) %>');
```
# パーシャルを作成する
先ほど、showページとjsファイルでrenderするパーシャルを作成していく。
・favorite_iconを表示する。
**いいねをした後の状態(createアクション後)**
```html:favorites/_favorited_state.html.erb
<%= link_to post_favorites_path(post.id), class: "btn", method: :delete, remote: true do %>
  <i class="fa-solid fa-thumbs-up fa-3x"></i>
<% end %>
```
**いいねをする前の状態(destroyアクション後)**
```html:favorites/_unfavorited_state.html.erb
<%= link_to post_favorites_path(post), class: "btn", method: :post, remote: true do %>
  <i class="fa-regular fa-thumbs-up fa-3x"></i>  
<% end %>
```
・いいねした人(favorite_user)を表示するために、パーシャルを作る。
```favorites/_favorite_user.html.erb
<% @favorite_users.each do |favorite_user| %>
  <div><%= favorite_user.name %></div>
<% end %>
```
# おわりに
Ajaxでいいね機能を実装することができた。