Adding a Navigation Bar
In this demonstration, I will show how to implement a navigation bar using the Bootstrap library. We will continue to build upon the QuizMe project from the previous demos.
In particular, we will add a Bootstrap navbar (short for navigation bar) to the QuizMe app’s pages. The navbar will provide hyperlinks to the apps main pages (e.g., the Welcome and About pages) as well as the sign-in/sign-out links, as depicted in Figure 1.
Adding the navbar will involve three main tasks:
- Add a placeholder navbar by pasting in template code from the Yeti Bootswatch Theme website.
- Customize the template navbar code to display hyperlinks to the QuizMe app’s pages.
- Update the navbar to conditionally format the link to the currently displayed (or active) page so that the link to the active page looks different from the other links.
1. Adding a Template Navbar to the App
Start by copying the example code for the blue navbar found on the Yeti Bootswatch Theme webpage){:target=”_blank”}. If you open the page in the Google Chrome browser, you can then open the HTML code for the page by clicking View
> Developer
> View Source
. To locate the template navbar code, search for the string “Navbars
” on the page (Edit
> Find
> Find...
), which is a string found only in the heading for the Navbars section.
Paste the Yeti navbar template code into the app/views/layouts/application.html.erb
at the top of the body
element, like this:
<nav class="navbar navbar-expand-md navbar-dark bg-primary">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarColor01" aria-controls="navbarColor01" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarColor01">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Features</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Pricing</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">About</a>
</li>
</ul>
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="text" placeholder="Search">
<button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button>
</form>
</div>
</nav>
Verify that this code displays correctly by running the app and opening http://localhost:3000 in the browser. The navbar should appear on all the app’s pages.
2. Migrating the QuizMe Navigation Links into the Navbar
For this task, we will migrate the existing list of links at the top of our pages (i.e., the ones in the ul
element) into the newly added navbar.
Change the navbar-brand
a
element to be a hyperlink to the QuizMe root page, like this:
<%= link_to 'QuizMe', root_path, class: "navbar-brand" %>
Replace the links in the template’s ul
element with our actual Home, About, etc. links, like this:
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<%= link_to 'Home', welcome_path, class: 'nav-link' %>
</li>
<li class="nav-item">
<%= link_to 'About', about_path, class: 'nav-link' %>
</li>
<li class="nav-item">
<%= link_to 'Contact', contact_path, class: 'nav-link' %>
</li>
<% if user_signed_in? %>
<li class="nav-item">
<%= link_to 'Quizzes', quizzes_path, class: 'nav-link' %>
</li>
<li class="nav-item">
<%= link_to 'My Quizzes', account_quizzes_path, class: 'nav-link' %>
</li>
<% end %>
</ul>
Replace the template’s form
element with another ul
element that contains the Devise links, like this:
<ul class="navbar-nav">
<% if user_signed_in? %>
<li class="nav-item">
<%= link_to "Hi, #{current_user.email}", edit_user_registration_path, class: 'nav-link' %>
</li>
<li class="nav-item">
<%= link_to 'Sign Out', destroy_user_session_path, method: :delete, class: 'nav-link' %>
</li>
<% else %>
<li class="nav-item">
<%= link_to 'Sign In', new_user_session_path, class: 'nav-link' %>
</li>
<li class="nav-item">
<%= link_to 'Sign Up', new_user_registration_path, class: 'nav-link' %>
</li>
<% end %>
</ul>
Delete the old navigation links in app/views/static_pages/welcome.html.erb
and the old Devise links in app/views/layouts/application.html.erb
.
Verify that the above links display and work correctly by reloading the app and testing it out.
3. Conditionally Highlighting the Active Navigation Link
In the above code, the nav-item
element for the Home link has an additional active
class; however, this is incorrect and needs to be fixed. The intent of the active
class is to highlight the nav link for the currently open page. However, to achieve this behavior, the active
class must be applied to the nav-item
element that corresponds to the current page. The above code is broken in that the Home nav-item
element is always styled as active
regardless of which page is actually open.
To fix this problem, we will conditionally add the active
class to the appropriate nav-item
element for the current page by making the following changes.
Add a helper method active_class
to the ApplicationHelper
module in app/helpers/application_helper.rb
that returns the string 'active'
if the path of the current HTTP request matches a path
parameter and that otherwise returns an empty string (''
), like this:
def active_class(path)
if request.path == path
return 'active'
else
return ''
end
end
Use this helper method in the navbar view code to add the active
class to the appropriate nav-item
element, like this:
<ul class="navbar-nav mr-auto">
<li class="nav-item <%= active_class(welcome_path) %>">
<%= link_to 'Home', welcome_path, class: 'nav-link' %>
</li>
<li class="nav-item <%= active_class(about_path) %>">
<%= link_to 'About', about_path, class: 'nav-link' %>
</li>
<% if user_signed_in? %>
<li class="nav-item <%= active_class(contact_path) %>">
<%= link_to 'Contact', contact_path, class: 'nav-link' %>
</li>
<li class="nav-item <%= active_class(quizzes_path) %>">
<%= link_to 'Quizzes', quizzes_path, class: 'nav-link' %>
</li>
<li class="nav-item <%= active_class(account_quizzes_path) %>">
<%= link_to 'My Quizzes', account_quizzes_path, class: 'nav-link' %>
</li>
<% end %>
</ul>
<ul class="navbar-nav">
<% if user_signed_in? %>
<li class="nav-item <%= active_class(edit_user_registration_path) %>">
<%= link_to "Hi, #{current_user.email}", edit_user_registration_path, class: 'nav-link' %>
</li>
<li class="nav-item <%= active_class(destroy_user_session_path) %>">
<%= link_to 'Sign Out', destroy_user_session_path, method: :delete, class: 'nav-link' %>
</li>
<% else %>
<li class="nav-item <%= active_class(new_user_session_path) %>">
<%= link_to 'Sign In', new_user_session_path, class: 'nav-link' %>
</li>
<li class="nav-item <%= active_class(new_user_registration_path) %>">
<%= link_to 'Sign Up', new_user_registration_path, class: 'nav-link' %>
</li>
<% end %>
</ul>
Note that, in the above code, we have added an embedded Ruby call to the active_class
method in the class
attribute string for each of the the li
elements. The argument passed to each call of active_class
is the path
route helper for the corresponding link destination page.
Verify that the above active
formatting works correctly by reloading the app and testing out the links.
The app now has a fully functioning navbar. In the upcoming demos, we will add additional UI elements and styling to the page bodies of the app.