Integrating Twitter Bootstrap 4 with Rails 5

In this article, we will develop a movie database webapp using Twitter Bootstrap 4 and Rails 5. I upgraded the Movie Review app developed by Mackenzie Child using Twitter Bootstrap 3.2 and Rails 4.1 to the current versions of Twitter Bootstrap and Rails. You may also be interested in How to integrate Twitter Bootstrap 3.2 with Rails 4.2. You can watch this tutorial as a screencast Integrating Twitter Bootstrap 4 with Rails 5.

Setup Movies Rails 5 App

To install Rails 5 RC1, run:

gem install rails --pre

Generate a new Rails 5 project and generate a movie scaffold.

rails g scaffold movie title duration director rating description:text

Migrate the database.

rails db:migrate

Install imagemagick. On Mac:

brew install imagemagick
Warning: imagemagick-6.9.2-4 already installed

Add paperclip gem to Gemfile.

gem "paperclip", "~> 5.0.0.beta1"

Run bundle. Add the paperclip methods to the movie model:

has_attached_file :poster, styles: { medium: "400x600#" }
validates_attachment_content_type :poster, content_type: /\Aimage\/.*\Z/

You can upload a poster for a movie. Use the paperclip generator to create the file upload fields for movies table.

rails g paperclip movie poster

The generated migration will add:

t.string   "poster_file_name"
t.string   "poster_content_type"
t.integer  "poster_file_size"
t.datetime "poster_updated_at"

to the movies table. Migrate the database.

rails db:migrate

Change the scaffold generated movie form partial to add multipart and file upload field:

<%= form_for(movie, html: { multipart: true }) do |f| %>
  ... same as before
  <div class="field">
    <%= f.label :image %>
    <%= f.file_field :image %>
  </div>
  ... same as before

Change the movie_params method to allow the poster to be uploaded.

def movie_params
  params.require(:movie).permit(:title, :duration, :director, :rating, :description, :poster)
end

Poster will not show up. You need to use the same name you used in the movie model. Change the view:

  <div class="field">
    <%= f.label :poster %>
    <%= f.file_field :poster %>
  </div>

Display the movie poster in the movie show page.

<%= image_tag @movie.poster.url(:medium) %>

Now you can see the uploaded movie poster. Add:

/public/system/movies/posters

to .gitignore. We don't want to upload images to git.

Integrate Twitter Bootstrap 4 with Rails 5

Add bootstrap gem to Gemfile.

gem 'bootstrap', '~> 4.0.0.alpha3'

Run bundle. Add:

@import "bootstrap";

to application.css. Rename application.css to application.scss. Remove:

*= require_tree .
*= require_self

from application.scss. Use @import to import sass files. Add:

//= require bootstrap

to application.js. It will now look like this:

//= require jquery
//= require bootstrap
//= require jquery_ujs
//= require turbolinks
//= require_tree .

Note: If you use bootstrap-sprockets, you will get the error:

File to import not found or unreadable: bootstrap-sprockets

Only for Twitter Bootstrap 3, bootstrap-sprockets is used. Create a header partial in app/views/layout folder, _header.html.erb.

<nav class="navbar navbar-dark bg-inverse">
  <%= link_to "Movie Reviews", root_path, class: "navbar-brand" %>
  <ul class="nav navbar-nav">

    <li class="nav-item active">
      <a class="nav-link" href="movies/new">New Movie <span class="sr-only">(current)</span></a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#">About</a>
    </li>
  </ul>
  <form class="form-inline pull-xs-right">
    <input class="form-control" type="text" placeholder="Search">
    <button class="btn btn-success-outline" type="submit">Search</button>
  </form>
</nav>

Define the resources in the routes.rb.

Rails.application.routes.draw do
  resources :movies do
    collection do
        get 'search'
    end
  end
  root 'movies#index'
end

The layout file displays the header partial for the navigation and displays the content in a container.

<!DOCTYPE html>
<html>
  <head>
    <title>Film Buff</title>
    <%= csrf_meta_tags %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <%= render 'layouts/header' %>
    <div class="container">
        <% flash.each do |name, msg| %>
            <%= content_tag(:div, msg, class: "alert alert-info") %>
        <% end %>
        <%= yield %>
    </div>
  </body>
</html>

Change the movies index page.

<div class="row">
  <% @movies.each do |movie| %>
    <div class="col-sm-6 col-md-3">
      <div class="thumbnail">
        <%= link_to (image_tag movie.poster.url(:medium), class: 'image'), movie %>
      </div>
    </div>
  <% end %>
</div>

You can now attach a movie poster when you create a movie. You can see all the movies in the home page. We now have the following issues:

  1. The top portion of the movie poster is hidden behind the navigation.
  2. The movie posters on the home page overlap.

To fix the overlap problem, change the css class used for the image.

<div class="row">
  <% @movies.each do |movie| %>
    <div class="col-sm-6 col-md-5">
      <div class="thumbnail">
        <%= link_to (image_tag movie.poster.url(:medium), class: 'image'), movie %>
      </div>
      <hr/>
    </div>
  <% end %>
</div>

Refer the Twitter Bootstrap Grid System in the resources section of this article to learn more. To fix the navigation bar hiding the poster, add navbar-fixed-top to the header partial. It now looks like this:

<nav class="navbar navbar-dark bg-inverse navbar-fixed-top">
  <%= link_to "Movie Reviews", root_path, class: "navbar-brand" %>
  <ul class="nav navbar-nav">

    <li class="nav-item active">
      <a class="nav-link" href="movies/new">New Movie <span class="sr-only">(current)</span></a>
    </li>
    <li class="nav-item">
      <a class="nav-link" href="#">About</a>
    </li>
  </ul>
  <form class="form-inline pull-xs-right">
    <input class="form-control" type="text" placeholder="Search">
    <button class="btn btn-success-outline" type="submit">Search</button>
  </form>
</nav>

We now have a simple movies database that can show all the movies in a nice grid and if you click on the movie poster, it will show the details of the movie. We have the following in our to do list.

  1. The forms are not styled using Twitter Bootstrap 4.
  2. The active class is hard coded in the header partial for showing the current active tab.

In order to select the current tab as the active tab, we need to make it dynamic by using a Rails helper. We will tackle these two issues in the next part of this series.

Twitter Bootstrap Upgrade Tip

You can convert a given Twitter Bootstrap 3.x class to 4.x by using Bootply. However, be careful, sometimes they old classes are not required at all.

Summary

In this article, you learned how to integrate Twitter Bootstrap 4 with Rails 5 apps.

References


Related Articles

Watch this Article as Screencast

You can watch this as a screencast Integrating Twitter Bootstrap 4 with Rails 5


Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.