Today I struggled trying to understand the Rails Way of cleaning up some of my boilerplate styling code in my view. The terms “partial, layout, template, helper” all get tossed around a lot, but few of the examples hint towards creating your own html helper tags – condensing complex HTML containers into simple ruby expressions.

The Bad

My views, being styled by Bootstrap and SBAdmin were difficult to navigate and digest, since the actual relevant content was surrounded by so much boilerplate HTML.

Here’s an example of what I was typically seeing:

views/targets/show.html.erb

<div class="row">
  <div class="col-lg-12">
    <div class="panel panel-default">
      <div class="panel-heading">
        <i class="fa fa-sitemap fa-fw"></i> Target
      </div>
      <div class="panel-body">
        <p>
          <strong>Name:</strong> <%= @target.name %>
        </p>
        <p>
          A bunch more content!
        </p>
      </div>
    </div>
  </div>
</div>

<div class="row">
  <div class="col-lg-12">
    <div class="panel panel-default">
      <div class="panel-heading">
        <i class="fa fa-industry fa-fw"></i> Target Statistics
      </div>
      <div class="panel-body">
        <table>
          It's a table! For statistics!
        </table>
      </div>
    </div>
  </div>
</div>

The documentation for partials and layouts, described something very close to what I wanted to achieve, but it left a few important use case scenarios, and their corresponding implementation out.

For some reason, the act of creating a recyclable HTML container snippet was not as straightforward as the documentation led me to believe. In fact, many articles on Stack Overflow and similar tutorials weren’t much help either – most were posing the question as generating an overall layout for your controller’s action, interacting with the primary layout, or they simply didn’t work.

The Better

First, a disclaimer.

I have no idea what I'm doing.

I’m not sure if this is the best way to accomplish it, but I was eventually able to find someone with the same problem. I used the following:

views/shared/sbadmin/_row_panel.html.erb

<div class="row">
  <div class="col-lg-12">
    <div class="panel panel-default">
      <div class="panel-heading">
        <%= header.html_safe %>
      </div>
      <div class="panel-body">
        <%= yield %>
      </div>
    </div>
  </div>
</div>

views/targets/show.html.erb

<%= render layout: 'shared/sbadmin/row_panel', locals: {header: '<i class="fa fa-sitemap fa-fw"></i> Target'} do %>
  <p>
    <strong>Name:</strong> <%= @target.name %>
  </p>
  <p>
    A bunch more content!
  </p>

<% end %>

<%= render layout: 'shared/sbadmin/row_panel', locals: {header: '<i class="fa fa-industry fa-fw"></i> Target Statistics'} do %>
  <table>
    It's a table! For statistics!
  </table>
<% end %>

Previously, the documentation led me to believe that to create individual sections, I should be using:

<div class="panel-heading">
  <%= yield: header %>
</div>

I couldn’t get yield: header to work, and I think it’s because it may have been feeding that up to a different layout (the primary). I really don’t understand.

While the word “Layout” seems to be different than what I seek, using a partial without :layout doesn’t seem to allow me to use the blocks for content in the middle.

The Sugar

After finding a working solution, I wanted to make it a little more user-friendly. I enlisted the help of html helpers as seen in this SO Answer.

views/shared/sbadmin/_row_panel.html.erb

<div class="row">
  <div class="col-lg-12">
    <div class="panel panel-default">
      <div class="panel-heading">
        <%= header.html_safe %>
      </div>
      <div class="panel-body">
        <%= yield %>
      </div>
    </div>
  </div>
</div>

helpers/sbadmin_helper.rb

module SbadminHelper
  def sbadmin_panel_for (header, &block)
    render layout: 'shared/sbadmin/row_panel', locals: {header: header.html_safe} do
      yield
    end
  end
end

views/targets/show.html.erb

<%= sbadmin_panel_for '<i class="fa fa-sitemap fa-fw"></i> Target' do %>
  <p>
    <strong>Name:</strong> <%= @target.name %>
  </p
  <p>
    A bunch more content!
  </p>
<% end %>

<%= sbadmin_panel_for '<i class="fa fa-industry fa-fw"></i> Target Statistics' do %>
  <table>
    It's a table! For statistics!
  </table>
<% end %>

I can now toss sbadmin_panel_for 'Title' around in my views anywhere I wish and recycle that same content easily.

This is significantly easier to manage. There are many of these panels throughout my various views, and they all contain the same generic styling. Now, I have a way to keep this styling DRY in a partial/layout, and more importantly, keep my views more to the point. I’m going to keep refining these SBAdmin template components out into various files in views/shared/sbadmin/ and see if I still feel comfortable with this later. One thing immediately coming to mind is that it would be nice to default to the col-lg-12 but allow it to be edited with a local assignment if desired.

I wonder out loud to you, if there is a more Railsy solution?