date_select, time_select doesn’t work with auto_prefix (object[])

11 03 2009

I ran into the following issue while i was dealing with a multi-model form where a Sport is being saved with associated Events


<%= render(:partial => "event", :collection => @sport.events) %>

This partial is being rendered for collection of events for new/edit Sport

<% new_or_existing = event.new_record? ? 'new' : 'existing' %>
<% prefix = "sport[#{new_or_existing}_event_attributes][]" %>

<% fields_for prefix, event do |e| -%>
 <%= e.time_select("best_time") %>
<% end -%>

Here is the problem:

For new Event, time_select name attribute should be:   sport[new][][best_time(5i)]
but this was being assigned:   sport[new][best_time(5i)]

Following error is thrown when this form is submitted

Status: 500 Internal Server Error
Conflicting types for parameter containers. Expected an instance of Hash but found an instance of Array

For more information regarding this issue you can go through this ticket

After digging into rails code this came into light

# rails/actionpack/lib/action_view/helpers/form_helper.rb:508

if @object_name.sub!(/\[\]$/,"")
  if object ||= @template_object.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
   @auto_index = object.to_param
  else
   raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
  end
end

So here @object_name would be “sport[new_event_attributes][]”
and @object_name.sub!(/\[\]$/,””) will return “sport[new_event_attributes]”
and @auto_index will set to nil

select_time internally calls option_with_prefix to assign name attribute for select element

#rails/actionpack/lib/action_view/helpers/date_helper.rb:664

def options_with_prefix(position, options)
 prefix = "#{@object_name}"
 if options[:index]
  prefix << "[#{options[:index]}]"
 elsif @auto_index
  prefix < "#{prefix}[#{@method_name}(#{position}i)]")
 end
end

Work around: pass a non nil index value along with date_select or time_select

<% new_or_existing = event.new_record? ? 'new' : 'existing' %>
<% prefix = "sport[#{new_or_existing}_event_attributes][]" %>

<% fields_for prefix, event do |e| -%>
 <%= e.time_select("best_time", :index => event.id || "") %>
<% end -%>