How to Build a View Helper to Dynamically Generate a Navigation Bar in Rails
Our application is looking great. However the navigation elements have quite a bit of duplicate code, which violates a core Rails development practice. In this guide we'll walk through how to build a view helper method that dynamically generates the navigation bar based on which page is being viewed. Additionally, we'll build a helper method that adds the `active` status to navigation elements when a user is on a specific page.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

I absolutely love the way that our application is looking it is a portfolio and have no problem saying that I want it to be my own portfolio. I think this is coming along nicely.

The code, for the most part, looks good. But one thing that I usually do after a big section and this section has been pretty massive. We've already gone through 20 Lessons in order to implement this design for the various layouts. One thing that I do at the very end is I go back and I see how I could clean up my code. This is something that is important when you're working on your own projects but it's also very important when you're working in real life scenarios. Say that you have just built out a huge feature and you have all kinds of new code elements that are added to your applications code base. A very good practice is to cycle back take a look at what you did and then see how you can clean it up.

Whenever you follow that kind of practice you'll find that it becomes much easier later on to add new features and it makes your code base cleaner and more scalable. With that in mind, I've gone through the code base and what I think is the main item that needs to be refactored is the set of our navigation items. Right now this looks fine, as far as what a user sees this looks fine. We have the navigation items working here. If you click on blogs then you have this working. Then if you click on portfolio you have another set of them.

Theoretically, this seems like it's fine except watch what happens if I open up Sublime Text. Coming down to shared let's say that I want to come to our application nav and I want to change this. Instead of saying blog, I want to say blogs. Now if I come back here and if I go to Home now it says blogs but if I actually go to blogs It only says blog and if I go to Portfolio, portfolio says blog I would have to go and make three different changes in every spot and that is a really bad way of doing it. This would be a considered a poor practice in a real-life application. Let's see how we can do it.

Because whenever you're looking at a refactor like this usually you have to take a balance and you say ok, is it worth the time and the added complexity of being able to create a module that could handle all of this. Because as you may look at these, yes the names are the same but everything else is different. Look how much different our navigation looks here, compared with here, compared with here. Completely different, they're wrapped differently, they have different styles associated with them, they have different behavior. If you come to the template you see that our active status here is a little triangle. Our active status here, the color is different and it has a thick border underneath it. Then here, if you go and reference a template. I don't have to because I know this is what looks like there is no active status.

This seems like it would be impossible and it seems like you would spend more time building out some kind of system that could manage all of this then to just say oh I can just make three changes but in all reality it's not it's going to take that much time to do it. The other thing is we're going to be able to learn some really cool stuff when it comes to how we can share content and how we can share code across even different layouts that have completely different styles so I think this is going to be a really good exercise when it comes to performing a refactor.

Let's come here and I'm going to open up all of our new navs. We have our application nav, we have our blog nav, and we have our portfolio nav. Then lastly we also have our old nav. These are our nav components. One thing I looked at the code base and I think that it's perfectly fine to keep all these files. I am good when it comes to having an application nav partial, a blog nav partial and a portfolio nav partial. I think that there is enough differentiation here with things like having this animated slider compared with having different types of class names. I think that that's perfectly fine and we'd be adding too much complexity to try to turn this into one kind of shared nav. I am good with that.

My problem is that we have nearly identical duplicated code in each file. That is what I want to replace, I want to be able to take all of this down and I want to be able to drill this down into a single line of code and we're going to use a view helper in order to accomplish it. I'm going to leave the login helper by itself because one it's working perfectly fine. Also, we already have a centralized place that we can go and change it. There's no reason to remove that. I know that was a bit of an intro but I think it was worth it because I think it's an important thought process. You shouldn't just try to refactor things just for the heck of it. You should be smart you should take all of the different variables into account and then say ok, this is what I want to clean up, and this is a way to structure the code base.

I'm going to open up our application_helper.rb because this is going to be something that is going to be available to the entire application. I'm going to start off creating what in my mind is probably the most naive type of implementation for this. I'm going to create a nav_helper.

  def nav_helper style, tag_type
nav_links = <<NAV 

NAV
  end

I'm not even going to. Well, actually we're going to have to pass in a couple arguments we're going to have to pass in let's say a style, we're also going to have to pass in a tag_type. These are completely arbitrary these are simply argument names. You could name them whatever you want. But let's talk about what they're going to be and why we need them. The style is going to be the CSS style. This is needed because if you look at each one of these items what we have to do is figure out what makes these different. Our application uses the class of nav_link. Each one of these are just links by themselves that have a class of nav_link. This is completely identical, technically we wouldn't even have to pass in a class if we were only using these two. But because we also have this nav, this is what throws in a lot of complexity. But at the end of the day, I'm glad it did because it's going to teach us a lot about what we can do to implement a shared helper.

Here this has a completely different class. It has a class of text white and not only that but it's also wrapped inside of this . That is going to make things a little bit more challenging because if you think about it right here we could essentially just create a series of links and that's pretty nice and easy but right here we have to create a series of links with each one of them being nested inside of an tag and inside of those they all have unique classes. What this tells me is that our nav_helper needs to know what the style is which we're going to treat style as we did up here. So styles going to be what CSS class is going to be used. Then it also needs to know the tag type. In this case the tag type is going to be and in this case, we are going to have no tag. But, what we could do. There's a couple of different options. I'm going to simply make these all spans. That's something that I kind of would want to do anyway so that we just don't have the links just hanging out here all by themselves. We have spans and that gives us a little bit more of a specific selector if we ever want to add any other items any other classes or styles in the future. What we will do with our helper is pass in a style and a tag type.

Now that we have that let's actually create what is called a here doc. If you've never heard of a heredoc they are a little bit different. I'm going to open up the terminal here and I'm going to open up a pry session because I think it helps to take a look at exactly what this is if you've never used it. What a heredoc is, is it allows us to have multi-line strings. Let's say that you have a variable just called a short_str right here and you could put anything you want inside of this. The only problem is it becomes a little bit cumbersome whenever you want to have multiple lines of this string. You'd have to do things like put in new line characters and that would throw a huge error for generating HTML. What we're going to do is we are actually going to take a different approach and we are going to use what's called a here doc and the syntax for this is and we're going to store it in a variable. I'm going to say...

large

something like nav_links. Then equals and the heredoc syntax looks a little bit weird. You give two shovels or less than symbols. I like to call them the shovel operators followed by the heredoc identifier. In this case, it's nav, we could say to HTML we could say whatever it really doesn't matter. All that matters is that we give it a way of closing eventually. The cool thing with this is now if I hit this you can see what pry does it takes us all the way down to the left. Now we can type whatever we want for as many lines as we want. You could go for hundreds of lines thousands of lines. It really doesn't matter. All that matters is at some point you end and say NAV. What this tells the Ruby parser is that, this right here is the end of the string. If I hit return you can see that it generated this full set of items and now if I type in nav_links it'll print them out. It treats it like a multi-line string. We don't have to worry about placing these in manually and then parsing and doing all of that kind of thing so we're going to follow this syntax right here.

I should say we're initially going to do it and I already have plans for how we're going to refactor what we're refactoring and I'm doing this for a very specific reason. If you're going through this course in my mind you want to know Rails inside and out. So I feel like it's my job to show you every single potential thing you could do with it. No course could ever do that, but my goal is to do that as well as humanly possible and to give you a really comprehensive view. So you've seen how to implement this navigation manually on each page. Now I'm going to show you how to implement it using a giant string and we're going to finish off by implementing a really clean elegant solution using a Ruby data structures. But I want you to see all the different versions because you never know with your application. Each one is going to work a little bit differently for you.

Now I'm off of my soapbox. I am going to show you how to implement a heredoc. Ruby parses heredocs very weirdly. The way that it is even if you're in a method and this method is indented this is one spot where it is very important to use the right kind of indentation and the indentation looks weird it's part of the reason why we're going to eventually refactor it because I don't like this. But your cursor has to be all the way to the left. This is going to be the start of it.

We're going to say nav_links = <<NAV. Then here at the very end, we'll say NAV again. Inside of this, this is where we can put our HTML. I'm going to start off and the way you can think about this and having these heredocs is that these are giant double quotation marks. What that tells me is that in order to insert Ruby inside which we need to get to, our style and our tag type variables into these arguments. What that tells me is in order to do that we need to use string interpolation just like this "#{ }". Even if you've never seen this kind of way of storing strings this is the way that we're going to accomplish it.

Coming down the very first thing that we need to do if we remember back is we need to set up our tags. The first thing is going to be our tag type and we're going to slide this in using string interpolation. Just like this <"#{tag_type}">, so if this is a span tag that means that this is going to get passed in and that's going to be a if it is an tag like we have right here. These are going to become 'li'. These are going to become list items that is the first part.

Next is going to be a link. Say <a href = "#{root_path}" class="" Close off our string interpellation. Then after that we are going to have a class so we're going to pass in whatever classes and inside of this we're going to pass in our style argument. After that, we can simply close this off. Now I can pass in whatever the title is. For this it's going to be Home and close off our and then we're going to close off our tag. Here we can pass in tag_type. Close that off and that is our first item.

  def nav_helper style, tag_type
nav_links = <<NAV
<#{tag_type}><a href="#{root_path}" class="#{style}">Home</a></#tag_type}>
NAV
  end

I'm just going to create two of these before we go and test this out. You say about_me and then here we'll say about me and then what we have to do at the end of this is we have to tell it that it's HTML_safe. I'm going to return nav_links.html_safe. Whenever we call nav helper then this is what's going to be returned but it's going to generate the HTML for us, so nav_helper style first tag type after that. Let's test this out in our home section. I'm going to grab all of this and delete it. Pass in our nav_helper. The first item I have already forgotten is the style. Ok, we pass this in first and then after that, the second argument is the tag type and for these, it's going to be . Then we can close this off. Let's go to take a look and see if this is what we want. If I hit refresh. Look at that. We have Home and About Me.

large

Now don't worry because they're butted up against each other we're going to add a custom style that will spread these out nicely. But this is working. Right here you can see this has our home and then it has the About Me section. This is looking really good. Let's go and let's actually go and populate this to our other partials. We can come here and save. We can come here and do the same thing except now we need to pass in 'text-white', follow this up with 'li' so that it knows what tags to create on each side. If I come here hit refresh everything's still good. If I come up to blogs everything here is working perfectly and you can see right here we don't even need to pass in any additional margin or padding or anything so that's good.

Let's go check out our portfolios. Go to portfolios come up here, look at that, that is all working. Very cool, this is looking much better already. If we make one change we only need to come to our application_helper and make that change right here. Let's populate a few more of these. We're going to have, contact, blog, and portfolios. We can come here and make each of these changes. Contact then change the text itself, for contact. Then blogs, change blogs right here, you could just say blog. Then portfolios and change portfolio here, hit save and let's come back and see if this is working. No errors, that's always a good start. Look at this, it's all working very nicely! You click About Me. These are all up here. Click on blog everything is here. This is working beautifully.

Now let's kind of spread this out a little bit at least for this application_helper. I think one of the best ways would be to simply come into the parent and create a new class. I'm going to create a class called app-nav. We can open up our application.css file. Scroll all the way down and we only need to add this class here because it looks like our blog is working perfectly just with span tags. So, app-nav and we also have to select any spans inside of this. Here we can just say margin-left: 15px I think we can do a margin-right:15 pixels. If I hit save there and come back to home, hit refresh.

There we go look at this.

large

This is nice and spread out and feel free to adjust those margins however you want. I think this works fine for what I am looking for, for my own portfolio. But look this is coming along very nicely.

Ok, before we go into our final refactor where we clean all of this up. When we come in here I'm going to actually show you how we don't even need any of this and how there is even a cleaner way of doing it. But before we get into that side of it let's talk about how we can make those statuses active. If you come here this is one of the templates or if you come here and you right click and click inspect you will see if you come down here the reason why this is white and why it has a different style is because it has active as a class. If you hit inspect right here you can see that this is just a nav_link. It doesn't have active. Essentially what we have to do is we have to figure out a way of inside of this class to make this add the word active. If I hit save and come back here. Come to our site you can see that this is active. I hardcoded that in, it's still active even when I'm right here. This is not our final solution but this does give us a really good hint on what we have to do in order to implement it. We need to be able to have the ability to know what page we're on in the application and then simply call it and add active if the page right here that we're on matches the page from the parameters. There is a very clean way of doing this and we're going to create another method that's going to handle it for us. I'm going to say...

  def nav_helper style, tag_type
nav_links = <<NAV
  <#{tag_type}><a href="#{root_path}" class="#{style} #{active? root_path}">Home</a></#{tag_type}>
  <#{tag_type}><a href="#{about_me_path}" class="#{style} #{active? about_me_path}">About Me</a></#{tag_type}>
  <#{tag_type}><a href="#{contact_path}" class="#{style} #{active? contact_path}">Contact</a></#{tag_type}>
  <#{tag_type}><a href="#{blogs_path}" class="#{style} #{active? blogs_path}">Blog</a></#{tag_type}>
  <#{tag_type}><a href="#{portfolios_path}" class="#{style} #{active? portfolios_path}">Portfolio</a></#{tag_type}>
NAV

    nav_links.html_safe
  end

  def active? path
    "active" if current_page? path
  end

def active this is going to be a boolean method so it's going to either return true or false. It's going to take a path as the argument and inside we just need a single line of code. We're going to say active which is the same as saying return active if and this is a special method inside of Rails called current page? and path. Essentially what this is going to do is it's going to say return active right here if the current page (which is a Rail's method) matches whatever path we passed in. In other words, if we pass in the root_path then it's going to return active. If not it's just going to return false. It's going to return nil and then nothing gets passed in and that's exactly what we want. All we have to do is now call this method. So, inside of the double quotations. Use string interpolation. We're going to say active and then pass in whatever the current path is we know that home page is root_path so you'll essentially just be populating this in each side. I'm going to just copy all of this including the space and now I can come select each one of these items, hit paste. Very important, this is before the end of the double quotation marks or else this will not work. It has to be encapsulated inside and it also needs a space between style and just that you need that in order for it to work. Go and populate each one of these. Technically, you don't really need this for the portfolios but I'm going do it just so all of them match. If I come back hit refresh. We're on the home page and it says home. Now the moment of truth if I click 'About Me' look at that. Now, these items are all active and even look at this, it switches and in a different layout it also works. Portfolio never had that so, that is totally fine, portfolio shouldn't change. Click home, we are back. This is working beautifully. Ok, all of this is fantastic. I'm very excited about it. I think this is looking very nice but are we at a spot where we want to stop. I don't think we're quite there. If I were to open up someone's codebase and I saw something like this. I would know what they were going for. But my first thought would probably be was there a better way of doing this because I see a lot of duplicate code look at this we have all of these hrefs. We have five hrefs and we have all of these tags and these classes. The only thing that really seems to be different is a few items. You know whenever I see something like this my first thought is could this have been handled in some type of a data structure or something and then simply generated on the fly. That is what I think the very best approach is going to be. So the next thing that we're going to do. Is we're going to create a list of our nav items.

def nav_items
  [
    {
      url: root_path
      title: 'Home'
    },
    {
      url: about_me_path
      title: 'About Me'
    },
    {
      url: contact_path
      title: 'Contact'
    },
    {
      url: blogs_path
      title: 'Blog'
    },
    {
      url: portfolios_path
      title: 'Portfolio'
    },
  ]
end

I'm going to say def nav_items. This is going to return an array and it's actually going to return an array of hashes. Our hash can be created just like this and inside of our hash we can create a set of key-value pairs. This is going to have all of the items that make each element different. Here we're going to have a root_path. Followed by a title which, the title is going to be a string just like this the string has to be obviously wrapped in quotation marks and then that is all we're going to do. I'm going to copy this and create five of them. Ok, Now all we have to do is just, populate what we have up here inside of our little data structure. So, contact_path, should be right here. Then blogs_path here, portfolios_path here and portfolio here. Look what we can do now. Technically, we're going to be able to get rid of our entire nav so we're going to be able to get rid of everything do this on a single line and we're not even going to have to use a heredoc. Let's see exactly how we could do this.

def nav_helper style, tag_type
  nav_links = ''

  nav_items.each do |item|
    nav_links << "<#{tag_type}><a herf='#{item[:url]}' class='#{style} #{active? item[:url]}'>#{item[:title]}</a></#{tag_type}>"
  end

What we can do we're going to use this kind of as an example. But instead of having to, you know go and create five lines. I'm going to create a variable called nav_links. From here I'm going to set this equal to an empty string. That is the first thing I'm going to do.

Next, I'm going to take nav_items and this is our data structure and I'm going to say nav_items.each do |item|. Now if any of this is unclear let's just get kind of an idea of what we're trying to do. I want to take this list of nav_items and I want to iterate over them and populate what we have here each time. Imagine coming to the terminal and I'm going to open up another pry session and let's go and I'm just going to paste everything in, hit end. I'm just going to say def and sample_collection if you're wondering why that didn't work. It's because these work in Rails but pry and Ruby by itself has no idea what a root_path is or what a contact_path is.

Let me just create a mini version of what we had. I'm going to create an array with just some basic hash values exactly like how we have here. I'm going to say [{url: "/", title: "Home"}, {url: "/about", title: "About"}] This first one is just going to be the root url, a title which will be home, and then we'll just do one more because we don't need to do the whole thing we just need a case example. If you're curious on why I'm doing this when I was originally planning this part of the curriculum out. This is exactly the process I followed. I want to kind of mimic what I did in order for me to implement it so that could hopefully help you when you're wanting to do it. Here I'll say about and title we can say about. Ok, then close off our hash and then close off the array then hit end. Now if I call sample_collection you can see that that has our sample collection.

What I can do is if I do sample_collection.each do |item| and then we'll say puts item, end. Right there you can see what it did. It printed out each of the items. If we want to grab this so if you're not very familiar with how to iterate over a collection of hashes or an array of hashes. The way that you would do that is we're going to follow exactly what we did. But now I'm going to say puts and then I can grab the item followed by the brackets followed by whatever the key is. Here if I want the url and then puts item and then the title and end. You can see what this does. This gave us the direct connection to grab the first root_path, the home, about and then About. This is the way that we are going to parse that.

I'm going it close out of that. With that knowledge in mind now we can come here and see exactly what we're going to do. We have nav_item. This is our array of hashes and I can say nav_items.each do. Then inside of this what we're going to do. Is, we're going to pipe all of that. We're going to generate all of our HTML and add it to nav_links. So nav _links is going to be inside the block and the way that you can add and append a set of strings is by using this double shovel operator. Once again just in case, this part of your Ruby is a little bit on the rusty side. If I create some type of string and do something like this if I want to add onto the string I can use a double shovel operator add some other string and as you can see string now has these extra values. That is essentially what we're going to do is we're going to take these nav_links and we're going to append this full set, this collection here on to nav_links and then eventually we're going to return the full set of items.

Part of the reason why I absolutely love this implementation is we can then add as many as we want we can take items away and we never actually have to touch the implementation code. We can treat this almost like our little database of our navigation elements. I really like this implementation. I think it's very clean and if you're looking to be a professional Rails developer these are the kind of practices you're going to want to follow.

I'm going to select all of this, wrap it in quotation marks. Now we have a little bit of a fix we need to do here which is we need to swap out all of these double quotation marks for single ones. The reason for that is because they would conflict with the way that our string interpolation works. I'm going to do a single one here, here and do that in each spot. Just like that. And that is all we have to do on that side and this doesn't quite work yet. Now we have to remove the hard-coded items right here we have root_path. What we want is item and then we need to select it so we're going to say item and url. We're going to use this over in our active question. So, this is going to pass this in and then we're going to use our title. Right here. Ok, now we're going to then return nav_links. Turn it html_safe and now theoretically unless I made a mistake everything should work perfectly.

Coming back if I hit refresh. Oh, it looks like things are not working perfectly and it's because I didn't perform string interpolation on it. Like that. OK. That's going to do it any time that you are placing and parsing things like this you need to make sure you wrap it up in some form of string interpellation so that Ruby knows that this is not just a regular string but it's something that has to process.

Ok, let's give it one more try. Hit refresh and look at that. We have fully functional links. Our actives are working and we have a full system for managing our nav. The reason why I like this is, I'm not a big fan of having logic or having a lot of heavy HTML that you have to work with. Instead, I would much rather work with pure Ruby code just like this. I mean look how much easier this is to manage then compared with when we had that giant heredoc. This is for a very small nav system I have built systems that have had dozens and dozens of links. If you try to simply embed all of that in all HTML you end up with a nasty bit of code that you have to work with. But here we created our own little database of nav items and now all we have to do is iterate over them pipe them right into our nav links and then return. Now our entire system is so much cleaner. Look how much better this looks inside of our portfolio nav, our blog nav, and our application now.

Now it makes it much easier and scalable to make changes. I am excited with where it's at. I think we are ready to close out this section so let's add this let's commit it. So we'll say "Implemented refactor for navigation and active statuses" and now let's say git push origin design.

Let's switch over to the master branch. git merge and this is going to be the design branch. That brought all of that down, that was quite a bit. The last thing we have to do is push up our master and this is going to push everything up to the GitHub repository. If you go and check it you can see that we are now at a total of 78 commits. We have done a lot of work and if you click on it you'll see that all of our latest changes are all live.

Excellent work! I know that was a ton I know we went through over 20 various guides in order to implement that design. But I'm very happy with where it's at. I think that's a start of a great portfolio. It'll be a good first impression for people that are coming to your site whether you're wanting to get a job or to position yourself as an expert. As a developer I think we're going to be in a really good spot there. Great job in going through that. We're going to take a little break from our application and in the next guide we're going to take a deep dive and see exactly how the Rails asset pipeline works. I will see you then.

Resources