[Rails 6] Like function (synchronous → asynchronous) implementation

Introduction

We are creating a portfolio for job change activities. This time, we have implemented a like function that allows users to register articles as favorites. First, check if it works synchronously, and then move to asynchronous. I am writing an article for memorandum and review.

environment

Ruby on Rails '6.0.0' Ruby '2.6.5' Introduced Font Awesome

Premise

-The user management function (user model) has already been implemented, and new registration and login are possible. -The article posting function (article model) has already been implemented, and users can browse articles.

① Model description (Synchronization likes)

Terminal


% rails g model like

Create a Like model and describe the migration file. This time, the columns will be the foreign keys use_id and article_id to store the user ID and post ID.

db/migrate/20201111111111_create_likes.rb


class CreateLikes < ActiveRecord::Migration[6.0]
  def change
    create_table :likes do |t|
      t.references :article, null: false, foreign_key: true
      t.references :user, null: false, foreign_key: true
      t.timestamps
    end
  end
end

In addition, the association will be as shown in the figure below. (One user can have multiple likes, and one article can have multiple likes)

ER (2)

app/models/like.rb


class Like < ApplicationRecord
  belongs_to :article
  belongs_to :user
end

app/models/user.rb


(Omitted)
  has_many :article
  has_many :likes, dependent: :destroy

app/models/article.rb


(Omitted)
  has_many :likes, dependent: :destroy

By writing "dependent:: destroy", the associated likes will be deleted when the article is deleted or when the user is deleted.

Also, this time, by defining the "liked_by?" Method in the user model, we can determine whether the user likes it.

app/models/user.rb


(Omission)
has_many :article
has_many :likes, dependent: :destroy

def liked_by?(article_id)
  likes.where(article_id: article_id).exists?
end

By using the where method, we are looking for the existence of "article_id" in the likes table. Also, the exists? Method is "true" if there is a corresponding value, otherwise " A method that returns "false". It seems that the method related to the interaction with the table will be put in the model. ..

② Controller description (Synchronization likes)

Terminal


% rails g controller likes

Execute the command to generate a likes controller. Describe the create action and destroy action in the created controller, and set the grant and release of likes.

app/controllers/likes_controller.rb


class LikesController < ApplicationController

  def create
    Like.create(user_id: current_user.id, article_id: params[:id])
    redirect_to root_path
  end

  def destroy
    Like.find_by(user_id: current_user.id, article_id: params[:id]).destroy
    redirect_to root_path
  end

end

The find_by method used in the destroy action is a method that allows you to specify multiple search conditions.

③ Routing description

config/routes.rb


post 'like/:id' => 'likes#create', as: 'create_like'
delete 'like/:id' => 'likes#destroy', as: 'destroy_like'

You can set a name for the routing by using the as option.

④ View file description

This time, in my implementation, I want to implement the like function in multiple places, so I use a partial template to describe the like view.

ruby:app/views/likes/_like.html.erb


<div class="likes_buttons">
<% if user_signed_in? %>
  <% if current_user.liked_by?(@article.id) %>
    <%= link_to destroy_like_path(@article.id, current_user.likes.find_by(article_id: @article.id).id), method: :delete do %>
      <p class="like-button"><i class="fas fa-heart fa-2x" style="color: #e82a2a;"></i><span style="color: #e82a2a"><%= @article.likes.count %></span></p>
    <% end %>
  <% else %>
    <%= link_to create_like_path(@article.id), method: :post do %>
      <p class="like-button"><i class="far fa-heart fa-2x" style="color: #e82a2a;"></i><span style="color: #e82a2a"><%= @article.likes.count %></span></p>
    <% end %>
  <% end %>
<% end %>
</div>

I'm using Font Awesome to display a heart emoji. Also, <% = @ article.likes.count%> is used to display the number of likes.

This completes the synchronization like. Then move to asynchronous.

⑤ Edit view file

To implement asynchronously, add "remote: true" to the link_to method from the previous description. By adding remote: true, it seems that the parameters will be sent in JS format instead of HTML format. By giving this time, views/likes/create.js.erb will be called after the create action of likes_controller.rb.

ruby:app/views/likes/_like.html.erb


<div class="likes_buttons">
<% if user_signed_in? %>
  <% if current_user.liked_by?(@article.id) %>
    <%= link_to destroy_like_path(@article.id, current_user.likes.find_by(article_id: @article.id).id), method: :delete, remote: true do %>
      <p class="like-button"><i class="fas fa-heart fa-2x" style="color: #e82a2a;"></i><span style="color: #e82a2a"><%= @article.likes.count %></span></p>
    <% end %>
  <% else %>
    <%= link_to create_like_path(@article.id), method: :post, remote: true do %>
      <p class="like-button"><i class="far fa-heart fa-2x" style="color: #e82a2a;"></i><span style="color: #e82a2a"><%= @article.likes.count %></span></p>
    <% end %>
  <% end %>
<% end %>
</div>

⑥ Edit controller

Here, the description of "redirect_to" that was described in the controller was deleted. If you leave it as it is, screen transition will occur and asynchronous processing will not be possible. Also, use "before_action" to get the id of the post.

app/controllers/likes_controller.rb


class LikesController < ApplicationController
  before_action :article_params
  def create
    Like.create(user_id: current_user.id, article_id: params[:id])
  end

  def destroy
    Like.find_by(user_id: current_user.id, article_id: params[:id]).destroy
  end

  def article_params
    @article = Article.find(params[:id])
  end
end

⑦ Description of js.erb file

Generate the following two files and write the same.

ruby:app/views/likes/create.js.erb


$('.likes_buttons').html("<%= j(render partial: 'likes/like', locals: {article: @article}) %>");

ruby:app/views/likes/destroy.js.erb


$('.likes_buttons').html("<%= j(render partial: 'likes/like', locals: {article: @article}) %>");

Only the HTML of the likes_buttons part describes the process of partially updating with render.

After that, if you call the partial template in the part you like, it is completed.

ruby:app/views/articles/show.html.erb


<%= render partial: "likes/like", locals: { article:@article } %>

⑧ jQuery environment settings in Rails 6

It seems that webpacker has become the standard from Rails6, and I had a hard time introducing jQuery, so I will describe it at the end. As a premise, it is assumed that yarn is already installed in mac. Also, this time my implementation also uses bootstrap 4, so I will proceed on that premise.

Terminal


% yarn add bootstrap jquery popper.js

First, execute the above command in the application directory you are creating.

Next, I will describe the introduction.

app/javascript/packs/application.js


require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
...
//Fill in below
require("bootstrap");
...

Since bootstrap and jQuery are dependent, it seems that "require (" jquery ")" is also described at the same time as "require (" bootstrap ")".

Finally, I will describe the webpacker.

config/webpack/environment.js


const { environment } = require('@rails/webpacker')

const webpack = require('webpack');

environment.plugins.prepend(
  'Provide',
  new webpack.ProvidePlugin({
    $: 'jquery/src/jquery',
    jQuery: 'jquery/src/jquery',
    Popper: ['popper.js', 'default']
  })
)

module.exports = environment

Now you can use bootstrap and jQuery on Rails.

Finally

I think there are some expressions that are difficult to understand, but I would like to make corrections as appropriate.

The articles that I referred to this time are as follows. Thank you very much. https://techtechmedia.com/favorite-function-rails/#i-2 https://qiita.com/naberina/items/c6b5c8d7756cb882fb20

Recommended Posts

[Rails 6] Like function (synchronous → asynchronous) implementation
[Rails] Asynchronous implementation of like function
[Rails] Implementation of like function
[Rails, JavaScript] Implemented like function (synchronous / asynchronous communication)
[Rails] About implementation of like function
[Ruby on rails] Implementation of like function
[Rails] [jQuery] Asynchronous like function implementation using remote: true and js.erb
Rails fuzzy search function implementation
Implementation of like function (Ajax)
[Rails 6] Implementation of search function
[Rails] Implementation of category function
Login function implementation with rails
[Rails 6] Pagination function implementation (kaminari)
[Rails] Implementation of CSV import function
[Rails] Implementation of image preview function
[Rails] Implementation of user withdrawal function
[Rails] Implementation of CSV export function
Rails sorting function implementation (displayed in order of number of like)
[Rails] gem ancestry category function implementation
[Rails] Comment function implementation procedure memo
Implementation of like function in Java
[Ruby on Rails] Follow function implementation: Bidirectional
Rails [For beginners] Implementation of comment function
Rails Basic CRUD function implementation procedure scaffold
[Vue.js] Implementation of menu function Implementation version rails6
[Vue.js] Implementation of menu function Vue.js introduction rails6
[Rails 6] Asynchronous (Ajax) follow function is implemented
DM function implementation
[Rails 6] Ranking function
[Rails] Category function
Rails follow function
[Rails] Notification function
[Rails] Implementation of search function using gem's ransack
Implementation of Ruby on Rails login function (Session)
[Rails 6] Implementation of inquiry function using Action Mailer
Posting function implemented by asynchronous communication in Rails
[Rails] Implementation of retweet function in SNS application
Ruby on Rails Email automatic sending function implementation
Rails hashtag search implementation
Ruby on Rails <2021> Implementation of simple login function (form_with)
Rails6 countdown timer implementation
[rails] gem'payjp' implementation procedure
[Rails] Implementation of drag and drop function (with effect)
Rails CRUD function implementation ② (edited and detailed this time)
Comment function (Ajax) implementation
[Rails] Implement search function
Implementation of search function
[Rails] Implemented hashtag function
[Ruby on Rails] Implementation of tagging function/tag filtering function
[rails] tag ranking function
Image preview function implementation
[Rails] Implementation of multi-layer category function using ancestry "seed"
Implementation of pagination function
Search function [copype implementation]
[Rails] Implementation of SNS authentication (Twitter, Facebook, Google) function
Rails CRUD function implementation ① (new addition, deletion this time)
I tried to implement the like function by asynchronous communication
[Rails] Implementation of multi-layer category function using ancestry "Editing form"
[Order function] rails Order function implementation Confirmation screen will also be created ~
[Ruby on Rails] Bookmark (favorite registration, like) function: One direction
[Rails] Implementation of multi-layer category function using ancestry "Creation form"