I am a beginner with 3 months of programming experience. When I created my personal app, I introduced a ** like function using asynchronous communication **, but I implemented it using the googled one as it is, so I will organize my mind ( I thought I'd post an article to deepen my understanding.
In addition, this article will be written on the assumption that a gem called Devise is installed when implementing this function.
This time, we will use the User model, Posts model, and Likes model.
Create a migration of the Like model.
db/migrate/○○_create_likes.rb
class CreateLikes < ActiveRecord::Migration[5.0]
def change
create_table :likes do |t|
t.references :post, foreign_key: true
t.references :user, foreign_key: true
t.timestamps
end
end
end
Both user_id and post_id are set as ** association foreign keys **.
We will set the association for each model.
models/user.rb
has_many :posts
has_many :likes
models/post.rb
belongs_to :user
has_many :likes
models/like.rb
belongs_to :user
belongs_to :post
** users can like multiple ** and ** Posts can also be liked **, so Both are associated with like with ** has_many **.
config/routes.rb
Rails.application.routes.draw do
devise_for :users
resources :users
resources :posts do
resources :likes, only: [:create, :destroy]
end
end
Nest likes in posts. Nesting allows you to determine which post has a like.
** For reference ** If you do rails routes ...
Terminal
post_likes POST /posts/:post_id/likes(.:format) likes#create
post_like DELETE /posts/:post_id/likes/:id(.:format) likes#destroy
You can see that like is called in association with a specific post.
controllers/likes_controller.rb
class LikesController < ApplicationController
before_action :set_post
def create
@like = Like.create(user_id: current_user.id, post_id: @post.id)
end
def destroy
@like = current_user.likes.find_by(post_id: @post.id)
@like.destroy
end
private
def set_post
@post = Post.find(params[:post_id])
end
end
destroy gets what the currently logged-in user ** likes ** by association and I'm looking for a ** post (@post) ** to unlike.
This time, the like button and the display part of the number of likes will be prepared with a partial template. You can understand the reason by looking at the explanation of JavaScript in the next section.
haml:view/likes/_like.html.haml
- if Like.find_by(user_id: current_user.id, post_id: @post.id)
= link_to "/posts/#{@post.id}/likes/#{@post.likes.ids}", method: :delete, class:"element", remote: true do
= icon("fas", "heart", class: "like")
- else
= link_to "/posts/#{@post.id}/likes", method: :post, class:"element", remote: true do
= icon("fas", "heart", class: "unlike")
.count
= @post.likes.length
If you already like **, it returns true **, and if you haven't liked it yet, it returns false **.
** "remote: true do" is an option for asynchronous communication **. ** Originally, the page will move to the link destination **, but if you write "remote: true do", ** JS will be searched instead **. Therefore, ** page movement is stopped **. (Since the value seems to jump to the link destination, the page does not move, but it seems that it will be registered in the DB.)
○○.haml
#like{ id: @post.id }
= render "likes/like"
Please include this when calling the partial template prepared in the previous section. It is a miso that the id is described by ** custom data attribute **.
JavaScript This is the key to asynchronous communication.
javascript:views/likes/create.js.erb
$("#like_<%= @post.id %>").html("<%= j(render 'likes/like') %>");
javascript:views/likes/destroy.js.erb
$("#like_<%= @post.id %>").html("<%= j(render 'likes/like') %>");
Both have exactly the same description. ** It seems to be considered as JS when using the "j" method **. Someone just stopped pagination and was looking for ** JS **. ..
When you press the like button, the contents of [** like_ <% = @ post.id%> "**] will be rendered ** to the ** partial template you set earlier.
In other words, it will be updated asynchronously to "posts / like" of the partial template ** without updating the page.
Thank you for reading to the end. Perhaps it's not difficult for those who are used to it, but it's still very difficult for beginners ... We hope that all beginners who refer to this article will be able to implement it safely!
This time it was difficult and I couldn't afford it, so don't be silly. ..
Recommended Posts