kishore cheruku
kishore cheruku

Reputation: 521

Locals issue in Rails application

I am getting errors while doing an AJAX call with remote: true with locals. Here's the error:

ActionView::Template::Error (undefined local variable or method `f' for #<#<Class:0x94db890>:0x9ab41d0>):

Here is my code below:

new.html.erb

<%= form_for(@author) do |f| %>
  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
 <div class="field">
   <%= f.label :age %><br>
   <%= f.number_field :age %>
 </div>
 <%= link_to "Add Books" , remote: true  %>

 <div id = "add_book"></div>
 <%= f.submit %>

Controller

def new
  @author = Author.new
  @books = @author.books.build

  respond_to do |format|
    format.html
    format.js 
  end
end

new.js.erb

$("#add_book").append(" <%= escape_javascript(render partial: 'addbook', :locals => { :f => f }) %> ");

Upvotes: 0

Views: 60

Answers (2)

David
David

Reputation: 3610

You are calling the new action of your controller using remote: true (an AJAX call). This will result in new.js.erb being rendered to be sent back as the response - the new.html.erb will not be processed.

Within new.js.erb you have:

$("#add_book").append(" <%= escape_javascript(render partial: 'addbook', :locals => { :f => f }) %> ");

Meaning it is attempting to render the addbook partial and set up a local variable for it, to be referenced in the partial as f, and assigning it the value (local to new.js.erb) f.

new.js.erb has not declared the local var f and thus your error.

To assist more we would need to know what the intention of this variable is - in what way does _addbook.html.erb require it?

Upvotes: 3

Taryn East
Taryn East

Reputation: 27747

So... the way that an erb template works is this:

Rails generates a page, in your example, this would be new by going through the new action in the controller and gathering up all the variables (eg author and books).

Then it gets the right template for the action. In the case when you first open up the new page you get the form... right? That comes from the new.html.erb template.

So rails reads through the template and runs the ruby code... replacing it with appropriate html. Then it sends the html to the browser.

By the time it's sent it to the browser, there is no longer any ruby code... just html, because the browser doesn't understand ruby, just html - so that's all that gets sent.

Now... the browser has an html form, that contains some js - this would be the add books link - it's html, that calls some js that sends a request to your rails app.

The rails app then goes through the new action, gathering the variables again, but this time, it doesn't render the html template... because it got called by js. So it grabs the js template instead.

At this point rails knows nothing about the html template. Anything that was in the html template is long gone - sent away to the browser. All it knows about is what is in the js template. It tries to run the ruby code in the template... and it's asking for f... and f doesn't exist in your js template... so it gets confused and tells you it doesn't know about this f thing.

It doesn't matter that at some point in the past it rendered a form called f, because that belonged to a way distant past request that no longer exists. The browser doesn't tell rails about f because it never knew about it (that was part of the ruby, not the html that is all that the browser ever saw). So... there is no f. ;)

In this case - I am not sure you need to pass f in as a local variable at all. If you are rendering the template that contains the form... then you don't need to pass in a form because it will already be rendering the form. Try just rendering the partial without passing in locals, and see what happens... if you get a bug, we can then work on that.

Upvotes: 1

Related Questions