Angular Material Sidenav


I've recently started working with a couple new tools: JSPM, and Angular Material. I won't go into much detail on using JSPM here, will save that for future posts, but I do mention it because it is what I'm using in order to use ES6 features. It uses a javascript compiler, such as Babel, and can take care of bundling your scripts, managing dependencies, etc... I happen to find it more to my liking that the current alternatives. In this post I will, however, focus on Angular Material, specifically the sidenav component.


Getting Started

The team building Angular Material made it very easy to get a sidenav up-and-running. A couple of very nice pieces of functionality is that you can specify when the sidenav is "locked" open - such as on screens larger than a certain number of pixels. The markup is very simple, having a primary directive and a few attributes to pass in:

  • md-sidenav - the primary directive. It has to be an element, not just an attribute.
  • md-is-locked-open (optional) - an expression for when the sidenav should remain locked open. You can reference the $mdMedia service in the expression if you want to use a specific breakpoint, etc...
  • md-component-id (optional) - a unique identifier for this instance of a sidenav so that you can use the $mdSidenav service to get a direct handle to it

Here's an example:

<md-sidenav md-is-locked-open="$mdMedia('gt-sm')" md-component-id="primaryLeft">
  <!-- content of sidenav here -->
</md-sidenav>

Notice the expression in the md-is-locked-open attribute: $mdMedia('gt-sm') This is taking advantage of the built-in media query service and specify that the sidenav should remain locked open any time the screen is greater than the small breakpoint. It's pretty handy that they take care of injecting that service when parsing the expression so that you can use the md-sidenav outside of any controller instances, etc... However, if you wanted to do something more custom, you'd likely want to use an expression that is specific to a controller for that view.

Additionally, you can use specific CSS classes to provide depth for the sidenav:
md-whiteframe-Xdp - You can use this format to provide various depths from 1 to 24 (i.e. md-whiteframe-3dp) via box-shadows.


A Handy Directive###

When I was first playing around with the sidenav and saw how it automatically locks open when the expression provided evaluates to, and then hides when the expression provided evaluates to false, I realized that I hadn't actually planned for handling a hidden sidenav. I had a menu icon in the <header> element that would display only when the screen size was less than or equal to the small breakpoint, but I didn't have any controller created for that part of the view, and honestly didn't feel it was warranted. So, in order to make a linkage between the toggle button and the sidenav, I made a really simple directive to let an element act as a toggle for any sidenav specified.

The API for the directive is just one simple attribute:

side-nav-toggle - the component ID of the sidenav you want the element to act as a toggle for.

Here's a quick example of the usage:

<button side-nav-toggle="primaryLeft">Toggle Primary Left Sidenav</button>

Given that we provided a component id, we can use the $mdSidenav service to interact with the specified instance:

import angular from 'angular';

function sideNavToggle($mdSidenav, $log) {
    return {
        accept: 'EA',
        link: (scope, el, attrs) => {
            var sideNav;

            if (angular.isDefined(attrs.sideNavToggle)) {
                el.on('click', () => {
                    if (!sideNav) {
                        //get and cache reference to the instance
                        sideNav = $mdSidenav(attrs.sideNavToggle);
                    }
                    //toggle the nav
                    sideNav.toggle();
                });
            } else {
              $log.error("sideNavToggle - no sidenav component id specified");
            }
        }
    };
}

sideNavToggle.$inject = ['$mdSidenav', '$log'];

angular.module('app')
    .directive('sideNavToggle', sideNavToggle);

One convenience of the $mdSidenav service is that it ignores calls to close or toggle the sidenav instance when it is locked open, so you don't have to worry about checking that yourself in the toggle directive. Of course, you can if you want to by using the isLockedOpen method.


So far, I'm pretty impressed by how well thought through things seem to be in Angular Material. I'm currently digging into various aspects of their theming functionality and hope to continue to learn and write more about Angular Material in the coming couple of months! I also plan to write a bit about JSPM as I get more knowledgeable with it - I'm finding it pretty well-documented and not really difficult to get started with it.

-Bradley