Rails Partials

Updated . Posted . Visible to the public. Repeats.

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.

Profile picture of Niklas Hä.
Niklas Hä.
Last edit
Niklas Hasselmeyer
License
Source code in this card is licensed under the MIT License.
Posted by Niklas Hä. to makandra dev (2023-03-07 09:44)