DEV Community

loading...

Rendering list items in Ruby on Rails 6

Ayaz
Programmer, Author, Podcaster
・5 min read

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 render method.

Let's see all of it in a little more detail.

Putting item markup in a file that also contains some other markup

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 suitings.html.erb file.

<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.

Problem with this approach

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.

What component is to the frontend partial is to the backend.

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.

Putting Item markup in its own separate partial

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 _suiting.html.erb partial.

But how does rails know which partial to use? We didn't tell that to the render method.

Rails figures that out on its own. It looks at the suiting variable and determines that it belongs to the Suiting class.

So inside of the views directory, it looks for the suitings directory, inside of that it looks for _suiting.html.erb file.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 render method.

<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 theSuiting class. So Rails would look for the _suiting_html.erb partial to render each item.

Making Suiting Partial/Component reusable

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 _product.html.erb.

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 product.

Conclusion

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.

Discussion (0)

Forem Open with the Forem app