LaunchSchool - An Online School for Developers /

Blog

Integrating Rails and Bootstrap, Part 2 - CSS and Components

This is part 2 of a 3-part tutorial series.

This portion of this tutorial series will introduce you to some of the CSS-based features that Bootstrap offers. In order to follow along with this tutorial, you will need an application that has Bootstrap integrated. If you do not have that yet, please check out part one of this tutorial series to Install Bootstrap with Rails

The Grid

Bootstrap has a great examples page here, if you are not familiar with what a grid system is, take a look there before continuing with this section.

In order to make use of the Bootstrap grid, you need three components in a very specific order. First, you must have an element (usually a div) which has the CSS class container. Inside of that element, you need an element with the CSS class row. The only elements that can be directly inside of your row elements are column elements.

Here is an HTML example of a basic grid layout in action:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...

<html>
  ....

  <body>
    <div id="main-container" class="container">
      <div class="row">
        <div id="sidebar" class="col-xs-3">
          <!-- sidebar content -->
        </div>
        <div id="content" class="col-xs-9">
          <!--  main content -->
        </div>
      </div>
    </div>
  </body>
</html>

Grid with fluid layouts

If you prefer your design to fill the entire width of the browser, Bootstrap has you covered. You simply change your outermost container-classed element to have the container-fluid class instead, and that element will flow all the way out to the full width of the browser. The container will also collapse with the browser width, which is where the col-xx-xx classes come in.

The same example as above, but with a fluid layout:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...

<html>
  ....

  <body>
    <div id="main-container" class="container-fluid">
      <div class="row">
        <div id="sidebar" class="col-xs-3">
          <!-- sidebar content -->
        </div>
        <div id="main-content" class="col-xs-9">
          <!--  main content -->
        </div>
      </div>
    </div>
  </body>
</html>

Adding the grid to our app

We can improve our app by implementing the grid system. We will create our container div, a row, and two columns in app/views/layouts/application.html.erb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- app/views/layouts/application.html.erb -->

<!DOCTYPE html>
<html>
  <head>
    ...
  </head>
  <body>

    <div id="main-container" class="container">
      <div class="row">
        <div class="col-xs-3">
          <h3>Tealeaf Academy Todo</h3>
        </div>
        <div class="col-xs-9">
          <%= yield %>
        </div>
      </div>
    </div>

  </body>
</html>

Notice we added a sidebar column that is three columns wide.

Now if you visit your app in your browser, you will see the content shifted to the right, making room for the sidebar on the left.

By the way, you can nest your rows inside of other columns, as long as each row has no more than 12 columns specified directly within it. For example, this is perfectly valid:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div class="container">
  <div class="row">
    <div class="col-xs-3"></div>
    <div class="col-xs-9">
      <div class="row">
        <div class="col-xs-6">
          <div class="row">
            <div class="col-xs-6"></div>
            <div class="col-xs-6"></div>
          </div>
        </div>
        <div class="col-xs-6">
          <div class="row">
            <div class="col-xs-6"></div>
            <div class="col-xs-6"></div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

If you had this html, with some colored borders applied to the div’s, this is what you would have. Take a minute and match each column element up to the html line that created it:

Tables

Tables are a great example of how easy it is to style your components using Bootstrap CSS classes. All you need to do is add the table class to the table element and refresh the page.

1
2
3
4
5
6
7
...

    <table class="table">
      ...
    </table>

...

Look how nice that looks now! There are more styles available for tables, which are used by appending one of the following classes to the table:

  • table-striped
  • table-bordered
  • table-hover
  • table-condensed
  • table-responsive

You can also apply styling to specific rows using the follow classes (applied to the tr element):

  • active
  • success
  • info
  • warning
  • danger

Take a minute to play with these classes and familiarize yourself with them. If you would like to see code examples of each of these classes in use, you can do so here.

Buttons

Looking to spruce up your links? We can handle that. Creating a button out of a text link is as easy as applying the btn class to it. If you have a button and you want to create a text link from it, you simply add the btn-link class as well.

The following classes can be used in conjunction with the btn class to create the button you are looking for:

  • btn-default
  • btn-primary
  • btn-success
  • btn-info
  • btn-warning
  • btn-danger
  • btn-link

You can also change the size of your button by adding one of these classes:

  • btn-lg
  • btn-sm
  • btn-xs

You might notice that medium is left out, that is because medium is the default button size. Simply do not include any of these classes in order to use the default size.

In action

Now for the fun part, let’s create a nice success button. In your app/views/todos/index.html.erb file, add the btn and btn-success classes to the “New Todo” link:

1
2
3
4
5
6
<!-- app/views/todos/index.html.erb -->
...

<%= link_to 'New Todo', new_todo_path, class: 'btn btn-success' %>

...

Next, lets style the Show, Edit, and Destroy links. We will also make these buttons extra small:

1
2
3
4
5
6
7
8
<!-- app/views/todos/index.html.erb -->
...

<td><%= link_to 'Show', todo, class: 'btn btn-primary btn-xs' %></td>
<td><%= link_to 'Edit', edit_todo_path(todo), class: 'btn btn-info btn-xs' %></td>
<td><%= link_to 'Destroy', todo, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger btn-xs' %></td>

...

You can read the official documentation on buttons here.

Glyphicons

It is amazing how much a simple icon can improve the look of a link or button, as well as other components in your ui. You can add icons to your application very easily using Glyphicons thanks to Bootstrap.

Let’s add a + icon to the “New Todo” button.

Change:

1
<%= link_to 'New Todo', new_todo_path, class: 'btn btn-success' %>

to:

1
2
3
<%= link_to new_todo_path, class: 'btn btn-success' do %>
  <i class="glyphicon glyphicon-plus"></i> New Todo
<% end %>

Refresh the page and you will see the icon. As you can see, we added an i element with the glyphicon class to the text of the link. We also included the glyphicon-plus class, which tells Bootstrap which icon to display.

To see all of the icons you have access to, go here.

Forms

There are a ton of helper classes for forms included. We will be going through the basics in this section.

Form structure

The basic structure is very simple. Each label and control pair within the form element are nested inside of a div with the class form-group. The controls are then assigned the class form-control.

Here is an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
<form role="form">
  <div class="form-group">
    <label for="username">Username</label>
    <input type="text" id="username" name="username" class="form-control" />
  </div>
  <div class="form-group">
    <label for="password">Password</label>
    <input type="password" id="password" name="password" class="form-control" />
  </div>

  <input type="submit" value="Login" />
  <input type="button" value="Cancel" />
</form>

This code results in this nicely styled form:

Inline forms

If you want to change your form to be inline instead, it is as easy as adding the form-inline class to your form element:

1
2
3
<form role="form" class="form-inline">
  ...
</form>

The result is this:

Horizontal forms

Another popular form style is the horizontal form, where the label is to the left of the control, but it is right-aligned. This requires a but more work to create, but it is still fairly easy to do. In order to achieve it, we need to dig into the grid knowledge that we learned above.

The label needs to be assigned a number of columns to occupy. The label also needs to be given the class control-label:

1
<label for="username" class="col-sm-2 control-label">Username</label>

The control needs to be nested inside of a div which specifies how many columns the right portion of the form should take up:

1
2
3
<div class="col-sm-10">
  <input type="text" id="username" name="username" class="form-control" />
</div>

Your buttons should be put inside of a div which specifies how many columns they should take up. Generally, this is the same number as the inputs. That div, should then be wrapped with a div with the class form-group. The thing that is different about your buttons, is they have no external label. That means you need to use an offset class to move the buttons in line with the controls.

Here is what the updated form looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<form role="form" class="form-horizontal">
  <div class="form-group">
    <label for="username" class="col-sm-2 control-label">Username</label>
    <div class="col-sm-10">
      <input type="text" id="username" name="username" class="form-control" />
    </div>
  </div>
  <div class="form-group">
    <label for="password" class="col-sm-2 control-label">Password</label>
    <div class="col-sm-10">
      <input type="password" id="password" name="password" class="form-control" />
    </div>
  </div>

  <div class="form-group">
    <div class="col-sm-offset-2 col-sm-10">
      <input type="submit" value="Login" />
      <input type="button" value="Cancel" />
    </div>
  </div>
</form>

And here is the rendered form:

One important thing to notice, we did not apply any column designation to the xs screen size. Because of that, xs screen sizes will not render the form horizontally, rather it will render it exactly like the basic form above.

Controls

Most common input types are supported, including all of the HTML5 types. checkboxes, radio buttons, static text controls, and selects are also supported.

A “Remember me” checkbox can be added by adding the following to our code, above the buttons:

1
2
3
4
5
6
7
8
9
10
<div class="form-group">
  <div class="col-sm-offset-2 col-sm-10">
    <div class="checkbox">
      <label>
        <input type="checkbox" value="1">
          Remember me
      </label>
    </div>
  </div>
</div>

Radio buttons can be added by including:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  <div class="form-group">
    <div class="col-sm-offset-2 col-sm-2">
      <div class="radio">
        <label>
          <input type="radio" name="loginTypeRadios" id="loginTypeExisting" value="existing_user" checked>
          Existing user
        </label>
      </div>
      <div class="radio">
        <label>
          <input type="radio" name="loginTypeRadios" id="loginTypeNew" value="new_user">
          New user
        </label>
      </div>
    </div>
  </div>

Checkboxes and radios can both be disabled by setting the disabled html attribute on the input, as well as assigning the container (which has a class of checkbox or radio) the disabled class.

Another nice feature is that you can change your checkboxes and radios into inline checkboxes and radios simply by replacing the checkbox or radio class on the container, with checkbox-inline or radio-inline respectively.

In order to use a select, you just assign the form-control class to the select element.

There are many more features regarding forms included with Bootstrap. Things like validation states and read-only controls. You can read more here.

Implementing a Bootstrap form in our app

Our form for creating a new Todo is the perfect place to try out Bootstrap forms. We will make this a horizontal form.

Here is our new form template:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!-- app/views/todos/_form.html.erb -->

<%= form_for(@todo, html: {class: 'form-horizontal'}) do |f| %>
  <% if @todo.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@todo.errors.count, "error") %> prohibited this todo from being saved:</h2>

      <ul>
      <% @todo.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field form-group">
    <%= f.label :title, class: 'col-sm-2 control-label' %>
    <div class="col-sm-10">
      <%= f.text_field :title, class: 'form-control' %>
    </div>
  </div>
  <div class="field form-group">
    <%= f.label :notes, class: 'col-sm-2 control-label' %>
    <div class="col-sm-10">
      <%= f.text_area :notes, class: 'form-control' %>
    </div>
  </div>
  <div class="actions form-group">
    <div class="col-sm-offset-2 col-sm-10">
      <%= f.submit %>
    </div>
  </div>
<% end %>

All you have to do in order to see our new form is click on “New Todo” on the app’s home page.

Navigation

One of the key components of a web site or application is navigation. Bootstrap offers navbars, tabs, pills, and lists.

For our app, we will add a navbar and some basic tabs.

Navbar

We will create a partial for our navigation bar:

1
2
3
4
5
6
7
8
9
<!-- app/views/shared/_navigation.html.erb -->

<nav class="navbar navbar-default" role="navigation">
  <div class="container">
    <div class="navbar-header">
      <a class="navbar-brand" href="/">Tealeaf Todo</a>
    </div>
  </div>
</nav>

In our application.html.erb file, we need to render the partial:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- app/views/layouts/application.html.erb -->

<!DOCTYPE html>
<html>
  ...

  <body>

    <%= render 'shared/navigation' %>

    ...
  </body>
</html>

You will now see a nice browser-width bar across the top of your app.

Tabs

We will add the code for our tabs in two places, app/views/todos/index.html.erb and app/views/todos/new.html.erb.

The only difference between the code in these two places is which tab is active. We set that by assigning the active CSS class to the tab we would like to be active.

1
2
3
4
5
6
7
8
<!--  app/views/todos/index.html.erb -->

<ul class="nav nav-tabs" role="tablist">
  <li class="active"><%= link_to 'View list', todos_path %></li>
  <li><%= link_to 'New todo', new_todo_path %></li>
</ul>

...
1
2
3
4
5
6
7
8
9
10
11
12
<!--  app/views/todos/new.html.erb -->

<ul class="nav nav-tabs" role="tablist">
  <li><%= link_to 'View list', todos_path %></li>
  <li class="active"><%= link_to 'New todo', new_todo_path %></li>
</ul>

<h1>New todo</h1>

<%= render 'form' %>

<%= link_to 'Back', todos_path %>

Now if you visit the app in your browser again, you will see that the “New todo” tab takes you to the same place as the “+ New Todo” button. You can also get back to the todos index by clicking on the “View list” tab.

By the way, if you would like to use pills instead of tabs, simply change the nav-tabs class on each ul to nav-pills.

Displaying messages

When your users interact with your app, you need to provide them with feedback to let them know whether their interaction was successful or not. Bootstrap provides alerts for that.

Using alerts in Rails

A common way to interact with alerts is using the Rails flash system. This is a simple thing to do. You simply need to loop through the messages in the flash bucket and display them.

We will add the following the code to app/views/layouts/application.html.erb, its own row in our grid:

1
2
3
<% flash.each do |name, msg| -%>
  <%= content_tag :div, msg, class: "alert alert-#{name}" %>
<% end -%>

Bootstrap alerts require both an alert class as well as one of the following classes:

  • success
  • info
  • warning
  • danger

Our new app/views/layouts/application.html.erb looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!-- app/views/layouts/application.html.erb -->

<!DOCTYPE html>
<html>
  <head>
    <title>BootstrapBlogSeriesTodoApp</title>
    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
    <%= csrf_meta_tags %>
  </head>
  <body>

    <%= render 'shared/navigation' %>

    <div id="main-container" class="container">
      <div class="row">
        <div class="col-xs-12">
          <% flash.each do |name, msg| -%>
            <%= content_tag :div, msg, class: "alert alert-#{name}" %>
          <% end -%>
        </div>
      </div>
      <div class="row">
        <div class="col-xs-3">
          <h3>Tealeaf Academy Todo</h3>
        </div>
        <div class="col-xs-9">
          <%= yield %>
        </div>
      </div>
    </div>

  </body>
</html>

Success alerts

Now we will change our controller to use the proper flash hash keys to work with our alerts. We will start with the success message when a todo is created.

Our changes will be made in app/controllers/todos_controller.rb. Look for the following three lines:

1
format.html { redirect_to @todo, notice: 'Todo was successfully created.' }
1
format.html { redirect_to @todo, notice: 'Todo was successfully updated.' }
1
format.html { redirect_to todos_url, notice: 'Todo was successfully destroyed.' }

Currently, our app is using the notice hash key in the flash hash (which is stored in the session, incidentally). Rails provides notice and error flash key helpers, but since we need to use the success key we will have to move our flash message out of the redirect_to method call.

old code:

1
2
if @todo.save
  format.html { redirect_to @todo, notice: 'Todo was successfully created.' }

new code:

1
2
3
if @todo.save
  flash[:success] = 'Todo was successfully created.'
  format.html { redirect_to @todo }

old code:

1
2
if @todo.update(todo_params)
  format.html { redirect_to @todo, notice: 'Todo was successfully updated.' }

new code:

1
2
3
if @todo.update(todo_params)
  flash[:success] = 'Todo was successfully updated.'
  format.html { redirect_to @todo }

old code:

1
2
respond_to do |format|
  format.html { redirect_to todos_url, notice: 'Todo was successfully destroyed.' }

new code:

1
2
3
respond_to do |format|
  flash[:success] = 'Todo was successfully destroyed.'
  format.html { redirect_to todos_url }

Now if you create, edit, or destroy a todo, you will see a nice green alert at the top of the page!

Failure alerts

Now we need to display an error if there is a failure in our controller. Similar to the success alerts, we need to add messages to the danger (the bootstrap class for red alerts) hash key in the flash hash.

Here are the final controller actions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def create
  @todo = Todo.new(todo_params)

  respond_to do |format|
    if @todo.save
      flash[:success] = 'Todo was successfully created.'
      format.html { redirect_to @todo }
      format.json { render :show, status: :created, location: @todo }
    else
      flash[:danger] = 'There was a problem creating the Todo.'
      format.html { render :new }
      format.json { render json: @todo.errors, status: :unprocessable_entity }
    end
  end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
def update
  respond_to do |format|
    if @todo.update(todo_params)
      flash[:success] = 'Todo was successfully updated.'
      format.html { redirect_to @todo }
      format.json { render :show, status: :ok, location: @todo }
    else
      flash[:danger] = 'There was a problem updating the Todo.'
      format.html { render :edit }
      format.json { render json: @todo.errors, status: :unprocessable_entity }
    end
  end
end

You now have a nice, presentable way to notify the users. Don’t forget, you simply change success and danger to one of the classes mentioned above to change the color of the alert.

Onward

Join us in part 3 of this series to learn how to add some of the javascript features of Bootstrap to your Rails app.