Rails Designer
Posted on November 14, 2024
This article was originally published on Rails Designer
Partials have been an integral part of Rails. They are conceptually simple to understand, but they pack quite a few smart and lesser known features you might not know about. Let's look at all of them!
Basic rendering
Let's look at the basics of rendering a partial.
<%= render partial: "application/navigation" %>
This will render the partial app/views/application/_navigation.html.erb. The filename starts with an underscore, but is references without one.
You can also omit the partial
keyword.
<%= render "application/navigation" %>
So far nothing you didn't know already, I assume.
Local Variables
You can pass variables like so.
<%= render partial: "user", locals: {user: @user} %>
Again, this short-hand can also be used:
<%= render "user", user: @user %>
Or if you follow conventions from a to z, you could write this:
<%= render @user %>
This assumes a few things:
-
@user
is present - a partial _user.html.erb in app/view/users.
Quite a bit cleaner, right? So when does one use the short-hand version and when not? Typically you can use the short-hand version, especially when you are only passing 1 or 2 variables. But you can also pass other arguments; then it's required to use the longer syntax. Like this example:
<%= render partial: "user", locals: {admin: @admin}, as: :user %>
Explicit local variables
Introduced in Rails 7.2. You can set explicit local variables to clearly tells which variables are required.
<%# locals: (name:) -%>
<%= name %>
You can also set a default value. So that if the user has no name set, it renders "Stranger".
<%# locals: (name: "Stranger") -%>
<%= name %>
Local assigns
You can also use “implicit local variables”. Assume the above user_details is used in an admin view.
<div>
<p>
<%= name %>
</p>
<% local_assigns[:admin?] %>
<dl>
<dt>
Created at:
</dt>
<dd>
<%= user.created_at %>
</dd>
</dl>
<% end %>
</div>
You can render the partial like so:
<%= render "user", user: @user, name: "Cam", admin?: true %>
Or omit the admin?: true
in non-admin views:
<%= render "user", user: @user, name: "Cam" %>
This is useful, because without the local_assigns
, rendering would fail.
Layouts for partials
You can also wrap a partial in a “layout”. This is not to be confused with view layouts stored in app/views/layouts.
<%= render partial: "user", layout: "shared/admin" %>
The admin “layout” is stored at app/views/shared/_admin.html.erb and could look like this:
<div class="admin">
<%= yield %>
</div>
Render collection
If you want to render a collection of users, you can write the following, for example in app/views/users/index.html.erb:
<%= render partial: "user", collection: @users %>
Or its shorthand variant:
<%= render @users %>
This automatically looks up the _user.html.erb partial in app/views/users, assuming @users
is a collection of User's.
It doesn't have to a collection of one and the same Resource though, e.g. User. This too works:
<%= render [User.first, Admin.first, Customer.first, User.second] %>
This is assuming you have those resources, and each has their respective partial, e.g. _user.html.erb, _admin.html.erb and _customer.html.erb.
Empty State
If the collection, e.g. @users
is empty, render
returns nil
. So if you want to render an empty state, that is simple.
<%= render(@users) || "No users yet…" %>
Local variables
You can change the the local variable with a collection too.
<%= render partial: "user", collection: @users, as: :customer %>
In the _user.html.erb partial, you can now use customer.id
(or whatever other attribute is available).
You also pass other variables:
<%= render partial: "user", collection: @users, locals: {admin?: true} %>
Counter variables
You can render a counter too: <%%= user_counter %>
. The name, the part before the underscore, is derived from the partial name. So **customer.html.erb* would be* <%%= customer_counter %>
.
Layout for collections
Similarly to resource partials, you can also pass collections partials the layout option.
<%= render partial: @users, layout: "users/wrapper" %>
Then in app/views/users/_wrapper.html.erb, you could have something like this:
<ul id="users">
<%= yield %>
</ul>
Spacer template
There is one last option for collection which is spacer templates. It will be inserted between each instance. Use it like so:
<%= render partial: @users, spacer_template: "user/divider" %>
This will load the partial app/views/users/_divider.html.erb.
Bonus: Make partials look like components
If you have used ViewComponent, but can't use them in your current project for whatever reason, here's a way you make your partials quack a bit more like ViewComponent.
Create your components in app/view/components. For example a _card.html.erb.
<section class="card">
<h4 class="card__header">
<%= title %>
</h4>
<div class="card__body">
<%= yield %>
</div>
</section>
Then in your view use it like this:
<%= render layout: "components/card", locals: { title: "User Profile" } do %>
<p>Just some text for this user</p>
<% end %>
But you probably want a helper to abstract some of that boilerplate away.
# app/helpers/components_helper.rb
module ComponentsHelper
def component(name, **, &)
render layout: "components/#{name}", locals: **, &
end
end
Now you render it like this:
<%= component "card", title: "User Profile" do %>
<p>Just some text for this user</p>
<% end %>
Pair that with the Explicit local variables and you got a pretty solid component-like set up without any dependencies.
And that are all the things you (didn't) know about Rails' partials.
Posted on November 14, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.