form_with不会影响控制器的动作

时间:2018-10-05 15:35:06

标签: ruby-on-rails ruby-on-rails-5.1

我同时尝试了form_tagform_with-结果是相同的,控制器的动作永远不会触发。

# routes.rb
resources :products do
  member do
    patch :add_comment
  end
end

# products_controller.rb
def add_comment
  # !!! damn form_with never gets here!!!
  product.add_comment!(params[:comment_id])
  redirect_back(fallback_location: products_path)
end

# view
<%= form_with(url: add_comment_product_path, local: true) do |form| %>
  <%= form.text_field :comment_id %>
  <%= form.submit 'Add comment' %>
<% end %>

实际日志:

Started PATCH "/products/1"
Processing by ProductsController#update as HTML
Parameters: {
  "utf8"=>"✓",
  "authenticity_token"=>"token",
  "products"=>{a_lot: :of_stuff},
  "comment_id"=>"2",
  "commit"=>"Add comment",
  "id"=>"1"
}

预期的日志:

Started PATCH "/products/1/add_comment?comment_id=2"
Processing by ProductsController#add_comment as HTML
Parameters: {
  "utf8"=>"✓",
  "authenticity_token"=>"token",
  "comment_id"=>"2",
  "id"=>"1"
}

编辑:

我认为这与form_with嵌套成更大的形式有关,并且当我点击Add comment时看起来会触发外部提交

4 个答案:

答案 0 :(得分:1)

Rails处理此问题的方法是将其作为单独的但嵌套的资源-因为您实际上是在创建新的资源(注释),而不修改产品本身。

这还使您的代码符合“单一职责原则”(SRP),因为每个控制器仅处理CRUD的一种资源。

您可以通过将对resources的调用嵌套来nest resources

resources :products do
  resources :comments, shallow: true
end

然后设置一个CommentController来处理CRUD注释:

class CommentsController < ApplicationController
  before_action :set_comment, only: [:index, :new, :create]

  # GET /products/:product_id/comments
  def index
    @comments = @product.comments
  end

  # GET /products/:product_id/comments/new
  def new
    @comment = @product.comments.new
  end

  # POST /products/:product_id/comments
  def create
    @comment = @product.comments.new(comment_params)
    if @comment.save
      redirect_to @product, success: 'Comment created'
    else
      render :new
    end
  end

  # ...

  private
  def set_product
    @product = Product.find(params[:product_id])
  end

  def comment_params
    params.require(:comment)
          .permit(:foo, :bar)
  end
end

要将表单操作属性设置为指向嵌套路由,只需使用数组或命名为product_comments(product_id: @product.to_param)的路由帮助器即可。

<%= form_with(model: @comment, url: [@comment.product, @comment], local: true) do |form| %>
  <%= form.submit 'Add comment' %>
<% end %>

由于产品ID是通过URI传递的,因此无需通过隐藏的输入传递它。

  

我认为这与form_with是   嵌套成更大的形式,当我点击添加评论时看起来   触发外部提交

您应注意,HTML标准(HTML5和更旧的(x)HTML标准)均不允许使用嵌套表单元素,并且如果浏览器应使用嵌套表单的action属性,则行为可能非常不可预测,因为未指定行为或将事件冒泡到您的案例中最有可能发生的父表单元素。参见:http://w3.org/TR/html5/forms.html

答案 1 :(得分:0)

add_comment_product PATCH  /products/:id/add_comment(.:format)    products#add_comment

您已将其声明为成员路线,但我看不到您正在为:id路径帮助程序传递任何值。

尝试将其更改为

<%= form_with(url: add_comment_product_path(product), local: true) do |form| %>

其中productProduct实例。

答案 2 :(得分:0)

我只需要将嵌套表单移出更大的表单即可使用。

答案 3 :(得分:-1)

请尝试一下-

# products_controller.rb
def add_comment
  # You need add permitted for get parameters
  params.permit(:comment_id)
  product.add_comment!(params[:comment_id])
  redirect_back(fallback_location: products_path)
end

# You can place this form anywhere in your application, but you need to specify product object and comment_id
<%- @product = Product.find(1) %>
<%= form_with(url: add_comment_product_path(@product, comment_id: 2), local: true, method: :patch) do |form| %>
  <%= form.text_field :comment_id %>
  <%= form.submit 'Add comment' %>
<% end %>

Started PATCH "/products/1/add_comment?comment_id=2" for 127.0.0.1 at 2018-10-05 22:01:37 +0600
Processing by ProductsController#add_comment as HTML

Parameters: {"utf8"=>"✓", "authenticity_token"=>"token", "comment_id"=>"2", "commit"=>"Add comment", "id"=>"1"}