Multiple forms for the same model in a single page

10,511

Solution 1

If you don't want to change your input names or your model structure, you can use the id option to make your form ID unique and the namespace option to make your input IDs unique:

<%= form_for Feedback.new(...), 
    id: "annotation_#{annotation.id}_feedback"
    namespace: "annotation_#{annotation.id}" do |f| %>

That way our form ID is unique, i.e. annotation_2_feedback and this will also add a prefix, e.g. annotation_2_, to every input created through f.

Solution 2

Did you consider nested_attributes for rails models? Instead of having multiple new feedback forms where each is tied to an annotation, you could have multiple edit annotation forms where each annotation includes fields for a new feedback. The id's of the generated forms would include the annotations id such as edit_annotation_16.

The annotation model would have a relationship to its feedbacks and will also accept nested attributes for them.

class Annotation < ActiveRecord::Base
  has_many :feedbacks
  accepts_nested_attributes_for :feedbacks
end

class Feedback < ActiveRecord::Base
  belongs_to :annotation
end

You could then add as many forms as you want, one for each annotation. For example, this is what I tried:

<% form_for @a do |form| %>
    Lyrics: <br />
    <%= form.text_field :lyrics %><br />
    <% form.fields_for :feedbacks do |feedback| %>
        Feedback: <br/>
        <%= feedback.text_field :response %><br />
    <% end %>
    <%= form.submit "Submit" %>
<% end %>

<% form_for @b do |form| %>
    Lyrics: <br />
    <%= form.text_field :lyrics %><br />
    <% form.fields_for :feedbacks do |feedback| %>
        Feedback: <br/>
        <%= feedback.text_field :response %><br />
    <% end %>
    <%= form.submit "Submit" %>
<% end %>

And the quick and dirty controller for the above edit view:

class AnnotationsController < ApplicationController
  def edit
    @a = Annotation.find(1)
    @a.feedbacks.build
    @b = Annotation.find(2)
    @b.feedbacks.build
  end

  def update
    @annotation = Annotation.find(params[:id])
    @annotation.update_attributes(params[:annotation])
    @annotation.save!
    render :index
  end
end

Solution 3

I had this same issue on a site I'm currently working on and went with the solution you mention at the bottom. It's not repetitive if you generate the ID programmatically and put the whole form in a partial. For example, on my site, I have multiple "entries" per page, each of which has two voting forms, one to vote up and one to vote down. The record ID for each entry is appended to the DOM ID of its vote forms to make it unique, like so (just shows the vote up button, the vote down button is similar):

<% form_for [entry, Vote.new], :html => { :id => 'new_up_vote_' + entry.id.to_s } do |f| -%>
  <%= f.hidden_field :up_vote, :value => 1, :id => 'vote_up_vote_' + entry.id.to_s %>
  <%= image_submit_tag('/images/icon_vote_up.png', :id => 'vote_up_vote_submit' + entry.id.to_s, :class => 'vote-button vote-up-button') %>
<% end -%>

Solution 4

I also had the same issue but wanted a more extensible solution than adding the ID to each field. Since we're already using the form_for ... |f| notation the trick is to change the name of the model and you get a new HTML ID prefix.

Using a variant of this method: http://www.dzone.com/snippets/create-classes-runtime (I removed the &block stuff)

I create a new model that is an exact copy of the model I want a second form for on the same page. Then use that new model to render the new form.

If the first form is using

@address = Address.new

then

create_class('AddressNew', Address)
@address_new = AddressNew.new

Your ID prefix will be 'address_new_' instead of 'address_' for the second form of the same model. When you read the form params you can create an Address model to put the values into.

Solution 5

For those stumbling here, looking for the solution for Rails 3.2 app, look at this question instead:

Rails: Using form_for multiple times (DOM ids)

Share:
10,511
Tom Lehman
Author by

Tom Lehman

Creator of Genius

Updated on June 21, 2022

Comments

  • Tom Lehman
    Tom Lehman almost 2 years

    On the front page of my rap lyrics explanation site, there's a place where users can try explaining a challenging line:

    alt text http://dl.dropbox.com/u/2792776/screenshots/2010-02-06_1620.png

    Here's the partial I use to generate this:

    <div class="stand_alone annotation" data-id="<%= annotation.id %>">
      <%= song_link(annotation.song, :class => :title) %>
    
      <span class="needs_exegesis"><%= annotation.referent.strip.gsub(/\n/, "\n <br />") %></span>
    
      <% form_for Feedback.new(:annotation_id => annotation.id, :created_by_id => current_user.try(:id), :email_address => current_user.try(:email)), :url => feedback_index_path, :live_validations => true do |f| %>
        <%= f.hidden_field :annotation_id %>
        <%= f.hidden_field :created_by_id %>
        <p style="margin-top: 1em">
            <%= f.text_area :body, :rows => 4, :style => 'width:96%', :example_text => "Enter your explanation" %>
        </p>
        <p>
          <% if current_user %>
            <%= f.hidden_field :email_address %>
          <% else %>
            <%= f.text_field :email_address, :example_text => "Your email address" %>
          <% end %>
          <%= f.submit "Submit", :class => :button, :style => 'margin-left: .1em;' %>
        </p>
      <% end %>
    </div>
    

    However, putting more than one of these on a single page is problematic because Rails automatically gives each form an ID of new_feedback, and each field an ID like feedback_body (leading to name collisions)

    Obviously I could add something like :id => '' to the form and all its fields, but this seems a tad repetitive. What's the best way to do this?