1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

railsお気に入り機能 一例

Last updated at Posted at 2018-12-22

お気に入り機能の実現

・ハートアイコンとそれに付属する数字の表示
・ハートアイコンを押す前に自分が押していなかったら数字を+1して、押していたのなら-1をする

HTML

show.html.rb
<div class="card-show" id="js-card-show" data-user-id="<%= @current_user.id %>" data-book-id="<%= @book.id %>">
      # この画面に来た時にユーザーが既にお気に入りしているのか否かで条件分岐
      <% if Like.find_by(impression_id: impressions.id, user_id: @current_user.id)%>
          
          <span data-liked="true" data-impression-id="<%= impressions.id %>" class="material-icons hart" >favorite</span>
          <span><%= impressions.likes.count %></span>
      <% else %>
          <span data-liked="false" data-impression-id="<%= impressions.id %>" class="material-icons">favorite</span>
          <span><%= impressions.likes.count %></span>
      <% end %>
</div>
data-user-id="<%= @current_user.id %>" data-book-id="<%= @book.id %>"

data-で始まる属性は、カスタムデータ属性といって、jquery,JavaScript内で値を取得できる形にしている
data-で始まる属性は、htmlとしての意味はないがJavaScriptやCSSで使うことができる

favoriteアイコンは[こちら]
(https://qiita.com/takish/items/9b65f1dcf36cc242357f)を使用しました

contrller

books.contrller.rb
    def show
      @book = Book.find(params[:book_id])
      @user = User.find_by(id: @book.user_id)
      @impressions = @book.impressions
    end

model

like.rb
    belongs_to :user
    belongs_to :impression
impression.rb
    has_many :likes
user.rb
    has_many :likes

user.rblike.rbが結びついているのはユーザー画面でお気に入りした投稿を観れるようにするため

Jquery

application.html.erbの方にjqueryの読み込みを書いていない場合

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>

[【2018年版】jQuery最新の読み込みスクリプト「コピペOK+裏技」]
(https://macoblog.com/jquery-saishin/)

show.html.rb
#  jquery が使えるように読み込む
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script>
  $(function () {
    var userId = $("#js-card-show").data("user-id");
    var bookId = $("#js-card-show").data("book-id");

    $('.material-icons').on('click', function () {
      var self = $(this);      // クリックされた要素を代入 
      var impressionId = self.data("impression-id");
      var hasMyLike = self[0].dataset.liked;
      if (hasMyLike === "true") { 
        $.ajax({                 // イフ文がtrueの場合
            url: '/likes/' + impressionId + '/destroy', // 正しい書き方ではない
            type: 'POST',         // 送信
            dataType: "json", // これ書かなかったら自動推察この場合はなくても動いたです
            timespan: 1000, // 通信のタイムアウトの設定(ミリ秒)
            data: {                    //送るデータのカラムと中身                         
              '_method': 'delete',
              'impression_id': impressionId,
              'user_id' : userId,
              'book_id' : bookId,
            }
          }).done((data) => {                   // 成功したら
            $(this).removeClass('hart');          // ".hart"をクリックした要素から外す
            self.next()[0].innerText = (data.count).toString();
            self[0].dataset.liked = false;       // html上の`data-liked`をtrueからfalseへ
            console.log("success");   // 検証のconsoleで見れる
          })
          .fail((data) => {          // 失敗した時の処理
            console.log("fail");
          })
          .always((data) => {      //常に行われる処理
            console.log("always");
          });
      } else {                      // イフ文がfalseの場合
        $.ajax({
            url: '/likes/' + impressionId + '/create',
            type: 'POST',           // POSTが送信GETが受信
            dataType: "json",                  
            timespan: 1000,                     
            data: {
              user_id : userId,
              impression_id : impressionId,
              book_id : bookId,
            }
          }).done((data) => {
            $(this).addClass('hart');        // class="hart"をクリックした要素に加える(.#はいらない)
            self.next()[0].innerText = (data.count).toString();
            self[0].dataset.liked = true;
            console.log("success");
          })
          .fail((data) => {
            console.log(data);
            console.log("fail");
          })
          .always((data) => {
            console.log("always");
          });
      }

    });
  });
</script>

var userId = $("#js-card-show").data("user-id");
変数宣言してから代入。data属性を使ってrailsのデータをとってきてそれをjquery内で使える変数に代入
bookIdも同じ

$('.material-icons').click(function () {
ブラウザ上でclass="material-icons"をクリックした時イベントを起こすための.clickイベント
idの場合は.(ドット)ではなく#を使う

(hasMyLike === "true") hasMyLikeという変数の中身にtrueという文字列を入れているので判定するためのtrueも文字列として確実に認識させるために"で囲んでいる。

ajaxの条件分岐で参考にしました。ずっとfalseにデータが行き続けるのはこれで解決
JavaScript 忘れがちな === と == の違い
https://qiita.com/PianoScoreJP/items/e43d70ec188c6fed73ed

こちらは調べても何もわからなかった時に何か一つでもと思って実装しました☟
非同期通信(Ajax)をするときはタイムアウト処理を必ず入れてほしい(切実)
https://qiita.com/tonkotsuboy_com/items/d1b3cf45ae5135441f9b

likesController.rb
    def create
      user_id = params[:user_id].to_i
      book_id = params[:book_id].to_i
      impression_id = params[:impression_id].to_i
      like = Like.new
      like.user_id = user_id
      like.book_id = book_id
      like.impression_id = impression_id
      if like.save
        current_like_count = Like.where(book_id: book_id, impression_id: impression_id).size
        logger.debug(current_like_count)
        success_json_object = {
          'count' => current_like_count,
        }
        render :status => :ok, :json => success_json_object
      else
        failer_json_object = {'status' => 'failer'}    
        render :status => :internal_server_error, :json => failer_json_object
      end
   end
   
    # ライク削除処理
    def destroy
      impression_id = params[:impression_id].to_i //paramsは文字列なので別の場所で数値化
      user_id = params[:user_id].to_i             //メソッドの中で数値化できなかった
      book_id = params[:book_id].to_i
      like = Like.find_by(                        
        impression_id: impression_id,
        user_id: user_id,
        book_id: book_id
      )
      logger.debug "#{like.inspect}"          // ajaxがログにも影響しているのを確認した名残り
      logger.debug "-----------------------------------"
      if like
        if like.destroy
          current_like_count = Like.where(book_id: book_id, impression_id: impression_id).size
          success_json_object = {
            'count' => current_like_count,
          }
          render :status => :ok, :json => success_json_object  //データがtrueの時jsonとして発動
        end
      end
    end
end

よんでくれてありがとう

1
2
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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?