我有一个关于 Rails 的问题。我正在建立一个帖子有喜欢的网站,帖子和喜欢都是他们自己的模型。用户只能对某个帖子点赞一次,一旦点赞按钮就会变成“不点赞”按钮,从而删除点赞。
我正在尝试创建一种用户可以喜欢或不同于帖子的体验 - 并且不会被重定向,但喜欢会更新。以我有限的铁路知识,这不是一件容易的事。谁能指出我正确的方向?
这是我的 /likes/_likes.html.erb
模板部分,带有喜欢/不喜欢按钮:
<% 喜欢 = @post.likes.find { |like| like.user_id == current_user.id} %>
<div class="likes">
<% if liked %>
<%= button_to 'Unlike', post_like_path(@post, liked), method: :delete %>
<% else %>
<%= button_to 'Like', post_likes_path(@post), method: :post %>
<% end %>
<%= @post.likes.count %><%= (@post.likes.count) == 1 ? 'Like' : 'Likes'%>
</div>
这是我的 Like Controller :
class LikesController < ApplicationController
before_action :find_post
before_action :find_like, only: [:destroy]
def create
if (!already_liked?)
@post.likes.create(user_id: current_user.id)
end
end
def destroy
if (already_liked?)
@like.destroy
end
end
private
def already_liked?
Like.where(user_id: current_user.id, post_id:
params[:post_id]).exists?
end
def find_post
@post = Post.find(params[:post_id])
end
def find_like
@like = @post.likes.find(params[:id])
end
end
以下是 _likes 部分显示的 View 之一(尽管该问题在出现的所有地方都仍然存在):
<div class="post-display">
<% if @post.title %>
<h1><%= @post.title %></h1>
<% end %>
<% if @post.user %>
Post by <%= @post.user.email %>
<% end %>
<% if @post.price %>
<p>$<%= sprintf "%.2f", @post.price %></p>
<% end %>
<% if @post.description %>
<p><%= @post.description %></p>
<% end %>
<% if @post.image.present? %>
<%= image_tag @post.image.variant(:small) %>
<% end %>
<%= render 'likes/likes' %>
</div>
<% if current_user == @post.user %>
<%= link_to "Edit", edit_post_path(@post) %>
<%= button_to "Delete", @post, method: :delete %>
<% end %>
<% if @post.comments.count > 0 %>
<div class="post-comments">
<h2 class="post-comments-headline">Comments</h2>
<%= render @post.comments %>
</div>
<% end %>
<h2>Add a comment:</h2>
<%= render 'comments/form' %>
如果您对我的问题没有答案,但对如何改进我的代码有想法 - 请让我知道!我正在尝试在这里学习...
谢谢,
吉尔
最佳答案
由于您使用的是 rails 7,因此渲染 turbo_stream
来响应“like”和“unlike”按钮将更新页面而不刷新。
# config/routes.rb
resources :posts do
# NOTE: i'm using singular `resource`, since there is no need to have `id`
# for each like.
resource :like, only: [:destroy, :create]
end
https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Resources.html#method-i-resource
# app/models/post.rb
class Post < ApplicationRecord
has_many :likes
def liked_by? user
likes.where(user: user).exists?
end
end
# app/models/like.rb
class Like < ApplicationRecord
belongs_to :post
belongs_to :user
# NOTE: avoid double likes.
validates_uniqueness_of :user, scope: :post, message: "already liked this post"
# TODO: create a unique index migration, to really avoid double likes.
# `add_index :likes, [:post_id, :user_id], unique: true`
end
我稍微简化了LikesController
。不需要 before_action 过滤器:
# app/controllers/likes_controller.rb
class LikesController < ApplicationController
# POST /posts/:post_id/like
def create
# NOTE: uniqueness validation in `Like` model will prevent creating dup likes.
post.likes.create(user: current_user)
# you can access `like` error if you want to show it:
# like = post.likes.create(user: current_user)
# like.errors.full_messages
# NOTE: that's it, now we render `likes` partial inside a `turbo_stream`
render turbo_stream: turbo_stream.replace(
helpers.dom_id(post, :like), # this is the target div `id` that will be replaced
partial: "posts/likes", # with `likes` partial.
locals: { post: post }
)
end
# DELETE /posts/:post_id/like
def destroy
# NOTE: this will work regardless if there are any likes or not.
post.likes.where(user: current_user).destroy_all
# NOTE: alternatively, we can render the same `turbo_stream` as above
# in a template `likes/likes.turbo_stream.erb`:
render :likes
end
private
def post
@post ||= Post.find params[:post_id]
end
end
<!-- app/views/posts/_likes.html.erb -->
<!-- `dom_id` helps us generate a uniq id: "like_post_1" -->
<div id="<%= dom_id(post, :like) %>">
<!-- yes, there is a rails helper for this -->
<%= pluralize post.likes.count, "like" %>
<% if post.liked_by? current_user %>
<%= button_to "Unlike", post_like_path(post), method: :delete %>
<% else %>
<%= button_to "Like", post_like_path(post) %>
<% end %>
</div>
此 turbo_stream
与 create
操作相同:
<!-- app/views/likes/likes.turbo_stream.erb -->
<%= turbo_stream.replace dom_id(@post, :like) do %>
<%= render "posts/likes", post: @post %>
<% end %>
关于ruby-on-rails - 就地删除/创建(无需重定向),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74063295/