views:

38

answers:

2

Hello, I have two models, Product and ProductImage, and I'd like to offer the possibility to add up to 6 images to each product.

Product

has_many :product_images, :dependent => :destroy

while ProductImage

belongs_to :product

So far, my views are the following:

#products/_form.html.erb.
    <% @product.product_images.build %>
    <%= form_for(@product, :html => { :multipart => true }) do |product_form| %>
      <% if @product.errors.any? %>
        <div id="error_explanation">
          <h2><%= pluralize(@product.errors.count, "error") %> prohibited this product from being saved:</h2>

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

      <div class="field">
        <%= product_form.label :title %><br />
        <%= product_form.text_field :title %>
      </div>
      <div class="field">
        <%= product_form.label :description %><br />
        <%= product_form.text_area :description %>
      </div>
      <div class="field">
        <%= product_form.label :price %><br />
        <%= product_form.text_field :price %>
      </div>
      <div id="product_images">
          <%= render :partial => 'product_images/form', :locals => {:form => product_form} %> 
        </div>
      <div class="actions">
        <%= product_form.submit %>
      </div>
    <% end %>

and

#product_images/_form.html.erb
    <%= form.fields_for :product_images do |product_image_form| %>
        <div class="image">
            <%= product_image_form.label :image, 'Image:' %>
            <%= product_image_form.file_field :image %>
        </div>
        <% unless product_image_form.object.nil? || product_image_form.object.new_record? %>
        <div class="image">
            <%= product_image_form.label :_destroy, 'Remove:' %>
            <%= product_image_form.check_box :_destroy %>
        </div>
        <% end %>
    <% end %> 

The question is: how do I force my partial form to print out 6 different file_fields, each with its own unique attribute name, e.g.:

name="product[product_images_attributes][0][image]"
name="product[product_images_attributes][1][image]"

and so on?

I mean, is that even possible or there is a better and different way to achieve such result?

I was thinking I could add as many fields with link_to and AJAX but I guess it's a lot easier for the user to have the six fields printed out and ready, instead of clicking a link each time in order to add a field.

+2  A: 

Assuming you use accepts_nested_attributes_for :product_images in your Product model, you need something like the following in your ProdcutsController:

def new
  @product = Product.new
  6.times { @product.product_images.build } # this will create 6 "blank product_images"
end

def edit
  @product = Product.find(params[:id]).includes(:product_images)
  (6 - @product.product_images.size).times { @product.product_images.build } # this will create as many "blanks" as needed
end

For new products, you build 6 new product images, for existing products you build only as many as you need in order to have 6.

Edit: Also remove the <% @product.product_images.build %> line from your products/_form.html.erb view since you are building the associated instances in your controller instead.

Teoulas
That works perfectly. Many, many thanks, Teoulas.
Neko
Teoulas, do you know where this sort of thing is documented? I really want to read more on how nested attributes are handled, but the documentations are really sparse on this subject.
vinhboy
You could always look at the code, if you want to know more about the internals of nested attributes. The concept is pretty simple, it just sets up some getter/setter methods for the associated model attributes. Here's the official documentation:http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Teoulas
A: 

Is there a way to make it with Ajax instead? i checked Railscasts version, works perfect on 2.3.X, but no luck with rails3, maybe i m missing something (. is there a modern solution, rails.js preferable? thanks in advance

sin
Sorry, mine is a useless answer, but I just wanted to confirm that the Railscast tutorials don't work with Rails 3. I always got a few errors regarding form.fields_for and the link_to_function (which I guess changed into link_to, in Rails 3). I too would be interested in a tutorial/example that addresses these issues.
Neko