In most web applications, breadcrumbs offer a nice way to trace back to previously visited pages. However, generating the breadcrumbs and showing them on dynamic pages sometimes need special care for the following reasons-
- The links of the breadcrumb may contain dynamic links.
- It is used in all the pages. So, changing the breadcrumb may trigger a series of changes.
In ScrumPad, we are using ruby on rails. There are a few plugins to help rails projects in managing breadcrumbs, but none seemed to satisfy our need. In fact, most projects come up with some distinctive ways to generate the breadcrumbs. However, we kept the breadcrumb clean in the following way in ScrumPad.
Step#1:
Encapsulated the breadcrumb generation logic into a single class and used it from all places.
class Breadcrumb
include ActionView::Helpers::UrlHelper
include ActionView::Helpers::TagHelper
include ApplicationHelper
attr_accessor :parts, :separator, :prefix
def initialize(separator = nil, parts = [])
self.separator = separator || "<img src="\"/images/stub_arrow_right.gif\"" />"
self.parts = parts
end
def add(title, url = nil)
self.parts << {:title => title, :url => url}
end
def set_prefix(title, url)
self.prefix = {:title => title, :url => url}
end
def to_s
if(!self.parts.nil? and !self.parts.empty?)
if(self.prefix.nil?)
parts_with_prefix = self.parts
else
parts_with_prefix = [self.prefix] + self.parts
end
breadcrumb_html = []
parts_with_prefix.each do |part|
if(part[:url].nil? or part[:url].empty?)
breadcrumb_html << "#{part[:title]}"
else
breadcrumb_html << link_to(part[:title], part[:url])
end
end
return breadcrumb_html.join(" #{self.separator} ")
end
return ''
end
end
Step#2:
A before_filter in the ActionController initializes the breadcrumb in the following way:-
def initialize_breadcrumb()
@breadcrumb = Breadcrumb.new
if(project_selected?)
@breadcrumb.prefix = {:title => "Dashboard", :url => dashboard_project_path(selected_project())}
end
end
Step#3:
Now, @breadcrumb is available to all actions. However, if the RESTful resource definition is right, the rest of the breadcrumb generation becomes really simple. We have methods like the following in our controllers-
def generate_breadcrumb(sprint, story = nil)
@breadcrumb.add(sprint.name, project_sprint_path(project.id, sprint.id))
if(!story.nil?)
@breadcrumb.add(story.display_title, story_path(story))
elsif(self.action_name != 'index')
@breadcrumb.add('Stories', stories_path())
end
current_page = self.action_name == 'index' ? 'Stories' : self.action_name.humanize
@breadcrumb.add(current_page)
end
Step#4:
Finally, in my view layout, I just use this instance variable as shown here:-
<%= @breadcrumb %>
So, the whole breadcrumb generation is encapsulated and the implementation is clean. Let me know if you liked it. I am in the process of creating a plugin so that anyone can just drop it in any rails app and start using straight away!