1日1%成長するブログ

毎日成長するために仕事/プライベートで得た学びをアウトプットするブログです

Rails5でajaxを使っていいね機能を実装する

ルーティングを用意する

resources :diaries do
  resources :diary_comments, only: [:create]
  resources :diary_likes, only: [:create, :destroy]
end
  • diaryが重複するので後でスッキリさせる

モデルを用意する

class Diary < ApplicationRecord
  belongs_to :user
  has_many :diary_likes, dependent: :destroy

  # ユーザーのいいねを取得
  def like(user_id)
    diary_likes.find_by(user_id: user_id)
  end
end

部分テンプレートを用意する

<% if diary.like(user.id) %>
  <button class="btn btn-default btn-xs disabled" type="button">
    <i class="fa fa-thumbs-o-up">いいね済み</i>
  </button>
<% else %>
  <button class="btn btn-default btn-xs" type="button">
    <i class="fa fa-thumbs-o-up">いいね</i>
  </button>
<% end %>
<% else %>

button_toに書き換える

 <%= button_to diary_likes_path(diary), remote: true, class: "btn btn-default btn-xs" do %>
   <i class="fa fa-thumbs-o-up">いいね</i>
 <% end %>
  • button_toは実際にはformとbuttonタグとして出力される。こんな感じ
<form class="button_to" method="post" action="/diaries/1/likes" data-remote="true"
    <button class="btn btn-default btn-xs" type="submit">
      <i class="fa fa-thumbs-o-up">いいね</i>
    </button>
    <input type="hidden" name="authenticity_token" value="xx">
</form>
<% if diary.like(user_id) %>
  <%= button_to diary_diary_like_path(diary, diary.like(user_id)), remote: true, method: :delete, class: "btn btn-default btn-xs" do %>
    <i class="fa fa-thumbs-o-up">いいね済み</i>
  <% end %>
<% else %>
  <%= button_to diary_diary_likes_path(diary), remote: true, class: "btn btn-default btn-xs" do %>
    <i class="fa fa-thumbs-o-up">いいね</i>
  <% end %>
<% end %>
<% else %>

いいねボタンを用意

.like-button{:class => "like-button-#{diary.id}"}
  = render "diary_likes/like", diary: diary, user_id: diary.user_id
  • 日記ごとに部分テンプレート化したいいね!ボタンをrenderする

いいね機能を実装する

def create
  @like = DiaryLike.new(user_id: current_user.id, diary_id: params[:diary_id])
  @like.save
end

def destroy
  @like = DiaryLike.find_by(user_id: current_user.id, diary_id: params[:diary_id])
  @like.destroy
end
  • diary_idしか送られてこない & そのまま保存するわけでもないのでストロングパラメータは不要
  • remote: trueで送っているので、xxx.js.erbが呼ばれる

create.js.erbを作成

$(".like-button-<%= @like.diary_id %>").html("<%= j(render partial: 'like', locals: { diary: @like.diary, user_id: @like.user_id }) %>")

destroy.js.erbを作成

$(".like-button-<%= @like.diary_id %>").html("<%= j(render partial: 'like', locals: { diary: @like.diary, user_id: @like.user_id }) %>")

laravelでURLパラメータを取得する

Route::get('user/{id}', 'UserController@show');
public function show(Request $request, $id) {

こんな感じで取得できる

商用利用可な動画を探せるサイトメモ

videos.pexels.com

www.clipcanvas.com

  • Webサイトで動画を流したい時に使えるフリー動画を探せるサイト

react-nativeでbox-shadowをつける

IOSの場合

shadowColor: #ccc,
shadowOffset: {
  width: 0,
  height: 2,
},
shadowRadius: 0,
shadowOpacity: 1,
  • shadowOffset
    • width: 横への影
    • height: 縦の影
  • shadowOpacity
    • 影の透明度
    • 0が透明、1は透明なし

f:id:masaru_furuya:20180126160202p:plain

これでこんな感じで下に影をつけられる

Androidの場合

shadowColor: #ccc,
shadowOffset: {
  width: 0,
  height: 2,
},
shadowRadius: 0,
shadowOpacity: 1,
elevation: 2
  • Androidの場合はelevationをつける

yenta風の特定の位置までドラッグしたらフェードアウトする処理を実装する

$('.draggable').draggable({
    drag: function() {
      var offset = $(this).offset();
      var xPos = offset.left;
      var yPos = offset.top;
      if (xPos >= 210) {
        $('.xxx).addClass('fadeout-right').delay(700).fadeOut(1);
      }
      if (xPos <= -210) {
        $('.xxx).addClass('fadeout-left').delay(700).fadeOut(1);
      }
      if (yPos <= -250) {
        $('.xxx).addClass('fadeout-top').delay(700).fadeOut(1);
      }
      if (yPos >= 300) {
       $('.xxx).addClass('fadeout-bottom').delay(700).fadeOut(1);
      }
    }
  });
.fadeout-right {
  transform: rotate(30deg) scale(0.8);
  transition: 1s;
  opacity: 0;
  margin-left: 200px;
}

.fadeout-left {
  transform: rotate(-30deg) scale(0.8);
  transition: 1s;
  opacity: 0;
  margin-left: -200px;
}

.fadeout-top {
  transform: rotate(30deg) scale(0.8);
  transition: 1s;
  opacity: 0;
  margin-bottom: 200px;
}

.fadeout-bottom {
  transform: rotate(-30deg) scale(0.8);
  transition: 1s;
  opacity: 0;
  margin-bottom: -200px;
}

ちなみにスマホだとブラウザのドラッグイベントが検知できないので、 jquery-ui-touch-pinch.js のライブラリを使いました。

Railsでコメント機能を作る

ルーティングの追加

resources :diaries do
  resources :comments, only: [:create]
end

コントローラー側

@diaryComment = DiaryComment.new(diary_comment_params)
@diaryComment.user_id = User.first.id
respond_to do |format|
  if @diaryComment.save
    format.html { redirect_to diaries_url, notice: 'Diary was successfully created.' }
    format.json { render :show, status: :created, location: @diaryComment }
  else
    format.html { redirect_to diaries_url }
    format.json { render json: @diaryComment.errors, status: :unprocessable_entity }
  end
end

def diary_comment_params
  # params.require(:diary).permit(:content)
  params.require(:diary_comment).permit(:content, :diary_id)
end

フォームの追加

= form_for([diary, diary.diary_comments.build]) do |f|
  %img.img-responsive.img-circle.img-sm{:alt => "Alt Text", :src => "xxxx"}/
  / .img-push is used to add margin to elements next to floating images
  .img-push
    = f.hidden_field :diary_id
    = f.text_field :content, class: "form-control input-sm", placeholder: "コメントを入力"

ポイント

  • 1:多のform_forの指定
  • ネストしたリソースのルーティング

今回はAjaxとしては実装しなかったのでまたそれは次回

Tinder風のUIをJavaScriptで実装する

codepen.io

こちらのコードを参考に学んでみた。

$(".buddy").on("swiperight",function(){
      $(this).addClass('rotate-left').delay(700).fadeOut(1);
      $('.buddy').find('.status').remove();

      $(this).append('<div class="status like">Like!</div>');      
      if ( $(this).is(':last-child') ) {
        $('.buddy:nth-child(1)').removeClass ('rotate-left rotate-right').fadeIn(300);
       } else {
          $(this).next().removeClass('rotate-left rotate-right').fadeIn(400);
       }
    });  
.rotate-left
  transform: rotate(30deg) scale(0.8);
  transition: 1s;
  margin-left: 400px;
  cursor: e-resize;
  opacity: 0;
  z-index: 10;

「右に400px,30度傾けて80%の大きさに変更する」を1秒かけて行うという処理

 transform: rotate(30deg) scale(0.8);
 transition: 1s;
 margin-left: 400px;
  • transition: 1sがないと一瞬で動いてしまう

やっていること

  • rotate-leftで要素を傾ける
  • delayとfadeOutで少し遅らせてフェードアウト(hide)する
  • スワイプイベントはhammer.jsを使っている

思ったこと

Tinderのように左右のスワイプはこれで実現できる。 ただこれだと毎回同じ見た目のスワイプになってしまうので、 Yentaのようにドラッグしてどこかに飛ばすということは出来ないなぁ