简体   繁体   中英

Rails: Passing local variables from Controller to View

I was trying to pass a local variable from controller to a view:

# pages_controller.rb
def my_method
  render template: "pages/new_page", message: @message
end

# new_page.html.erb
<% if message %>
  Do something
<% else %>
  Do something else
<% end %>

But this gives me undefined local variable or method message error, whereas this works correctly in case of partials.

I know this can be fixed like this:

def my_method
  render template: "pages/new_page", locals: { message: @message }
end

I was just eager to know, how this works without locals in case of partials.

It's all part of the (not always clear or clearly documented) magic of render . Per a comment in the code ( github ):

If no options hash is passed or if:update is specified, then:

If an object responding to render_in is passed, render_in is called on the object, passing in the current view context.

Otherwise, a partial is rendered using the second parameter as the locals hash.

It's the last part of the comment that is relevant. When you use the shorthand form of render to render a partial implicitly, Rails just assumes that the hash that follows are meant to be passed as locals. So:

<%= render 'my_partial', message: 'I hate bananas' %>

will work fine and message ends up as a local in your partial.

As soon as you specify an option to render (including, ironically, :partial ), you need to explicitly pass a locals hash to pass locals:

# BAD
render template: 'foo/bar', message: 'I hate bananas'
render partial: 'my_partial', message: 'I hate bananas'
render 'full_view', message: 'I hate bananas'  # no options, but not a partial

# GOOD
render template: 'foo/bar', locals: { message: 'I hate bananas' }
render 'full_view', locals: { message: 'I hate bananas' }
render partial: 'my_partial', locals: { message: 'I hate bananas' }

(I don't really hate bananas.)

This part is just opinion, but I don't think the implicit locals in one specific use case is worth it. I tend to just always wrap partial locals explicitly as :locals to save remembering stuff. Note also that some folks would argue that Rails convention is to use instance vars for full views rather than locals, but it is totally valid syntactically.

You're correct that the controller's instance variables are accessible in the associated view. Though you don't seem to be following the Rails framework. I'm unsure if I understood your doubt fully and will attempt my best to explain it in one go.

# pages_controller.rb
def home
    @message = Message.count
end

# home.html.erb
<%= render “shared/navbar” %>

<% if @message > 10 %>
    # Do something
<% else %>
    # Do something else
<% end %>
  1. It's Rails guidelines that the method be associated to the filename. Therefore, “def home” would be associated to the home.html.erb in the pages directory. I recommend you follow this, as it can get very messy.
  2. Rendering a partial is usually reserved for a “shared” directory in the views folder. For example, a navbar.html.erb inside the shared folder.
  3. And most important, the instance variable is passed to the view. A variable without “@“ in the controller will not be sent to the view file.

The short answer is that render is an overloaded function and can be called in a "short form" , that is

render 'number_display', value: 4

which automagically translates to

render :partial => 'number_display', :locals => { :value => 4 }

The long answer can be found here .

Generally speaking, the short form is always going to render a partial, while the second example could specify different options:

The primary options are:

:partial - See ActionView::PartialRenderer[...] for details.

:file - Renders an explicit template file. Add:locals to pass in, if so desired. It shouldn't be used directly with unsanitized user input due to lack of validation.

:inline - Renders an ERB template string.

:plain - Renders provided text and sets the content type as text/plain.

:html - Renders the provided HTML safe string, otherwise performs HTML escape on the string first. Sets the content type as text/html.

:json - Renders the provided hash or object in JSON. You don't need to call.to_json on the object you want to render.

:body - Renders provided text and sets content type of text/plain.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM