No view_context, in views, in Rails 3.1 changes

by Irish on October 13, 2011

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) %>");

{ 2 comments… read them below or add one }

Gonzalo February 7, 2012 at 8:25 pm

Isn’t view_context unnecessary in some_view.js.erb?
Just fields_for alone should work

Reply

Irish February 8, 2012 at 2:45 pm

You’re correct, before it was necessary to use it however, now it’s not.

Reply

Leave a Comment

Previous post:

Next post: