Over a million developers have joined DZone.

Creating Navigation Bars With Current Section Highlight In Rails

·
UPDATE: I have since packaged this functionality up as a Rails plugin - more information can be found at http://opensource.agileevolved.com

*THE CODE BELOW HAS BEEN DISCOVERED TO HAVE PROBLEMS RUNNING IN PRODUCTION MODE - THIS HAS BEEN FIXED IN THE PLUGIN VERSION OF THIS SNIPPET. PLEASE SEE THE URL ABOVE*

There are many ways of creating navigation bars using XHTML and CSS but the most common method is to create an unordered list of links which are then formatted in a variety of ways using CSS. There are also many ways of highlighting the tab for the current page, some using server-side logic, some using CSS.

This method enables you to create as many navigation lists as you like in your Rails views, giving each navigation list a unique ID which is then referenced using a simple macro in your controller to set the highlighted link.

First of all, we have the main helper function that lets us create our navigation lists - add the following to your application helper:


def navigation_list(links, options={})
  link_html = ''
  links.each do |link|
    class_name = 'navlink'
    class_name << ' current' if current_section(options[:id], link[:text])
    link_html << content_tag('li', link_to(link[:text], link[:url]),
      :id => "nav_#{link[:text].rubify}", :class => class_name)
  end
  content_tag('ul', link_html, :id => "navbar_#{options[:id]}" || 'navigation')
end

private
  def current_section(navigation_id, link_text)
    link_name = link_text.rubify.to_sym
    navigation_id = navigation_id.rubify.to_sym
    return true if (@controller.current_section?(navigation_id, link_name))
  end


As a small aside, you'll notice the above code relies on a small extension to the Ruby String class - create a file called string.rb to your Rails lib/ folder and add the following monkey patch:


class String 
  def rubify
    self.downcase.strip.gsub(' ', '_')
  end
end


Some of the above functionality will become apparent later, but essentially what it does is take an array of links in a specific format (see below) and turn them into an unordered list of links. The function takes an optional :id parameter (which otherwise defaults to 'navigation') which is used as both the HTML ID on the UL element and as an identifier when using the navigation mappings functionality below. Each LI element is given a class of 'navlink' and also a class of 'current' if it is the current link (see below). This provides plenty of hooks for styling in your CSS.

Here is an example usage of the above function:


<%=	navigation_list [
		{ :text => 'Dashboard', :url => { :controller => 'dashboard' },
		{ :text => 'Admin', :url => { :controller => 'admin', :action => 'login' } }
	], :id => 'main_nav' %>


The :text key is the actual link text, and the :url key is the standard set of Rails url_for options. You can create as many of these navigation lists on a page as you want - just be sure to give each one a unique ID.

The other step is to create a way of storing your "navigation mappings" - a hash of navigation list to current link pairs. Add the following to your ApplicationController:


@@navigation_mappings = {}

def self.navigation_section(mapping)
  @@navigation_mappings.merge!(mapping)
end
  
def current_section?(navigation_id, link_name)
  return (@@navigation_mappings[navigation_id] == link_name)
end


You can use the navigation_section macro to specify which link to set as current for a particular navigation list. Using the example navigation list above, if we wanted to set the Dashboard link as current for all the actions in our DashboardController, we would add the following to our controller code:


class DashboardController < ApplicationController
  navigation_section :main_nav => :dashboard
end


Again, you can add as many navigation_section declarations to your controller if you have many navigation lists on your page that you want to control. The key is the ID of the navigation list and the value is a symbol version of the particular link text - for example, 'This is a link' would become :this_is_a_link.

Adding this mapping will add a 'current' class to the LI element for that particular link - you can now use this in your stylesheet to style the current page link appropriately.
Topics:

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}