Carrying out Rails challenges. Among them, there was a task to create a "Like" function with Ajax. Actually, I used to create a "Like" function that is not asynchronous communication **.
▼ Click here for the record at that time Create a "Like" function in Rails-(1) Give an alias to the association Create a "Like" function with Rails-② Create action for "Like" Create a "Like" function in Rails-③ Enable to cancel "Like"
Also, in another article ** "Someday with Ajax" Like! While saying "I want to make a function" **, I didn't make it, so I would like to summarize the procedure for making it in a notebook.
In addition, the implementation procedure is introduced here as a memo. Please see the above article for the actual detailed implementation procedure.
The mounting environment is as follows.
Rails 5.2.3
Ruby 2.6.0
The Like feature requires a slight twist in the definition of association. First of all, check the DB structure and specifications this time as well. As a result, the DB structure and specifications were as follows.
images
table is outside the screen.Of these, ** "I can't like my post" ** is realized by not displaying the "Like" button in the view when posting my own post, as shown below. ..
▼ When posting your own (display the edit / delete button)
▼ When posting other than yourself (display the like button)
Also, ** like_posts
** written in red is another name for the association described below.
Those who say "another name for the association ???" are explained in detail in this article.
Create a "Like" function in Rails-(1) Give an alias to the association
Let's implement it from here. First, create a migration file.
It is assumed that the users
table and the posts
table have been created respectively.
Create the likes
table as follows.
db/migrate/XXXXXXXXXX_create_likes.rb
class CreateLikes < ActiveRecord::Migration[5.2]
def change
create_table :likes do |t|
t.references :user
t.references :post
t.timestamps
t.index [:user_id, :post_id], unique: true
end
end
end
Likes are achieved if you can properly enter the data in the likes
table.
(Please see this article for why this happens ^^)
Create a "Like" function with Rails-② Create action for "Like"
Also,
t.index [:user_id, :post_id], unique: true
In the part of, the DB side controls so that "Like" to the same user and the same post cannot be posted.
The associations are described in the model files of posts
, users
, and likes
as follows.
class User < ApplicationRecord
# ★1
has_many :posts, dependent: :destroy
# ★2
has_many :likes, dependent: :destroy
has_many :like_posts, through: :likes, source: :post
end
class Post < ApplicationRecord
# ★1
belongs_to :user
# ★2
has_many :likes, dependent: :destroy
has_many :users, through: :likes
end
class Like < ApplicationRecord
belongs_to :post
belongs_to :user
end
The two lines below ★ 1 and ★ 2 of User
and Post
are respectively.
-** ★ 1 ... Definition of post posted by user ** -** ★ 2 ... Definition of posts that users like **
is. The point is that the flow of User
=> Like
=> Post
is given an alias of: like_posts
and the association.
has_many :like_posts, through: :likes, source: :post
with this,
user = User.first
user.like_posts
↑ In this form, you can get a list of posts that the user has liked.
The contents of the controller are as follows. Define "Like" (create
) and" Like "( destroy
) as follows.
likes_controller.rb
class LikesController < ApplicationController
def create
@post = Post.find(params[:post])
current_user.like(@post)
end
def destroy
@post = Like.find(params[:id]).post
current_user.unlike(@post)
end
end
The like
and unlike
in the code are the User
model methods that like and unlike, respectively.
like
and unlike
)In User.rb
, define the following model method.
models/user.rb
class User < ApplicationRecord
has_many :posts, dependent: :destroy
has_many :likes, dependent: :destroy
has_many :like_posts, through: :likes, source: :post
def own?(object)
id == object.user_id
end
def like(post)
likes.find_or_create_by(post: post)
end
def like?(post)
like_posts.include?(post)
end
def unlike(post)
like_posts.delete(post)
end
end
like?
Is a method to determine if ** the user has already liked the post **. I will post it because I will use it in the view after this.
Also, owm?
Is not directly related to this implementation, but it is included because it will be used in the view after this. A method to determine the creator of the target object.
like_posts.delete(post)
I wonder if it's a miso to get a list of posts that users like with like_posts
and destroy it.
After that, I will write the view. First, create a view with a ** non-asynchronous ** implementation. For readability, decoration elements and functions have been omitted.
- @posts.each do |post|
- if logged_in? #Login confirmation
- if current_user.own?(post) #Confirm ownership
= link_to post_path(post), method: :delete do
= icon 'far', 'trash-alt' #Trash can icon
= link_to edit_post_path(post) do
= icon 'far', 'edit' #Edit icon
- else
- if current_user&.like?(post) #Already like! Check if you are doing
= link_to like_path(current_user.likes.find_by(post: post)), method: :delete do
= icon 'fa', 'heart' #Heart (black)
- else
= link_to likes_path(post: post), method: :post do
= icon 'far', 'heart' #Heart (white)
logged_in?
is a Gem-generated method that determines if you are logged in.
In fact, with the implementation so far, the "Like" function itself has been created. When you press the "Like" button and reload the screen, you can see that "Like" and "Unlike" are switched respectively.
▼ If you press "Like" and then reload (press the button outside the screen), the icon will switch.
Well, the main subject is from here. We will realize these "Like" functions asynchronously.
remote: true
First, set the link to remote: true
and make the communication asynchronous.
- @posts.each do |post|
- if logged_in?
- #abridgement
- else #Link respectively_remote behind to:Add true
- if current_user&.like?(post)
= link_to like_path(current_user.likes.find_by(post: post)), method: :delete, remote: ture do
= icon 'fa', 'heart'
- else
= link_to likes_path(post: post), method: :post, remote: ture do
= icon 'far', 'heart'
Of the above view files, the like
and unlike
buttons are separated into separate partials for subsequent Ajax processing. In addition, the id
attribute is also added so that it can be used as a marker for Ajax processing.
- @posts.each do |post|
- if logged_in?
- #abridgement
- else id="like-button-#{post.id}" #Add id attribute
- if current_user&.like?(post)
= render 'likes/unlike_button', post: post #Move to partial
- else
= render 'likes/like_button', post: post #Same as above
Here is the contents of the partial.
slim:views/likes/_unlike_button.html.slim
= link_to like_path(current_user.likes.find_by(post: post)), method: :delete, remote: true do
span.c-icon-button= icon 'fa', 'heart', class: 'fa-lg'
slim:views/likes/_like_button.html.slim
= link_to likes_path(post: post), method: :post, remote: true do
span.c-icon-button= icon 'far', 'heart', class: 'fa-lg'
.js.erb
fileCreate .js.erb
files corresponding to the create
and destroy
actions of likes
, respectively.
js:/views/likes/create.js.erb
$("#like-button-<%= @post.id %>").html("<%= j(render 'unlike_button', post: @post) %>")
js:/views/likes/destroy.js.erb
$("#like-button-<%= @post.id %>").html("<%= j(render 'like_button', post: @post) %>")
The .js.erb
file is explained in this article, so please have a look if you like.
remote: true to POST ajax posts.
It was a very easy step, but with the above implementation, I like it! Features can be implemented asynchronously: relaxed: It's surprisingly easy!
Impressions ... It was the third time I made Ajax with both the "Like" function and the Rails function, so I'm glad I was able to do both: relaxed:
Recommended Posts