#198
Jan 25, 2010

Edit Multiple Individually

Use checkboxes to edit multiple records in one form, where each one has an individual set of form fields.
Tags: views forms
Download (33.6 MB, 13:53)
alternative download for iPod & Apple TV (19.8 MB, 13:53)

Resources

# products_controller.rb
def edit_individual
  @products = Product.find(params[:product_ids])
end

def update_individual
  @products = Product.update(params[:products].keys, params[:products].values).reject { |p| p.errors.empty? }
  if @products.empty?
    flash[:notice] = "Products updated"
    redirect_to products_url
  else
    render :action => "edit_individual"
  end
end

# routes.rb
map.resources :products, :collection => { :edit_individual => :post, :update_individual => :put }
<!-- views/products/index.html.erb -->
<% form_tag edit_individual_products_path do %>
  <table>
    <tr>
      <th></th>
      <th>Name</th>
      <th>Category</th>
      <th>Price</th>
    </tr>
  <% for product in @products %>
    <tr>
      <td><%= check_box_tag "product_ids[]", product.id %></td>
      <td><%=h product.name %></td>
      <td><%=h product.category.name %></td>
      <td><%= number_to_currency product.price %></td>
      <td><%= link_to "Edit", edit_product_path(product) %></td>
      <td><%= link_to "Destroy", product, :confirm => 'Are you sure?', :method => :delete %></td>
    </tr>
  <% end %>
  </table>
  <p>
    <%= select_tag :field, options_for_select([["All Fields", ""], ["Name", "name"], ["Price", "price"], ["Category", "category_id"], ["Discontinued", "discontinued"]]) %>
    <%= submit_tag "Edit Checked" %>
  </p>
<% end %>

<!-- views/products/edit_individual.html.erb -->
<% form_tag update_individual_products_path, :method => :put do %>
  <% for product in @products %>
    <% fields_for "products[]", product do |f| %>
      <h2><%=h product.name %></h2>
      <%= render "fields", :f => f %>
    <% end %>
  <% end %>
  <p><%= submit_tag "Submit" %></p>
<% end %>

<!-- views/products/_fields.html.erb -->
<%= f.error_messages :object_name => "product" %>
<% if params[:field].blank? || params[:field] == "name" %>
<p>
  <%= f.label :name %><br />
  <%= f.text_field :name %>
</p>
<% end %>
<% if params[:field].blank? || params[:field] == "price" %>
<p>
  <%= f.label :price %><br />
  <%= f.text_field :price %>
</p>
<% end %>
<% if params[:field].blank? || params[:field] == "category_id" %>
<p>
  <%= f.label :category_id %><br />
  <%= f.collection_select :category_id, Category.all, :id, :name %>
</p>
<% end %>
<% if params[:field].blank? || params[:field] == "discontinued" %>
<p>
  <%= f.check_box :discontinued %>
  <%= f.label :discontinued %>
</p>
<% end %>

RSS Feed for Episode Comments 39 comments

1. Joseph Silvashy Jan 25, 2010 at 00:06

This is awesome, I'd like to se this combined with Formtastic, thanks for the awesome screencast!


2. senthil Jan 25, 2010 at 00:24

+1 for combination with Formtastic.


3. Stingy Jan 25, 2010 at 00:34

i love formtastic. would be great if it is standard like ryans nifty generators.


4. Branden Jan 25, 2010 at 00:46

Thanks for this screencast Ryan. This definitely enhances the functionality of episode #165. These casts on dealing with multiple edits, models, nested_attributes etc are extremely helpful for my self.newbie to wrap my head around these things.

Much appreciated,

Branden


5. waqas Jan 25, 2010 at 01:59

This is xcellent screencast. Keep up the good work Ryan. I will be using this in my upcomgin RoR project.


6. Jamie, Baymard Institute Jan 25, 2010 at 04:13

Interesting as always..

Didn't know about the ClassName.update method.. been writing some excessive work-arounds to match this exact functionality (with validations).


7. Claudio Jan 25, 2010 at 05:41

Thanks for this screencast Ryan.


8. andresgutgon Jan 25, 2010 at 05:46

+1 for combination with Formtastic.

Thanks for your work!


9. Steve Jan 25, 2010 at 07:49

I love how you make the techniques in each screencast seem so simple, as in, "Why didn't I think of that!".

I think it inspires new guys like myself to sit back and look for a simple solution instead of over thinking a problem, which I tend to do all the time.

More times than not the solution is simple; it just takes a better understanding of the wonder that is rails!

Thanks for your work Ryan. I greatly appreciate it.


10. Zoga Jan 25, 2010 at 08:23

great episode, as usual :)
+1 for combination with Formtastic.


11. GGC Jan 25, 2010 at 10:37

That's a great episode. Thanks a lot Ryan. It's very useful and perfect to put on practice on every rails project or any project. I like Ruby more. Simple and elegant. Thanks again Ryan.


12. René Jan 25, 2010 at 11:13

Very nice episode, thanks a lot!


13. B Jan 25, 2010 at 11:42

You said the route would best be a GET and I agree; why not add :method => :get on the form to make it so?


14. Patrick Jan 25, 2010 at 15:41

Favorite episode to date! Thanks.


15. Thomas Jan 25, 2010 at 16:46

Ryan,

Love the screenscasts! Can I recommend you do one on an administrative tool like Typus (http://intraducibles.com/projects/typus) or something similar. I have used Typus for the first time recently and found it extremely helpful in getting up and running quickly. It does require some configuration of YAML files but you also have the ability to override pretty much everything.

Keep up the good work! Thanks.


16. javon Jan 25, 2010 at 22:23

Keep up the good work! Thanks.


17. javon Jan 25, 2010 at 22:24

Keep up the good work! Thanks.


18. Vincent Bray Jan 26, 2010 at 04:16

Awesome as ever. Perhaps someone would be kind enough to document f.error_messages in the API docs?


19. Hubert Łępicki Jan 26, 2010 at 08:23

I watched it yesterday and was thinking it's trival, useless and will never use it. Or not soon.

And now it looks like it's perfect solution for my today's problem at work. Man, how do you do it?


20. Judd Jan 26, 2010 at 11:34

Thanks Ryan! At times I feel like we work together. ;)


21. Nisse Jan 26, 2010 at 14:25

I cannot understand why you wouldn't do a GET request instead. I mean, sure it will be a long query-string but that also means you could bookmark edit pages. If you keep track of certain products and update them daily that would be excellent.

Or is it really necessary with POST like you said?


22. Martin Jan 28, 2010 at 04:08

One of my favorite episodes.


23. pduersteler Jan 28, 2010 at 08:10

also +1 for formtastic


24. CCNA Jan 28, 2010 at 19:21

Keep up the good work! Thanks.


25. Zoga Jan 29, 2010 at 16:04

Would be interesting to combine webforms2 with this. Following example is screaming at this screencast :)

http://webforms2.googlecode.com/svn/trunk/testsuite/003.html


26. davis Feb 05, 2010 at 20:11

Hmm, this line in the controller feels a little risky:

Product.update(params[:products].keys, params[:products].values)

"params" is a Hash (actually a subclass of Hash, HashWithIndifferentAccess), and its "keys" and "values" methods return arrays in no particular order. It's probably not a good idea to assume they'll pair up properly every time. Even if it works in practice, it probably exploits an undocumented fact about the Hash implementation.

Something like the following might be more bulletproof. It removes the assumptions but does basically the same thing:

keys = params[:products].keys
values = keys.map {|k| params[:products][k]}

Aside from that, thanks for all the helpful 'casts.


27. buy terracotta warriors Feb 08, 2010 at 00:00

I watched it yesterday and was thinking it's trival, useless and will never use it. Or not soon.


28. paese Feb 11, 2010 at 05:39

very nice one, thanks!

+1 for combination with formtastic
+1 for usually using formtastic in railscasts ;-)


29. Mike_S Feb 14, 2010 at 08:56

Ryan,

I haven't tried this yet, but is this the solution to completing the "survey series" (#196, #197) ?

Or do you have something else ?

If so, I'd love to see a final episode to complete the survey app, i.e. where a user can answer the questions on the survey.

Mike


30. Mike_S Feb 15, 2010 at 12:15

Ryan,

If you click 'Edit Checked' without anything checked you get a walkback:

Couldn't find Product without an ID

How do you trap that error / post an error message?

Mike


31. MIke_s Feb 15, 2010 at 13:27

Ryan,

I tried the following code, the flash gets displayed, but the 'Product.find' is still getting executed resulting in the error :
"Couldn't find Product without an ID"

begin
rescue params[:product_ids].empty?
  logger.error("No products selected for 'Edit Selected'" )
    flash[:notice] = "Nothing selected for 'Edit Selected' action"

redirect_to products_path
end

@products = Product.find(params[:product_ids])


32. David Feb 21, 2010 at 12:29

Awesome! Very handy!


33. mwicks1968 Feb 26, 2010 at 03:52

Ryan - Great Cast once again!

A Question: How would you implement a "mass edit" facility?

What I mean to say is: say you had an attribute which you want to set to the same thing for ALL rows selected on the front form. How do you do this with a single control?

Cheers


34. mwicks1968 Mar 03, 2010 at 04:15

In reply to myself, I checked out Episode 165

Result!


35. mwicks1968 Mar 03, 2010 at 07:52

Another question:

How do you implement a check box that checks all the check boxes (if you know what I mean!)?

Cheers


37. Ohad Mar 14, 2010 at 23:51

how can I use nested_attributes with this approch? e.g. http://stackoverflow.com/questions/2445097/accepts-nested-attributes-with-model-update-for-multiple-models


38. jhj1989 Mar 17, 2010 at 01:30

ghd iv styler hair straightener is used to straighten hair, high-temperature electronics. The main body of the temperature through a heat conduction to a metal plate or ceramic plate surface, the surface temperature is set at 200 degrees, through which heat up 200 degrees of the splint to straighten the hair.


39. yyl Mar 17, 2010 at 01:51

The new GHD.IV Styler is one of many hair care products in the most popular, which makes it easy for colleges and universities has straight hair. So you can always have a beautiful flowing straight hair, the use of imported PTC heating elements, fast heat up 180 degrees temperature does not harm hair, smooth ceramicsurface, straightened hair


40. debt settlement Mar 18, 2010 at 21:36

Thanks for providing this great post.

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player
Give Back to Open Source