[Rails] I tried to implement "Like function" using rails and js

This time I would like to implement a like function with rails and js

** Finally, we will implement a function that allows users to display likes of posts **

The explanation of reading js is omitted!

** Articles that I referred to **

https://techtechmedia.com/favorite-function-rails/ https://qiita.com/hayabusa3703/items/2b916e652a1dc85bb6e3

Rendering

スクリーンショット 2020-12-22 21.34.19.png

Preparation

Because users like a lot of posts and posts are also liked by a lot of users

Many-to-many table structure of users and posts with likes table as intermediate table

rails g model like 

** Migration file **


class CreateLikes < ActiveRecord::Migration[5.0]
  def change
    create_table :likes do |t|
      t.integer     :user_id
      t.integer     :drink_id
      t.timestamps
    end
  end
end
rails g controller likes

association

like.rb


class Like < ApplicationRecord
  belongs_to :user
  belongs_to :drink, counter_cache: :likes_count
end

-Counter_cahce:: likes_count means to put the value of the number of related likes in the value of the column called likes_count of the relation destination. So let's add the likes_count column to the stories table. (It's okay if you run rails g migration AddLikes_countToStories likes_count: integer in the terminal.) Source of this text

drink.rb


class Drink < ApplicationRecord
  has_many :likes
  has_many :liking_users, through: :likes, source: :user
end

Since there is no liking_users model, I tell rails that I will use the likes table as an intermediate table and draw associations with the user model.

** has_many is not the essence of belongs_to to form an association, but a method that creates a method. ** **

In other words, if you do something like @ drink.liking_user, you can create a method that can get a list of users who like the post, and you can also create an association.

user.rb


  has_many :likes
  has_many :like_drinks, through: :likes, source: :drink

You can also get a list of posts that the user liked by doing something like user.like_drinks. This is useful when viewing posts that the user likes, which is common on Instagram and Twitter.

** has_many is not the essence of belongs_to to form an association, but a method that creates a method. ** **

Let's remember this and go home.

Like button description

drinks/index.html.erb

This is the post list page


<%if @drinks%>
      <% @drinks.each do |drink|%>
      <li class='list'>
        <%= link_to drink_path(drink.id) do %>
          <%= link_to user_path(drink.user.id) do%>
            <div class="user-info-timeline">
                <%=image_tag drink.user.image.variant(resize: '60x60'),class: "user-img-timeline"  if drink.user.image.attached?%> 
                <div class="username-timeline">
                  <%= drink.user.nickname %>
                </div>
            </div>
          <% end %>
        <div class='item-img-content'>
          <%= image_tag drink.image , class: "item-img" if drink.image.attached? %>
          
          <%# if drink.trade%>
          
   
          
          <%# end %>
        </div>
        <div class='item-info'>
          <h3 class='item-name'>
            <%= drink.name %>
          </h3>
          <div class='item-price'>
            <span><%= drink.price %>Circle<br>(tax included)</span>
            <div class='star-btn'>
              <%# image_tag "star.png ", class:"star-icon" %>
              <span class='star-count'>0</span>
            </div>
          </div>
          <div class='item-explain'>
            <%= drink.explain%>
          </div>
          <div class='item-tag'>
            <% drink.tags.each do |tag| %>
              #<%=tag.tag_name%>
            <%end%>
          </div>
          <%= render "likes/like",drink: drink%>
        </div>
       
        <% end %>
      </li>
      <%end%>
    

 <%= render "likes/like",drink: drink%>

I want you to pay attention to!

First of all, to improve readability The like button of the image is cut out with a partial template,

And, for the drink: drink part,

   <% @drinks.each do |drink|%>

The block variable in each statement of is passed to apply to likes/like.

What is a block variable (skip those who understand it)

Block variables are variables that can be used only within the method, such as each statement, times statement, and form_with. In other words, if it is each, a variable that can be used without a range from each to end

@There is a lot of information in drinks as an array,|drink| By doing so, each piece of information in the array is included in drink, and the number of arrays in @drinks is displayed.

likes/_like.html.erb

It is customary to name the file _like to make it easier to understand that it is a partial (partial template). However

<%= render "likes/like",drink: drink%>

You don't need an underscore when calling with


<div class="like" id="like-link-<%= drink.id %>">
  <% if current_user.likes.find_by(drink_id: drink.id) %>
    <%= link_to unlike_path(drink.id), method: :delete, remote: true do %>
        <div class = "iine__button">❤️<%= drink.likes.count %></div>
    <% end %>
  <% else %>
    <%= link_to like_path(drink.id), method: :post, remote: true do %>
        <div class = "iine__button">♡️<%= drink.likes.count %></div>
    <% end %>
  <% end %>
</div>


id="like-link-<%= drink.id %>"

Is miso.

I want to switch screens asynchronously with js, so to distinguish the id for each post so that I can get the id Let's write it like this.


<%= link_to unlike_path(drink.id), method: :delete, remote: true do %>

, remote: true

By writing

Since ajax fires when you press the link, communication is performed asynchronously.

If you like it by pressing the like button, it will jump to like_path otherwise.

Since each path has not been defined yet, a routing error will occur if it is left as it is.

routes.rb

  post 'like/:drink_id' ,to: 'likes#like', as: 'like'
  delete 'like/:drink_id',to: 'likes#unlike', as: 'unlike'

Let's write

By setting as:'like', you would have to specify the path as likes_like_path (drink.id). You can send a post request to likes # like with like_path (drink.id)

Now that you've followed the link to send your request, let's take a look at the controller.

likes_controller


class LikesController < ApplicationController
  include SessionsHelper

  before_action :set_variables
  
  def like  
    like = current_user.likes.new(drink_id: @drink.id)
    #redirect_to drinks_path
    #Since js is used, screen transition is not performed
    #binding.pry
    like.save
  end

  def unlike
    like = current_user.likes.find_by(drink_id: @drink.id).destroy
    #binding.pry
  end

  private

    def set_variables
      @drink = Drink.find(params[:drink_id])
      @id_name = "#like-link-#{@drink.id}"
    end

end

remote: Since the like and dislike actions are called from the true link, The default transition destinations are ilke.js.erb and dislike.js.erb respectively.

「⚠︎ @id_name = "#like-link-#{@drink.id}" I think that writing View processing in Controller is not very good in terms of MVC pattern. "

It's not very good, but there is no functional problem, so I'll move on.

likes/like.js.erb

$("<%= @id_name %>").html('<%= escape_javascript(render("likes/like", drink: @drink  )) %>');

/likes/unlike.js.erb

$("<%= @id_name %>").html('<%= escape_javascript(render("likes/like", drink: @drink  )) %>');

Go back to likes/_like.html.erb

At this time again

drink: @drink 

Let's pass the variable to _like.html.erb

This @drink is

** likes_controller **


  private

    def set_variables
      @drink = Drink.find(params[:drink_id])
      @id_name = "#like-link-#{@drink.id}"
    end

It's @drink.

This is the end of implementation. Thank you for your hard work.

As a bonus, cover posts that users like

users/show.html.erb


 <%= link_to "#{@user.nickname}Liked posts",user_likes_path(@user.id)%>

Create a link like this

@userh is users # show @user = User.find (params [: id]) I define it in a common way

I haven't defined user_like_path yet

routes.rb

  get 'user/likes/:id',  to: 'users#likes',as: 'user_likes'
  resources :users do
    member do
      get :following,:followers
      #If you use the member method
      #Will handle URl containing user id
    end
  end

I think that everyone will do resources: user, so get'user/likes /: id', to:'users # likes', as:'user_likes' on resources

Let's write

Now you can skip the GET request to users # likes once you follow the link

users_controller

   def likes
    @user = User.find(params[:id])
    @drinks = @user.like_drinks.paginate(page: params[:page],per_page: 10).order("created_at DESC")
     
   end

Let's implement it like this

.paginate(page: params[:page],per_page: 10)

You don't have to write if you haven't incorporated pagenations yet.

The .like_drinks method

  has_many :like_drinks, through: :likes, source: :drink

And user.rb, so you can get a list of posts that users like.

By default, you will be redirected to users/likes.html.erb, so prepare that view as well.

users/likes.html.erb



  <div class="user-profile">
    <h2 class="user-profile-name"><%= current_user.nickname %></h2>
    <h2><%= image_tag @user.image.variant(resize: '100x100'),class: 'user-img' if @user.image.attached? %></h2>
    <div class="user-like-post">
      <%= link_to "#{@user.nickname}Liked posts",user_likes_path(@user.id)%>
    </div>
    <div class="user-edit">
      <% if current_user?(@user) %>
      <%= link_to "Edit profile",edit_user_path(@user)%>  
      <% end %>
    </div>
      <% unless current_user?(@user) %>
        <div id="follow_form">
        <% if current_user.following?(@user) %>
          <%= render 'unfollow' %>
        <% else %>
          <%= render 'follow' %>
        <% end %>
        </div>
      <% end %>
  </div> 





<% @user ||= current_user %>
<div class="stats">
  <a href="<%= following_user_path(@user) %>">
    <strong id="following" class="stat">
      <%= @user.following.count %>
    </strong>
    following
  </a>
  <a href="<%= followers_user_path(@user) %>">
    <strong id="followers" class="stat">
      <%= @user.followers.count %>
    </strong>
    followers
  </a>
</div>

<div class='main'>

  <%#Product list%>
  <div class='item-contents'>
    <h2 class='title'><%= @user.nickname%>Post</h2>
    <%= will_paginate @drinks%>
    <ul class='item-lists'>
      
      <%#If there is something in the instance variable of the product, let's make it possible to expand all the contents%>
      <%if @drinks%>
      
      <% @drinks.each do |drink|%>
      
      <li class='list'>
      
        <%= link_to drink_path(drink.id) do %>
        <div class='item-img-content'>
          <%= image_tag drink.image , class: "item-img" if drink.image.attached? %>
          
          <%# if drink.trade%>
          
   
          
          <%# end %>
        </div>
        <div class='item-info'>
          <h3 class='item-name'>
            <%= drink.name %>
          </h3>
          <div class='item-price'>
            <span><%= drink.price %>Circle<br>(tax included)</span>
            <div class='star-btn'>
              <%# image_tag "star.png ", class:"star-icon" %>
              <span class='star-count'>0</span>
            </div>
          </div>
          <div class="item-explain">
            <%= drink.explain%>
          </div>
        </div>
        <% end %>
      
      </li>
       
      <%end%>
    </ul>
    <%= will_paginate @drinks%>
  </div>
  <%end%>
</div>

I'm like this

That's it. Thank you for your hard work.

Summary of code so far

My github

Recommended Posts

[Rails] I tried to implement "Like function" using rails and js
Try to implement tagging function using rails and js
I tried to implement the like function by asynchronous communication
Rails API mode I tried to implement the keyword multiple search function using arrays and iterative processing.
I tried to implement the image preview function with Rails / jQuery
I tried to implement a server using Netty
Rails learning How to implement search function using ActiveModel
[Rails / JavaScript / Ajax] I tried to create a like function in two ways.
I tried unit testing Rails app using RSpec and FactoryBot
[Swift] I tried to implement the function of the vending machine
I tried using Hotwire to make Rails 6.1 scaffold a SPA
[Rails] I tried to implement batch processing with Rake task
[Ruby on Rails] Since I finished all Rails Tutorial, I tried to implement an additional "stock function"
I tried to integrate Docker and Maven / Netbean nicely using Jib
[Rails] [jQuery] Asynchronous like function implementation using remote: true and js.erb
Rails Tutorial Extension: I tried to create an RSS feed function
I tried to make a group function (bulletin board) with Rails
I tried to introduce CircleCI 2.0 to Rails app
I tried to implement the Iterator pattern
I tried to use Selenium like JQuery
How to implement image posting using rails
I tried to implement ModanShogi with Kinx
[Rails] Implementation of multi-layer category function using ancestry "I tried to make a window with Bootstrap 3"
I introduced WSL2 + Ubuntu to Window10 and tried using GDC, DMD, LDC
I tried to make my own transfer guide using OpenTripPlanner and GTFS
I tried barcode scanning using Rails + React + QuaggaJS
I tried to implement polymorphic related in Nogizaka.
[Rails] I tried to raise the Rails version from 5.0 to 5.2
I tried to organize the session in Rails
I tried to link grafana and postgres [docker-compose]
[Android] I quit SQLite and tried using Realm
Implement star rating function using Raty in Rails6
I tried to implement deep learning in Java
How to implement the breadcrumb function using gretel
Flow to implement image posting function using ActiveStorage
[Rails] I made a draft function using enum
How to implement a like feature in Rails
I tried to link JavaFX and Spring Framework.
I tried to implement a function equivalent to Felica Lite with HCE-F of Android
How to implement image posting function using Active Storage in Ruby on Rails
[JDBC ③] I tried to input from the main method using placeholders and arguments.
I tried using Gson
[Rails] Implement search function
I tried using TestNG
I tried using Galasa
Continued ・ Flow to implement image posting function using ActiveStorage
I tried to implement file upload with Spring MVC
I tried using the Server Push function of Servlet 4.0
How to implement a like feature in Ajax in Rails
I tried to read and output CSV with Outsystems
I tried to implement TCP / IP + BIO with JAVA
I tried to implement Firebase push notification in Java
[Rails, JS] How to implement asynchronous display of comments
I tried to operate SQS using AWS Java SDK
I started MySQL 5.7 with docker-compose and tried to connect
I tried to integrate AWS I oT button and Slack
Implement user follow function in Rails (I use Ajax) ②
I tried to make a login function in Java
I tried using YOLO v4 on Ubuntu and ROS
I want to define a function in Rails Console
Implement user follow function in Rails (I use Ajax) ①