6

I have a view on my current project which does something like the following(in haml):

-@horses.each do |horse|
  = render :partial => 'main/votingbox', :locals => {:horse => horse}

The in the _votingbox.html.haml file I have the following:

%div.votingbox
  %span.name= horse.name
  %div.genders
    - if horse.male
      %img{:src => 'images/male.png', :alt => 'Male'} 
    - if horse.female
      %img{:src => 'images/female.png', :alt => 'Female'}
  %div.voting_form
    = form_for(Vote.new, {:url => horse_vote_path(horse)}) do |f|
      = f.label :comment, "Your opinion"
      = f.text_field :comment
      ...
      a bunch of labels and input elements follow generated using the form helpers

This will generate working code but it does generate forms with the same ids for all the form elements which makes the HTML invalid once the votingbox partial is rendered a second time.

My first guess at fixing this was to specify a unique :id to form_for but that only applies to the form tag generated by form_for and not any of the tags inside the form_for block.

One definite solution to this problem is to go through and manually define my own unique ids on form_for and all the form elements I use. This is more work than I had hoped for.

Is there an easier or cleaner way to get unique ids in a similar format to the way Rails currently generates them on all my form elements?

Jordan Running
  • 102,619
  • 17
  • 182
  • 182
cyberkni
  • 61
  • 1
  • 3

2 Answers2

2

You can override the autogenerated ids and names of all form_for content with :as like the following:

= form_for(Vote.new, :as => horse.name, {:url => horse_vote_path(horse)}) do |f|
  = f.label :comment, "Your opinion"
  = f.text_field :comment

So if a given horse.name is foobar, it will generate a comment field whose id is foobar_comment and name is foobar[comment]

But remember to make sure that the dynamic parameter is acceptable as an html id, a horse.name like hor$e is not acceptable as an html id and therefore might break something.

P.S: Sorry for answering very late, but at the time the question was asked, I haven't had learnt anything at all about rails! hope that might help someone out there!

Tamer Shlash
  • 9,314
  • 5
  • 44
  • 82
2

I have removed the original answer as it is totally irrelevant to the updated version of the question.

Update: So now we know that you have an unsaved ActiveRecord object passed to the form_for call, the answer becomes simple: You can override any parameter that form_for generates. That includes the element id. And fields_for sets the context for a specific record.

= form_for(Vote.new, :url => horse_vote_path(horse), :id => dom_id(horse, 'vote')) do |f|
  = f.fields_for horse, :index => horse do |fh|
    = fh.text_field :whatever
    …
edgerunner
  • 14,873
  • 2
  • 57
  • 69
  • get_vote in this scenario is always going to return `Vote.new` which will be an unsaved ActiveRecord object. I've revised the original question accordingly. What would be ideal here is if I could generate the form ids based on the id of the horse object. In this situation the id for the form would be `vote_new_1` for when that partial is rendered for the Horse object with id 1. – cyberkni Oct 31 '10 at 19:04
  • 1
    May I suggest: `:id => dom_id(horse, 'vote')` to generetate `horse_123_vote`. It's easier to read. – Ariejan Oct 31 '10 at 20:53
  • This is a good suggestion and is a much cleaner version of my current code. However this still leads to needing to do the following inside that form_for block that would follow: `f.text_field :comment, :id => dom_id(horse, 'vote_comment')` – cyberkni Nov 02 '10 at 03:16
  • I'm not sure this will work fine and I can't try it now but `form_for([horse, Vote.new], :url => horse_vote_path(horse))` may just work. Try it and let me know if it works so I'll update the answer. – edgerunner Nov 02 '10 at 08:24
  • Another thing to try: `form_for(horse.votes.build, :url => horse_vote_path(horse))`. This could work if you have `has_many :votes` in your `Horse` model. – edgerunner Nov 03 '10 at 06:58