Multiple forms for the same model in a single page - ruby-on-rails

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?

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.

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

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 -%>

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.

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)

Related

Rails 4 : Checkbox array is not updating my attribute

My starting place was this discussion: Syntax for form_for when building an array from checkboxes
I have a call to my model passing back an array of valid options. This array then makes a series of check_box_tag
<%= form_for #game, :url => wizard_path do |f| %>
<div>
<% #game.select_races.each do |a| %>
<%= f.label a %>
<%= check_box_tag 'game[races][]', a , true %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
This successfully creates an array called 'races' containing the desired output. The problem is that it doesn't actually update the races attribute. So my races attribute is still nil.
I'm sure this is a painful Rails beginner question. Any help is appreciated.
UPDATE
My allowed params were:
def game_params
params.require(:game).permit(:shattered_empire, :shards_of_the_throne, :number_of_players, :rules, :strategy_cards, :players, :races)
end
Which needed to be updated to:
def game_params
params.require(:game).permit(:shattered_empire, :shards_of_the_throne, :number_of_players, :rules, :strategy_cards, :players, {:races => []})
end

Rails 4: text_field for acts_as_taggable_on not separating tags with a comma

I am trying to get the text_field in my form partial to comma-separate acts_as_taggable_on tags. Right now, when I reload the page, the commas disappear so if a field has two or more tags, they become one big tag instead. For instance, I get "Tag1 Tag2 Tag3" instead of "Tag1, Tag2, Tag3". I am using acts-as-taggable-on 3.4.2.
Here is my _form.html.erb partial:
<h2>Tags:</h2>
<p>Please separate the tags with a comma ','</p>
<% #article.tag_types.each do |tag| %>
<div class="form-group">
<strong><%= label_tag tag.to_s.titleize %></strong><br />
<%= f.text_field "#{tag.to_s.singularize}_list".to_sym, :placeholder => "Comma-separated list of #{tag.to_s}", class: 'form-control' %>
</div>
<% end %>
Every time I reload the edit page, the input value somehow removes the commas from the already-present tags, so the text field looks like this:
<input id="article_country_list" class="form-control" type="text" name="article[country_list]" value="China U.S.A." placeholder="Comma-separated list of countries">
instead of having value="China, U.S.A." as it should be.
Here is my model, article.rb:
class Article < ActiveRecord::Base
acts_as_taggable_on :people, :cities, :countries, :other
end
Any help would be much appreciated :)
Thanks!
Apparently this is a new security feature.
I solved the comma separation issue by doing:
<% #article.tag_types.each do |tag| %>
<div class="form-group">
<strong><%= f.label tag.to_s.titleize %></strong><br />
<% tag_sym = "#{tag.to_s.singularize}_list".to_sym %>
<% tag_list = "#{tag.to_s.singularize}_list" %>
<%= f.text_field tag_sym, value: #article.send(tag_list).to_s, :placeholder => "Comma-separated list of #{tag.to_s}", class: 'form-control' %>
</div>
<% end %>
Thanks! Since I am using ActiveAdmin with Formtastic I made a custom input.
So I created a new class: app/inputs/tag_list_input.rb with:
class TagListInput < Formtastic::Inputs::StringInput
def input_html_options
super.merge(:value => "#{#object.send(method).to_s.html_safe}")
end
end
and using this like:
f.input :some_tag_list, :as => :tag_list, :label => "SomeTags"

rails 3 has_many :through Form with checkboxes

Asked similar before.
Rails 3 has_many :through Form
But can't get the relationship with employmentships to be created from the users form.
Have read http://www.justinball.com/2008/07/03/checkbox-list-in-ruby-on-rails-using-habtm/
and http://millarian.com/programming/ruby-on-rails/quick-tip-has_many-through-checkboxes/ (which I was really hoping that it worked.)
Form submits, but only creates a blank record in employmentship.
<%= form_for #user do |f| %>
...
<p>
<% Company.all.each do |company| %>
<%= check_box_tag :company_ids, company.id, #user.companies.include?(company), :name => 'user[company_ids][]' -%>
<%= label_tag :companies_ids, company.id %>
<% end %>
</p>
<p><%= f.submit %></p>
<% end %>
Include a hidden field tag in the form to make sure something gets submitted when none of the check boxes are selected. This should work, after the <%end%>:
<%= hidden_field_tag "user[company_ids][]" %>
I may be wrong, but I think that the first arg of the check_box_tag function is the actual name of the input, so instead of
check_box_tag :company_ids, company.id, #user.companies.include?(company), :name => 'user[company_ids]'
you could try something like
check_box_tag 'user[company_ids]', company.id, #user.company_ids.include?(company.id)
Let me know if it works!

Rails 3 - Nested Forms, using Builder

I have the following:
My Controller:
def new
.
.
#teammembers.each do |teammember|
request = #request_thread.requests.build(:recipient_id => teammember.user_id, :full_name => 'Billy Bob T')
end
My View:
<%= f.fields_for :requests do |builder| %>
<div class="field">
<%= builder.label :full_name %>
<%= builder.check_box :recipient_id, :checked => false %>
</div>
<% end %>
The nested form for request only holds the user_id, not the user.name... Problem is in the nested form, I need to show the user.name next to the check_box. So I tried adding a virtual attribute in the model (attr_accessor :full_name), so I could use full_name but I can't see to access that in the nested form (inside builder).
Any tips or suggestions from the pros?
Thanks
What about:
User.find(:recipient_id).name
EDIT: I hope i understood your question ..

Transferring variables between models using a view

I am creating a very simple book review site and it needs the ability to allow the user to add little comments about a book. Now I have my two tables, one for the book and one for the comments and now need a way to transfer data between the two because i find the way rails handles things quite puzzling.
So my book model contains "has_many :comments"
and me comment model has: "belongs_to :book"
the view i am using to both view and add comments is "/views/book/viewbook.html.erb"
this shows the book and all its details, at the bottom is a section where the user can add their own comments, it looks like this:
<%= form.text_field :title %>
<%= form.text_area :body %>
<%= submit_tag "Add Comment", :class => "submit" %>
now i know this cannot work because that above ":title" and ":body" would be in the book model but i need them to be send to the comment model because these are in the comment DB. How do i pass this data to the comment database. I've tried ":comment.title" and other various things but still cannot workout how to pass this data.
Any thoughts would be greatly appreciated.
(I apologize if this question is very stupid or has not been explained to well, my lecturer set this assignment and rails is not a language i have ever used.)
You define what the form is for in the opening form tag:
<% form_for :comment do |form| %>
<%= form.text_field :title %>
<%= form.text_area :body %>
<%= submit_tag "Add Comment", :class => "submit" %>
<%= end %>
The idea is that the form is an empty comment object. Controllers communicate between models and views, so your controller should have an action to process the form that knows to save the comment object into the comment model. You'll also want to specify which book the comment is for. There are a lot of ways to handle this (hidden fields, nested RESTful resources, etc.).
Maybe start here: http://guides.rubyonrails.org/action_controller_overview.html
It sounds like you need to use nested object forms. This is a new feature in Rails 2.3:
http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes
So i have modified it using your great suggestions and now only one problem has come-up. Whilst i can now successfully store the comments title, body, time and the person who left the comment to the DB i still cant work out how to use store the book.id. Here is the view:
<% form_for :comment, :url => { :action => "addcomment" } do |form| %>
<%= form.hidden_field :user_id, :value => session[:user_id] %>
<%= form.hidden_field :book_id, :value => #book.id %> <!-- WONT WORK -->
<%= form.label "Title" %><%= form.text_field :title %><br />
<%= form.label "Comment" %><%= form.text_area :comment %>
<%= submit_tag "addComment", :class => "submit" %>
<% end %>
Here is my controller that can now successfully store the details, apart from the book.id
def addcomment
#newcomment = Comment.new(params[:comment])
if #newcomment.save
flash[:notice] = "Comment Was Added To The System."
redirect_to :action => "index"
end
end
i though that "#book.id" would work because in that view i am also showing the books details using things like "#book.title" and "#book.authour" and that seems to work, but not for the ID field though.
So can now successfully post comments and store them with the correct details, now im on to displaying a list of comments of that particular book using the "book_id" value. Here is what i thought would work, i also have code like this in the search part of my app so i thought it would well:
def view
#book = Book.find(params[:id])
#reviews = Comment.find_by_book_id(#book.id)
end
With the corresponding view:
<% if #reviews %>
<% for review in #reviews %>
<%= form.label "Title: " %><%h review.title %> <br />
<%= form.label "Review:" %><%h review.comment %>
<% end %>
<% end %>
Now that should get the comments that have the "book_id" of the book i am viewing and the display each one using the for loop. Doesnt quite work though, it spits out an error message saying the following:
#undefined method `each' for #<Comment:0xb682c2f4>
#Extracted source (around line #27)
And line 27 is
<% for review in #reviews %>
To find the comments for a book it's just:
#book = Book.find(params[:id])
like you've already done, then:
#book.comments
for that books comments. So:
<% for review in #book.comments %
<%= form.label "Title: " %><%h review.title %> <br />
<%= form.label "Review:" %><%h review.comment %>
<% end %>
You don't have to find the comments in the controller.

Resources