【问题标题】:Validate form called through another controller验证通过另一个控制器调用的表单
【发布时间】:2012-02-21 17:12:21
【问题描述】:

我正在编写一个博客引擎,所以我有一个帖子视图 (show.html.erb),它显示了一个帖子、cmets 列表和一个 cmets 表单。

我这样称呼表格:

<%= render :partial => 'comments/form', :locals => {:comment => @post.comments.new} %>

我不确定以这种方式传递评论是否正确,但至少我得到了新评论的 post_id。

这是我的表单(cmets 视图上的 _form.html.erb):

<%= form_for comment, :action => "create" do |f| %>
    <% if comment.errors.any? %>
        <div id="error_explanation">
          <h2><%= pluralize(comment.errors.count, "error") %> prohibited this comment from being added:</h2>

          <ul>
            <% comment.errors.full_messages.each do |msg| %>
                <li><%= msg %></li>
            <% end %>
          </ul>
        </div>
    <% end %>

  <%= f.hidden_field :post_id %>
  <p>
    <%= f.label :name %>
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.label :email %>
    <%= f.text_field :email %>
  </p>
  <p>
    <%= f.label :url %>
    <%= f.text_field :url %>
  </p>
  <p>
    <%= f.label :content %>
    <%= f.text_area :content %>
  </p>
  <p class="no-border">
    <%= f.submit "Post", :class => "button" %>
  </p>
<% end %>

这个动作:

  def create
    @comment = Comment.new(params[:comment])

    respond_to do |format|
      if @comment.save
        format.html { redirect_to :back }
      else
        format.html { redirect_to :back }
      end
    end
  end

有点冗长,但我想添加更多内容。

我可以完美地添加 cmets,但我无法查看验证错误。

我将所有内容留空(我的模型有验证内容),我看到在 create 操作中创建的评论有错误并转到 else 路径。

但是...表单没有显示任何错误。

我认为我有一个带有错误的对象,但是当我重定向回来时,我传递给表单的对象又是一个新对象并且没有错误。

那么,问题出在哪里?

编辑:额外的东西:

我的 show.html.erb 我也有这个(在表格之前):

<ol class="commentlist">
  <%= render @post.comments %>
</ol>

所以,当在 show 动作中,我放了额外的变量:

def show
  @post = Post.find(params[:id])
  @comment = @post.comments.new

看来渲染器也想渲染空注释,并抛出异常。

如何绕过?

【问题讨论】:

    标签: ruby-on-rails forms validation


    【解决方案1】:

    当您重定向回来时,您将再次调用PostsController#show 操作,这将重置所有实例变量。如果您想在 CommentsController#create 调用失败后保存状态,则需要调用 render 'posts/show' 而不是 redirect :back,这将重用在当前操作中声明的实例变量

    def create
       # assuming you have nested the comments routes underneath posts...
       @post = Post.find(params[:post_id])
       @comment = @post.comments.build(params[:comment])
    
       respond_to do |format|
          if @comment.save
             format.html { redirect_to :back }
          else
             format.html do
                # remember to declare any instance variables that PostsController#show requires
                # e.g. @post = ...
                render 'posts/show'
             end
          end
       end
    end
    

    您还需要确保部分使用 @comment 而不是每次都创建新评论

    <%= render :partial => 'comments/form', :locals => {:comment => @comment } %>
    

    并确保PostsController 声明@comment

    # e.g. inside PostsController
    def show
       @post = Post.find(params[:id])
       @comment = Comment.new
    end
    

    要记住的最重要的事情是确保失败的create 调用中的代码初始化了PostsController#show 操作模板所需的所有实例变量,否则你会得到错误。

    【讨论】:

    • 很棒的答案。我还有一个问题。如果我需要在 create 方法中创建 @post,我需要 post id。该 id 是否在创建请求中的某处?
    • 假设您已经嵌套了路由,那么您应该可以通过params[:post_id] 访问它。检查rake routes 以确保您的评论路径嵌套在帖子下方,例如posts/1/comments
    • 不,我没有,这是我的第一个项目。但我会对此进行研究。 Otoh,我更新了这个问题,因为我之前忘记评论你的解决方案有一个额外的问题。
    • 我嵌套了路线,但仍然有显示问题(请参阅我更新的问题)。该列表将呈现我当前的 cmets,并将尝试呈现新评论或带有验证错误的评论。我想我需要尝试另一种方法来呈现我的评论列表。
    • 好吧,在 show 上创建一个新评论并手动为其分配帖子 ID 并在 create 中从 params(而不是 build)创建一个新评论似乎可行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-11
    • 1970-01-01
    • 2015-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多