views:

584

answers:

3

Hi all,

I'm using the field_for form helper with a loop:

<% f.fields_for :permissions do |permission_form| %>
    <tr>
      <td><%= permission_form.object.security_module.name %><%= permission_form.hidden_field(:security_module_id) %></td>
      <td><%= permission_form.object.security_module.description %></td>
    <tr>
<% end %>

The resulting output of the above code is this:

    <input id="role_permissions_attributes_0_id" name="role[permissions_attributes][0][id]" type="hidden" value="76" />
    <tr>
      <td>Diary<input id="role_permissions_attributes_0_security_module_id" name="role[permissions_attributes][0][security_module_id]" type="hidden" value="13" /></td>
       <td>Access to the Diary Module</td>
    </tr>
    <!-- next input field then <tr> tag -->

The problem with this markup is that the input tag falls outside of the tr tag which there for causes validation issues with XHTML.

Does anyone know how I can have the input tag fall inside the tr tag therefore giving me valid XHTML 1.0 STRICT markup?

Thanks

+2  A: 

If you take a look at the Rails source code you'll find this.

# in actionpack/lib/action_view/helpers/form_helper.rb
def fields_for_nested_model(name, object, args, block)
  if object.new_record?
    @template.fields_for(name, object, *args, &block)
  else
    @template.fields_for(name, object, *args) do |builder|
      @template.concat builder.hidden_field(:id)
      block.call(builder)
    end
  end
end

Notice it is adding the hidden field directly here, and it doesn't look like there is any option to change this behavior. The easiest thing is probably to create your own custom form builder.

# in lib/no_id_form_builder.rb
class NoIdFormBuilder < ActionView::Helpers::FormBuilder
  private
  def fields_for_nested_model(name, object, args, block)
    @template.fields_for(name, object, *args, &block)
  end
end

And then use this in your form. You'll need to add the id field manually.

<% f.fields_for :permissions, :builder => NoIdFormBuilder do |permission_form| %>
  <tr>
    <td>
      <%= permission_form.object.security_module.name %>
      <%= permission_form.hidden_field(:security_module_id) %>
      <%= permission_form.hidden_field(:id) unless permission_form.object.new_record? %>
    </td>
    <td><%= permission_form.object.security_module.description %></td>
  <tr>
<% end %>

You may want to submit a lighthouse ticket on this. perhaps there could be a :skip_id_field option to fields_for which does this.

ryanb
Thanks Ryan! This definately helps! I will be logging this with lighthouse this week.
schone
Good answer! Schone, I think you forgot to up-vote it (it was 0 when I got here). As Michael Johnston states (I guess it should be a comment here instead of a separate answer), the :builder option goes on the form_for, not fields_for. Otherwise, perfect answer.
tokland
A: 

Slight correction:

The :builder option needs to go on the form_for containing the fields_for, not the fields_for.

Michael Johnston
+1  A: 

There is a workaround available as of 2.3.5. If you explicitly place the :id field, it won't implicitly add it for you:

<% form_for @foo do |f| %> 
<table> 
  <tbody> 
    <% f.fields_for :bars do |bf| %> 
      <tr>
        <%= bf.hidden_field :id %> 
        <td><%= bf.text_field :name %></td> 
      </tr> 
    <% end %> 
  </tbody> 
</table> 
<% end %>

See https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/3259

Tyler Rick
<inputs> are not allowed in <tr>, i guess it would have to go in the <td>
Jon