Got burned by two things today in one of my apps by upgrading to Rails 3.1. The first thing was a few nested forms stopped working when editing an existing record that had child records. None of the children were getting rendered. For briefity, the form basically was like:
<%= form_for @meal do |meal_form| %>
<table>
<tbody>
<% meal_form.fields_for :meal_items do |item_form| %>
<%= render :partial => 'meal_item_fields',
:locals => { :f => item_form } %>
<% end %>
</tbody>
</table>
<% end %>
Can you spot the error? Once I spotted it I felt rather dumb myself. It’s the following erb tag
<% meal_form.fields_for :meal_items do |item_form| %>
Ya even when they’re nested you HAVE to use the <%= form... doh!
<%= meal_form.fields_for :meal_items do |item_form| %>
The second problem I ran into, and which I still don't have an answer for, though if you do answer my StackOverflow question. Basically, in Rails 2.3 if you needed to get at an instance of the Template Builder you could use @template, but then they changed it in Rails 3 to a special method called "view_context". Far enough, so I was using view_context in Rails 3.0.X to create nested forms (other than the one above) dynamically using jQuery ajax and js.erb partials. Using view_context allowed me to call fields_for off of it so that I would get nicely named form_fields when I didn't have the parent FormBuilder handy. Like this
In some_view.js.erb
<% fields =
view_context.fields_for :meal_items, Meal.new.meal_items.new do |f|
render :partial => 'meal_item_fields', :locals => {:f => f}
end %>
$("#meal-items").append("<%= escape_javascript(fields) %>");
But once I upgrade to 3.1, I started getting the following error
ActionView::Template::Error (undefined local variable or method `view_context' for #<#:0x1057b9f70>):
Perhaps this was removed as the Core team figured, why do you need the view_context available when you're in a view template already... but I don't know. I was able to workaround it though by moving my code up into the controller. Which does seem to be a violation of the MVC separation, but I couldn't find any other way to make it work like pre Rails upgrade. So instead of creating a local variable "fields", this became an instance variable that was set in the controller action. And render needed to become render_to_string with an html_safe call tacked on the end.
In controller
def some_ajax_action
@fields =
view_context.fields_for :meal_items, Meal.new.meal_items.new do |f|
render_to_string(:partial => 'fields', :locals => {:f => f}).html_safe
end
end
I don't like this, but it'll do for now I suppose. And now in some_view.js.erb
$("#meal-items").append("<%= escape_javascript(@fields) %>");