ruby 对于嵌套的注解,如何将父注解的ID传递给注解表单?

cwdobuhd  于 12个月前  发布在  Ruby
关注(0)|答案(1)|浏览(119)

我已经成功地创建了以下形式的评论,当评论直接引用一篇文章时。

<%= form_with(model: [@article, @article.comments.build]) do |f| %>
 
 <p>Comment:</p>
 <%= f.text_area :body, class:'' %>
 <%= image_submit_tag("Post.jpg", height: "25", width: "25", class: "mt-5 ms-3") %>
<% end %>

但是,现在我试图创建嵌套的注解,我不知道如何传递父注解的ID到表单,当表单作为对先前创建的注解的回复访问时,或者如何传递nil父注解ID,当表单直接从文章本身访问并引用该文章时。
在app/views/articles/show.html.erb中,评论表单被调用,以便直接在文章上创建评论,这是我尝试过的:

<%= render '/comments/comment_form', locals: {article: @article, parent: nil} %>

在app/views/comments/show.html.erb中,表单被调用作为对先前创建的评论的回复,这是我尝试的:

<%= render '/comments/comment_form', locals: {article: comment.article, parent: comment} %>

在尝试创建嵌套注解之前,我只是从articles/show.html.erb中呈现表单,而没有传递任何东西(没有本地变量:{etc.})而且效果很好。
我非常希望在这方面得到一些指导。以下是我认为与此相关的所有其他代码。
在app/models/comments.rb中

class Comment < ApplicationRecord
  belongs_to :article
  belongs_to :commenter, class_name: 'User', foreign_key: :user_id
  belongs_to :parent, class_name: 'Comment', optional: true

  has_many :comments, foreign_key: :parent_id

  validates_presence_of :body, length: { minimum: 3, maximum: 500 }
end

在app/controllers/comments_controller.rb中

class CommentsController < ApplicationController
  before_action :authenticate_user!

  

  def create
    @comment = Comment.new(comment_params.merge(commenter: current_user))
    @comment.article_id = params[:article_id]
    @comment.save
    redirect_to article_path(params[:article_id])
  end

  private

  def comment_params
    params.require(:comment).permit(:body, :article_id, :user_id, :parent_id)
  end
end
odopli94

odopli941#

你混淆了示例变量和局部变量,这很容易做到。
任何带有@符号的东西都是Ruby中的示例变量。它在Rails中的工作方式是,控制器中的所有示例变量都通过 view context(传递给模板引擎的上下文)对视图可用。把它基本上想象成你观点中的自我。
另一方面,本地赋值在调用render时传递,并作为普通方法或通过local_assigns方法调用。然后Rails通过元编程将这些调用委托给视图上下文。这些调用不应该有@标志。同样值得注意的是,view locals 不应该与 local variables 混淆,这是一种语言特性。
所以让我们从改变partial开始,实际上使用局部变量:

<%= form_with(model: [local_assigns(:parent), comment]) do |f| %>
  # ..
<% end %>

local_assigns(:parent)是一个漂亮的小技巧,可以在没有传递local的情况下避免NoMethodError,并且可以用于使相同的表单可重用于浅嵌套。
你也不应该在表单中执行@article.comments.build。示例化模型是控制器而不是视图的工作。表单也将始终绑定到一个新的示例,这意味着如果表单无效并且您正在断开表单进行编辑,则任何用户输入都将丢失!
在控制器端,有两种不同的方法来处理可以嵌套在多个不同资源中的资源。第一种方法是将所有内容路由到单个控制器中,并使用“param sniffing方法”:

class CommentsController < ApplicationController
  before_action :authenticate_user!
  before_action :set_parent
  
  # Creates articles nested in a Article or in another Comment
  # POST /articles/1/comments
  # POST /comments/2/comments
  def create
    @comment = @parent.comments.new(comment_params) do |c|
      c.user = current_user
    end
    # don't assume that saving will always be successful
    if @comment.save
      redirect_to @parent
    else
      render :new
    end
  end

  private

  def set_parent
    if params[:article_id].present?
      @parent = Article.find(params[:article_id])
    elsif params[:comment_id].present?
      @parent = Comment.find(params[:comment_id])
    end
  end

  def comment_params
    # you should not be permitting any of those params!
    params.require(:comment).permit(:body)
  end
end

虽然这种方法很流行,但我并不喜欢这种方法,因为随着复杂性的增加,它会变得非常丑陋。它也完全忽视了单一责任原则。例如,您可能希望根据父类型进行不同的重定向。
第二种方法是使用组合并创建单独的控制器:

resouces :articles do 
  resouces :comments, only: :create, module: :articles
end

resouces :comments, only: [] do 
  resouces :comments, only: :create, module: :comments
end
# Creates Comments on an Article 
class CommentsController < ApplicationController
  before_action :authenticate_user!
  before_action :set_parent
  
  # Creates comments nested in a parent record. 
  # This parent record should be set by subclasses. 
  def create
    @comment = @parent.comments.new(comment_params) do |c|
      c.user = current_user
    end
    # don't assume that saving will always be successful
    if @comment.save
      redirect_to @parent
    else
      render :new
    end
  end

  private

  def set_parent
    raise "This method must be implemented by subclasses!"
  end

  def comment_params
    # you should not be permitting any of those params!
    params.require(:comment).permit(:body)
  end
end
module Articles 
  # Creates Comments on an Article 
  class CommentsController < ::CommentsController 
    private

    def set_parent
      @parent = Article.find(params[:article_id])
    end
  end
end
module Comments 
  # Creates Comments nested in another Comment 
  class CommentsController < ::CommentsController 
    private

    def set_parent
      @parent = Comment.find(params[:comment_id])
    end
  end
end

相关问题