Reputation: 52357
I tried both form_tag
and form_with
- the result is the same, controller's action never gets triggered.
# 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 %>
Actual logs:
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"
}
Expected logs:
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"
}
Edit:
I think it has something to do with the fact that this form_with
is nested into bigger form and it looks when I hit Add comment
it triggers the outer submit
Upvotes: 3
Views: 4177
Reputation: 52357
I simply had to move the nested form out of the bigger form to make it work.
Upvotes: 0
Reputation: 102218
The Rails way to handle this would be as a seperate but nested resource - as you´re really creating a new resource (a comment) and not modifying the product itself.
This also keeps your code in line with the Single Responsibility Principle (SRP) as each controller only handles CRUD'ing a single type of resource.
You can nest resources by nesting the calls to resources
:
resources :products do
resources :comments, shallow: true
end
Then setup a CommentsController to handle CRUD'ing comments:
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
To set the form action attribute to point to a nested route you simply use an array or the named product_comments(product_id: @product.to_param)
route helper.
<%= form_with(model: @comment, url: [@comment.product, @comment], local: true) do |form| %>
<%= form.submit 'Add comment' %>
<% end %>
As the product id is passed through the URI there is no need to pass it via a hidden input.
I think it has something to do with the fact that this form_with is nested into bigger form and it looks when I hit Add comment it triggers the outer submit
You should note that the HTML standards (both HTML5 and older (x)HTML standards) do not allow nested form elements and the behaviour can be very unpredictable as its not specified if the browser should use the action attribute of the nested form or bubble the event to the parent form element which is most likely happening in your case. see: http://w3.org/TR/html5/forms.html
Upvotes: 2
Reputation: 638
Yon try this -
# 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"}
Upvotes: -1
Reputation: 33542
add_comment_product PATCH /products/:id/add_comment(.:format) products#add_comment
You have declared it as a member route, but I don't see you are passing any value for the :id
the path helper.
Try changing it to
<%= form_with(url: add_comment_product_path(product), local: true) do |form| %>
where product
is the Product
instance.
Upvotes: 0