62

I am rendering a partial like this:

$("#box_container").html("<%= escape_javascript( render :partial => 'contacts/contact_tile', :collection => @contacts) %>")

Problem is that my partial is expecting the variable 'contact'.

ActionView::Template::Error (undefined local variable or method `contact'

I simply want to tell the partial to expect a variable contact. Should iterate through @contacts as contact. How do I do that?

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
botbot
  • 7,299
  • 14
  • 58
  • 96

4 Answers4

143

Found this is also helpful from the docs. You aren't limited to having the variable named after the partial:

http://guides.rubyonrails.org/layouts_and_rendering.html

To use a custom local variable name within the partial, specify the :as option in the call to the partial:

<%= render :partial => "product", :collection => @products, :as => :item %>

With this change, you can access an instance of the @products collection as the item local variable within the partial."

rubyprince
  • 17,559
  • 11
  • 64
  • 104
botbot
  • 7,299
  • 14
  • 58
  • 96
  • this is strangely not working for me in Rails 4.1. Was it added in 4.2 or something? – jrochkind Apr 08 '16 at 15:55
  • 7
    This works, but *only if you specify `partial:`*. You cannot do `render "product", collection: @projects, as: :item`. You need to specify `partial: "product"` or the `as` option isn't applied. – coreyward Aug 12 '17 at 03:15
  • @coreyward is correct, confirmed by Rails documentation: `If you're not going to be using any of the options like collections or layouts, you can also use the short-hand defaults of render to render partials.` for at least `5.1.3`. – roflmyeggo Sep 05 '17 at 02:04
17

The documentation at http://guides.rubyonrails.org/layouts_and_rendering.html says:

When a partial is called with a pluralized collection, then the individual instances of the partial have access to the member of the collection being rendered via a variable named after the partial.

So it will be passed a variable called "contact_tile" instead of "contact". Perhaps you can just rename your partial.

If this naming is important, you could do it explicitly without the collection option by something like:

@contacts.each { |contact| render :partial => 'contacts/contact_tile', :locals => {:contact => contact } }

(although as a commenter pointed out, this may not be as performant)

matthew.tuck
  • 1,267
  • 9
  • 11
  • 1
    see below answer. it's easy to override this behavior by overriding the name of the variable with the :as option. – botbot Oct 05 '12 at 23:40
  • 1
    You could simply write `render @contacts`, convention over configuration, you know. `@contacts` is an array of `Contact` instances, so every `contact` record returns `'contacts/contact'` on `to_partial_path` invocation (Rails does this under the hood). http://api.rubyonrails.org/classes/ActionView/PartialRenderer.html#class-ActionView::PartialRenderer-label-Rendering+the+default+case – dskecse Dec 17 '14 at 10:19
  • 1
    Your code example will parse and compile the template `N` times, and may slow down your site. – akuhn Dec 17 '18 at 08:11
  • @dskecse , I think you could change your comment to a post answer. – bonafernando Nov 21 '19 at 19:15
15

Latest syntax are :

index.html.erb

<%= render partial: "product", collection: @products %>

_product.html.erb

<p>Product Name: <%= product.name %></p>

@products is used in partial as product

Where @products can be considered as Product.all and product can be considered as a row of product i.e. Product.first as looped all product one by one.

Manish Shrivastava
  • 30,617
  • 13
  • 97
  • 101
  • NOTE: You can't use a symbol for the `partial` value, like `:product`. You need to use a String, like you have it `"product"`. I learnt that the hard way and it ended up defaulting to the model partial of the collection instead of the custom partial I wanted. – Joshua Pinter Sep 29 '20 at 16:38
3

You can specify a custom variable name as the default with the keyword as:

<%= render partial: 'line_items/line_item', collection: order.line_items, as: :item %>
Thomas Van Holder
  • 1,137
  • 9
  • 12