OBLSK Logo

Build a Zebra Table Using the Cycle Helper in Rails

For those new to Rails, styling dynamic data inside of an ERB file can present some challenges if you are not familiar with the Rails API. Below, I’ll walk you thought building a zebra table in a Rails ERB file using the Rails cycle helper and Tailwind CSS styles. The example app we will build for this post will be a stock-tracker app that lists companies, their exchanges, and stock prices.

Let’s start with a fresh Rails application using Tailwind and Postgresql as our database.

        rails new schedule-dinner --css tailwind -d postgresql
    

Change directory (cd) into the stock-tracker folder that was just created.

        cd stock-tracker
    

Create the database

        rails db:create
    

You should see output similar to the following in your terminal

    stock-tracker git:(main) ✗ rails db:create
Created database 'stock_tracker_development'
Created database 'stock_tracker_test'

Before moving any farther, we will scaffold a company for use within the application. The following code will create templates, test files, and a database migration to create a table in Postgresql that will have a Companies table with a separate column for each company name, the exchange a company is listed on, and stock price.

        rails generate scaffold company name:string exchange:string price:decimal
    

Dealing with pricing data for companies’ stock prices, we will want to adjust precision and scale for decimal before we run our migration.

In your favorite editor, navigate to the app/db/migrate folder. There should be a file named,

        [timestamp]_create_companies.rb
    

where [timestamp] is replaced with the timestamp from the time your created the migration. Your migration file should look like

class CreateCompanies < ActiveRecord::Migration[7.0]
    def change
        create_table :companies do |t|
            t.string :name
            t.string :exchange
            t.decimal :price

            t.timestamps
        end
    end
end

Update that migration to match the code below. We will only being making changes to the line used to define :price

class CreateCompanies < ActiveRecord::Migration[7.0]
    def change
        create_table :companies do |t|
            t.string :name
            t.string :exchange
            t.decimal :price, :precision=> 8, :scale => 2

            t.timestamps
        end
    end
end

Run the migration to create the companies table in the database.

rails db:migrate

Start up the Rails app to see what we have so far,

rails s

rails s is shorthand for rails server. That will start up your Rails server, so that you can view your Rails application in a browser on your machine. You should see command output similar to the following,

stock-tracker git:(main) ✗ rails s
=> Booting Puma
=> Rails 7.0.1 application starting in development
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 5.6.1 (ruby 2.7.2-p137) ("Birdie's Version")
* Min threads: 5
* Max threads: 5
* Environment: development
* PID: 3047
* Listening on http://127.0.0.1:3000
* Listening on http://[::1]:3000
Use Ctrl-C to stop

Open up a browser and navigate to http://localhost:3000. You should see,

rails initial screenshot

Great, success!

In your terminal use ctrl + C to stop your rails server and start the rails server back up with ./bin/dev. The ./bin/dev command will start rails, but will also rebuild your Tailwind css when you make view changes.

Another option is to leave Rails running through rails s and running the following in another terminal window. The command below will watch for UI changes and rebuild Tailwind CSS, when needed.

npx tailwindcss -i app/assets/stylesheets/application.css -o app/assets/stylesheets/application.tailwind.css --watch

After starting your rails server with ./bin/dev, navigate to http://localhost:3000/companies in your browser. You should see,

companies screenshot

Click on the New Company button. You should see,

New Company screenshot

Add data for 5 companies of your choice. After adding some companies, navigate back to http://localhost:3000/companies. You should see something similar to,

New Company not empty screenshot

This list of companies would like much nicer in a table, so let’s change this view by opening up app/views/companies/index.html.erb in your editor. Update your code to:


    <div class="w-full">
        <% if notice.present? %>
            <p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block" id="notice"><%= notice
        %></p>
    <% end %>

    <div class="flex justify-between items-center">
        <h1 class="font-bold text-4xl">Companies</h1>
        <%= link_to 'New company', new_company_path, class: "rounded-lg py-3 px-5 bg-blue-600 text-white block font-medium"
    %>
    </div>

    <div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
        <table class="min-w-full divide-y divide-gray-200">
            <thead class="bg-gray-300">
                <tr>
                    <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
    Name
                </th>
            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
    Exchange
            </th>
            <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                Stock Price
            </th>
            <th scope="col" class="relative px-6 py-3">
                <span class="sr-only">Show</span>
            </th>
            <th scope="col" class="relative px-6 py-3">
                <span class="sr-only">Edit</span>
            </th>
        </tr>
    </thead>
    <tbody>
        <% @companies.each do |company| %>
            <tr>
                <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
                    <%= company.name %>
                </td>
                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
                    <%= company.exchange %>
                </td>
                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
                    <%= company.price %>
                </td>
                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
                    <%= link_to "Show", company, class: "text-indigo-600 hover:text-indigo-900" %>
                </td>
                <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
                    <%= link_to 'Edit', edit_company_path(company), class: "text-indigo-600 hover:text-indigo-900" %>
                </td>
            </tr>
        <% end %>
    </tbody>
    </table>
    </div>
    </div>

Refresh http://localhost:3000/companies in your browser. You should see,

Companies Table screenshot

It might be hard to tell rows apart as this list grows, so let’s add zebra styling to our table. Since our list of companies is dynamically generated, we do not have separate HTML for odd vs even rows, so we will use the cycle helper in Rails. We will change one line in app/views/companies/index.html.erb. It should be line 34 in your editor. will change to

        <tr class="<%= cycle("bg-white", "bg-gray-200") -%>">
    

The cycle helper will set the class of every other row with bg-white and the opposite rows with a class of bg-gray-200. WIth that change, you should have a table of companies with zebra table styling. Your list of companies should look like,

Company Zebra Tiled screenshot

Thank you for following along. At this point, you should have what you need to get a basic Rails application started with Tailwind CSS and Postgresql. In a future post, we’ll refactor this table out into a ViewCompoent for ease of reuse in other areas of the application.

References:

Rails Guides for Migrations - https://guides.rubyonrails.org/v3.2/migrations.html

cycle helper - https://api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html#method-i-cycle

ViewComponents - https://viewcomponent.org