3

I have a tree of products in Rails 3.2 and would like to have an add/remove feature so that users can add new children products or remove existing children for a product. I use the ancestry gem to generate the tree, for a Product 1 it might look like this:

Product 1
add | remove
    Product 2
    add | remove
        Product 3
        add | remove

In a partial _product.html.erb I have added Add and Remove links, which works for the add feature, but I cannot get the remove link to work:

<span><%= product.name.capitalize %></span>
<%= link_to "Add", new_product_path(parent_id: product) %> | 
<%= link_to "Remove", {parent_id: nil}, method: :update %>

I would like to update the parent_id to nil for the product to be removed when "Remove" is clicked, but the above link_to doesn't seem to be working. I get an: No route matches [POST] "/products/1/edit" routing error. In my product_controller I have:

def update
   if @product.update_attributes(params[:product])
     flash[:success] = "Product updated"
     redirect_to @product
   else
     render 'edit'
  end
end

What am I doing wrong?

Edit:

I tried to use method: put instead:

<%= link_to "Remove", {parent_id: nil}, method: :put %>

then I get a No route matches [PUT] "/products/1/edit" error instead when clicking on the link.

I can now change/remove the parent using a form, not at all what I want but anyway:

<%= form_for @product do |f| %>    
    <%= f.label :parent_id %>
    <%= f.text_field :parent_id %>
    <%= f.submit "Update", class: "btn" %>  
<% end %>

Would it be possible to automatically pass the parent_id: nil into the form so that when you hit Update, it sets the parent_id: nil (not having a text field just a button)?

graphmeter
  • 1,125
  • 1
  • 9
  • 24
  • What does your `config/routes.rb` contain for products? – Jason Noble Dec 03 '12 at 16:26
  • My routes.rb have this for the products: resources :products, only: [:edit, :update, :show, :index, :create, :destroy, :new] – graphmeter Dec 03 '12 at 16:32
  • 1
    you could `<%= f.hidden_field :parent_id, nil %>` (btw you can still have the link if you want to - you have to explicitly point it to the url for update (/products/1 in your case) - `rake routes` is your friend) – froderik Dec 03 '12 at 20:02
  • @froderik: I have tried `:parent_id, nil`, but then I get an `undefined method `merge' for nil:NilClass`error? I have also tried using hidden_field_tag :parent_id, nil, which doesn't throw an error but the field is not updated... – graphmeter Dec 04 '12 at 08:41
  • 1
    if you use hidden_field_tag you'll have to pick up :parent_id 'by hand' in your controller: `@product.parent_id = params[:parent_id]` – froderik Dec 04 '12 at 14:54
  • @froderik: Thanks, that is very useful to know. I will try picking it up in the controller. – graphmeter Dec 04 '12 at 14:57
  • 1
    @froderik: works as well, thanks! – graphmeter Dec 04 '12 at 15:09

2 Answers2

7

Try

<%= link_to "Remove", update_products_path(product:{parent_id: nil}), method: :put %>

There is no HTTP-Verb update what you need is put. You can read about Rails and HTTP-Verbs here

krichard
  • 3,699
  • 24
  • 34
  • Tried with put, get an error: `No route matches [PUT] "/products/1/edit"` – graphmeter Dec 03 '12 at 16:12
  • thats because your code points to `edit` which wont respond to `put` – krichard Dec 03 '12 at 16:28
  • i guess that is because I do this under the edit page for the product, should it point to the product instead? – graphmeter Dec 03 '12 at 16:31
  • 2
    Think of it this way: `new` uses GET to present an empty form that you use to `create` and object using POST; `edit` uses GET to present a form filled with the current state of data so that you use to `update` the object using PUT. `new` and `edit` display data, `create` and `update` save it. Displaying always uses the HTTP GET verb, create uses POST and update uses PUT. – Tom Harrison Dec 03 '12 at 16:57
  • @TomHarrisonJr: Thanks! That makes it more clear. I could use a form for updating the parent_id, I tried this for other parameters such as name of product and so on, and it seems to work. However, a link to change the parent_id is more neat. – graphmeter Dec 03 '12 at 17:05
  • How would the helper function above, update_products_path, look like that takes the argument `product:{parent_id: nil}`, and where the path is `/products/id`? I tried to write one, but without success. – graphmeter Dec 04 '12 at 16:54
2

I finally got it to work thanks to @krichard answer together with @froderiks comment, by setting the path to /products/id and passing parent_id: nil using method: :put:

<%= link_to "Remove", "/products/#{product.id}?product%5Bparent_id%5D=", method: :put %>

The same effect can be achieved using form_for and a hidden_field (Passing a fixed value to a field using a button/link):

<%= form_for product do |f| %>
  <%= f.hidden_field :parent_id, value: nil %>
  <%= f.submit "Remove", class: "btn" %>
<% end %>

If a hidden_field_tag is used instead of f.hidden_field, :parent_id must be picked up in the controller (thanks @froderik):

<%= form_for product do |f| %>
  <%= hidden_field_tag :parent_id, nil %>
  <%= f.submit "Remove", class: "btn" %>
<% end %>

products_controller.rb

def update
  @product.parent_id = params[:parent_id]
  ...
end
Community
  • 1
  • 1
graphmeter
  • 1,125
  • 1
  • 9
  • 24