Rails partials have a lot of "hidden" features and this card describes some non-obvious usages of Rails Partials.
Rendering a basic partial
The most basic way to render a partial:
render partial: 'partial'
This will render a _partial.html.erb
file. Notice how all partials need to be prefixed with _.
It's possible to define local variables that are only defined in the partial template.
# _weather.html.erb
<h1>The weather is <%= condition %></h1>
# index.html.erb
render partial: 'weather', locals: { condition: 'good' }
Since this is a common use-case, there's a shorthand way of rendering the partial and defining local variables:
# index.html.erb
render 'weather', condition: 'good'
Notice that it's not possible to mix the explicit partial: 'template' and the shorthand arguments for defining local variables. So either use longform variant or the shorthand variant, but don't mix them.
Another way of defining a local variable in a partial is using the object:
keyword:
# _weather.html.erb
<h1>The weather is <%= weather.condition %></h1>
render 'weather', object: Weather.new(condition: 'bad')
It will automatically define a local variable named after the partial filename, in this example weather.
Using yield within a partial
It's possible for partials to call yield and act like a layout. You can use this to extract common containers in your HTML.
# _card.html.erb
<div class="card">
<%= yield %>
</div>
# index.html.erb
<%= render partial: 'card' do %>
<p>This is card content</p>
<% end %>
<%= render partial: 'weather', locals: { condition: 'good' }, layout: 'card' %>
# will render
<div class="card">
<h1>The weather is good</h1>
</div>
Stick to the long-form for this one.
Rendering a partial in a controller
The examples above are for the common usage of rendering a partial in a template. It's also possible to render a partial in a controller:
class ExampleController < ApplicationController
def show
render partial: 'weather', locals: { condition: 'good' }
end
end
Stick to the long-form partial: '...', locals: {...}
for this one. This will render the partial without any layout.
Rendering collections
It's possible to pass a collection to render and it will render a partial for every item in the collection.
weathers = Weather.all.limit(3)
render partial: 'weather', collection: weathers, layout: 'card'
# will render
<div class="card"><h1>The weather is good</h1></div>
<div class="card"><h1>The weather is bad</h1></div>
<div class="card"><h1>The weather is nice</h1></div>
When rendering a collection, there will be two additional local variables assigned, which help you to do common tasks when rendering multiple items. In the example above, there will be the local variable weather_counter
defined, which returns a counter of the currently rendered item in the collection. Furthermore it will add a weather_iteration
object, with useful helper methods like first?
or last?
Partials vs. Helpers
I recommend to avoid using helpers when rendering large chunks of html and go for a partial instead. If your helper method requires a large chunk of html, you can also render a partial within a helper.
#_mobile_navigation.html.haml
.mobile_navigation
.htmlclass
.htmlclass
.foo
links.each do |link|
= link_to(link)
module SomeHelper
def render_mobile_navigation
links = build_links
render(partial: 'mobile_navigation', locals: { links: links })
end
end
Rendering empty states
Most screens should render something more sensible if a collection is empty. One can avoid ifs
and any?
calls in templates and make use of the return value of render.
<%= render(@collection) || render('empty_state') %>
Caching collections
Fragment caching a rendered collection is easily possible with render @collection, cached: true
. Notice how this is more efficient than multiple normal cache @record
calls, since this uses the Redis' mget
command to request all cache keys in a single redis command.