ruby-on-rails - 就地删除/创建(无需重定向)

标签 ruby-on-rails ruby ruby-on-rails-7

我有一个关于 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_streamcreate 操作相同:

<!-- app/views/likes/likes.turbo_stream.erb -->
<%= turbo_stream.replace dom_id(@post, :like) do %>
  <%= render "posts/likes", post: @post %>
<% end %>

https://turbo.hotwired.dev/handbook/streams

关于ruby-on-rails - 就地删除/创建(无需重定向),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74063295/

相关文章:

ruby-on-rails - Rails 4 Devise 重新激活 "soft deleted"帐户

javascript - 如何将自定义 JS 文件添加到新的 rails 7 项目

ruby-on-rails - [7.1]在迁移中意味着什么

ruby-on-rails - 运行 Ruby 代码的 Google OpenID 示例?

ruby-on-rails - 替换字符串中的电子邮件、电话和地址(就像 AirBnB 那样)?

ruby - 删除正则表达式匹配行之前的换行符

ruby-on-rails - Ruby 控制台参数

javascript - 使用 importmaps 时如何在 Rails 7 中自定义 Trix 工具栏?

ruby-on-rails - 如何增加 JRuby Trinidad 池的大小?

ruby-on-rails - ruby : Compilation error during installation of ‘rails’ gem on macOS