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) {
こんな感じで取得できる
react-nativeでbox-shadowをつける
IOSの場合
shadowColor: #ccc, shadowOffset: { width: 0, height: 2, }, shadowRadius: 0, shadowOpacity: 1,
- shadowOffset
- width: 横への影
- height: 縦の影
- shadowOpacity
- 影の透明度
- 0が透明、1は透明なし
これでこんな感じで下に影をつけられる
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で実装する
https://codepen.io/developingidea/pen/meAIncodepen.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のようにドラッグしてどこかに飛ばすということは出来ないなぁ
hammer.jsを使ってブラウザで上下左右のスワイプを検知する
var element = document.getElementById('swipeTarget'); var hammertime = new Hammer(element); hammertime.get('swipe').set({ direction: Hammer.DIRECTION_ALL, threshold: 1, velocity:0.1 }); hammertime.on('swipeleft',function() { moveNext('left'); }); hammertime.on('swiperight',function() { moveNext('right'); }); hammertime.on('swipeup',function() { moveNext('top'); }); hammertime.on('swipedown',function() { moveNext('bottom'); });
こんな感じのコードになった。
注意点
- $()だと動かなかった。document.getElementByIdを使う必要がある
- direction: Hammer.DIRECTION_ALLの指定が無いと上下の検知がうまくできなかった