Creating Sticky Navigation In Angular

A post from Oleg Yanchinskiy

I’m a big fan of elegant ui components which not only present data intuitively but also encourage active user interaction. One of my favorite elements from a blog where I frequently read articles is the navigation bar from the Player’s Tribune. It starts off discretely at the top, and as you scroll down the opacity decreases until it passes the main article picture. The navigation receives the opposite opposite treatment when a user scrolls up (TPT recently updated the navigation to attract less attention, in the past the entire nav bar would ‘roll’ in the direction of your scroll at the start of a scroll - it was really cool!).

One of the recent projects I’ve been assigned to, IMPACT, requires lots of these visually stimulating elements which encourage our users to click around and interact with the data we’re presenting. In IMPACT, the nav bar would play a central role in summarizing the data and making it accessible no matter where the user is on the page vertically. For this to work I needed to create a “sticky” nav bar which sticks to the top of the page as the user scrolls down. Sounds easy enough - just position: absolute, add some other CSS magic and it should work. However, the trick was that the bar could only appear once the user reached a certain point on the page that was the cutoff of the boxes that summarized the data. After that point the nav bar would take over as the summary of the data. Let me show you what I’m talking about (I'm scrolling slow so you can see the effect).

The sticky nav fades in once you scroll past the above summary boxes and fades out once you scroll back up. The sticky nav is supposed to retain all the same functionality as the original summary navigation, meaning you should be able to click on each metric to get new data and the metric in focus should be more apparent.

This feature has a lot of moving pieces but I'll focus only on the navigation. When there are many moving pieces it’s best to break the feature down into as many little components as possible. I broke this piece down into 3 Angular directives and one model which controlled the data.

The directives were as follows -

  1. A surrounding directive for the summary boxes which controlled and fed the data. ng-repeat iterates over the array of summary boxes.

  2. Summary box directive simply displayed data and got it's on-click functionality from its' parent.

  3. The "sticky" navigation directive.

Here is how they work in unison -

  • All of the directives are contained within a main html template.

  • The summary directive follows the scroll and emits an event when the scroll reaches a certain threshold.

  • The main feature controller listens for that event and toggles the sticky metrics directive on/off using ng-if.

  • A little css magic allows for the cool fade-in / fade-out effect.

    templateUrl: function (elem, attrs) {
        if (attrs.sticky) {
          return 'stickyTemplate.html';
        } else {
          return 'normalTemplate.html'
        }
     }
    

The above code goes into the directive declaration and based on the attributes that are passed in, determines which template to fetch.

This solution is quite simple, the real challenge is to make this scroll bar reusable throughout the rest of our app. The best solution I found for that is to decouple the directive from the model and requiring particular resources based on the passed in attributes.

bleak theme by Jack Preston