In web apps, we are all too familiar with the concept of looping over list items and displaying them to the user. Be it a list of products, users, articles, or some other entity.
In rails, we can display a list of items in the following ways.
1) By putting a markup of each item in a file that also contains some other markup. Basically, the item markup is not in its own separate file.
2) By putting item markup into its own separate file (partial).
In this second option, we have two convenient ways to render each item in the list.
- loop over the list and pass each item as a local variable into the item partial.
- pass the entire list directly to the
Let's see all of it in a little more detail.
Imagine that we are running a men's suiting store and we have a suitings page on which we display surprisingly all the men suits via
<h1>Welcome to the Men's Clothing</h1> <h2>We offer the finest men's suiting West of Atlantic</h2> <p>While you are here paint the digital town red!</p> <ul> <% @suitings.each do |suiting| %> <li> <div><%= suiting.name %></div> <div><%= suiting.type %></div> <div><%= suiting.fabric %></div> <div><%= suiting.category %></div> <div><%= suiting.description %></div> <div><%= suiting.price %></div> </li> <% end %> </ul> <p>We hope you won't leave, without giving comfort to your body that it deserves!</p>
This is the first and the most straightforward way to display a list of items. We have
@suitings instance variable coming from the
controller#action. We loop over the list and display each item one by one.
Currently, the markup for each list item housed in
<li> tag is only a few lines long, so it doesn't matter much.
But who is to say that our store won't become a big hit tomorrow and then we would have to render much more complicated list item markup, several lines long.
The product card may soon grow to contain checkboxes, radio buttons, drop-down, text-field, and other information about the product. Such a markup would naturally swell the list item template.
Also currently we are dumping the list item template in a
suitings.html.erb file that also has its own markup, such as the
<h1> <h2> and <p> tags. Perhaps this page will also grow to contain more markup.
Thus putting everything in one file can create a lot of mess and with time can become a maintenance nightmare.
That is where the second form of list items rendering comes into play, where we separate the code out into its own partial.
But before we look at that, it helps to have a little detour about the partials.
If you have done any front-end dev with Vue, Angular or React you can think of a partial as a component.
For instance, you can have a form, header, footer, product-card, table, etc. markup littered here and there in your Rails app. They can all be converted into their own standalone partials.
These partials can be reused in multiple places very much like front-end components.
You can even design these partials to be generic so that they can respond to variables being passed in from the outside. In doing so you will be defining a public API to communicate with the partial and thus can customize it according to the context in which it is used.
We will look at a brief example of that at the end.
Having mentioned that, let's get back and look at the second way of rendering list items.
Now we will create a separate partial
_suiting.html.erb and put the relevant markup in there.
<li> <div><%= suiting.name %></div> <div><%= suiting.type %></div> <div><%= suiting.fabric %></div> <div><%= suiting.category %></div> <div><%= suiting.description %></div> <div><%= suiting.price %></div> </li>
Now we can display a list of suitings in the
suitings.html.erb with the following code
<ul> <% @suitings.each do |suiting| %> <%= render suiting %> <% end %> </ul>
Here, we are passing
suiting as a local variable inside of the
But how does rails know which partial to use? We didn't tell that to the
Rails figures that out on its own. It looks at the
suiting variable and determines that it belongs to the
So inside of the
views directory, it looks for the
suitings directory, inside of that it looks for
views/suitings/_suiting.html.erb. Rails does that all automatically.
With that, we have a cleaner code. Now we have our suiting template housed in its own partial and if tomorrow it grows and gets complicated it won't pollute other places.
But then there is even a better and lighter syntax to render a list of items. You don't even have to define the loop construct. You can directly pass the list to the
<ul> <%= render @suitings %> </ul>
Here again, rails will look at each item in the
@suitings list, determine that it is instantiated from the
Suitings class and will look for the
_suiting_html.erb partial and use that to render each item in the list.
Here the instance variable name is
@suitings. But it can be any name of your choice. You can name it
@men_clothes and it would work just the same because each item in the collection is still of type
Suiting or instantiated from the
Suiting class. So Rails would look for the
_suiting_html.erb partial to render each item.
In closing, let's see how we can generalize
_suiting.html.erb so that it can work with any product list such as sweaters, socks, pants and not only with suitings.
Firstly, we will rename our partial as
_product.html.erb to make it more generic and make it work with any kind of product list.
Also, we will move it into a shared directory
shared/_product.html.erb because now it will be shared or reused in multiple places (putting in
shared directory is not required but it's a common convention).
Secondly, we will have to refactor the markup as well
<li> <div><%= product.name %></div> <div><%= product.type %></div> <div><%= product.fabric %></div> <div><%= product.category %></div> <div><%= product.description %></div> <div><%= product.price %></div> </li>
Here we are using
product as the local variable, we will see why.
Now if we want to render a list of say @shorts we can do.
<ul> <%= render partial: "shared/product", collection: @shorts %> </ul>
Here we have passed a configuration hash to the
render method mentioning which list we want to render in our case
@shorts using which partial in our case
Inside the partial, each list item will be available under the same name as the partial name. Since, in our case, the partial name is
_product.html.erb, each item inside the partial will be called
There are three main ways to render lists inside the Rails templates.
- Mixing item markup with another markup (not having its own separate file).
- Creating a separate partial for item markup.
- Generalizing the partial so that it can be reused in multiple places and work with any list.