<!DOCTYPE html>
<html ng-app="myMenuApp">
<head>
<script data-require="lodash.js@3.5.0" data-semver="3.5.0" src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.js"></script>
<link href="https://fonts.googleapis.com/css?family=Ubuntu:500,700" rel="stylesheet" type="text/css" />
<link data-require="angular-material@0.10.0" data-semver="0.10.1" rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/0.10.1/angular-material.min.css" />
<link data-require="font-awesome@4.3.0" data-semver="4.3.0" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="fill-height">
<div ui-view=""></div>
</div>
<script data-require="angular.js@~1.4.0" data-semver="1.4.0" src="https://code.angularjs.org/1.4.0/angular.js"></script>
<script data-require="angular-animate@1.4.0" data-semver="1.4.0" src="https://code.angularjs.org/1.4.0/angular-animate.js"></script>
<script data-require="angular-aria@1.4.0" data-semver="1.4.0" src="https://code.angularjs.org/1.4.0/angular-aria.js"></script>
<script data-require="angular-material@0.9.0" data-semver="0.9.4" src="https://ajax.googleapis.com/ajax/libs/angular_material/0.9.4/angular-material.min.js"></script>
<script data-require="ui-router@0.2.12" data-semver="0.2.15" src="//rawgit.com/angular-ui/ui-router/0.2.15/release/angular-ui-router.js"></script>
<!--my scripts-->
<script src="app.modules.js"></script>
<script src="menu.service.js"></script>
<script src="home.controller.js"></script>
<script src="menu_toggle.directive.js"></script>
<script src="menulink.directive.js"></script>
<script src="app.routes.js"></script>
</body>
</html>
(function () {
'use strict';
// Declare app level module which depends on filters, and services
angular.module('myMenuApp', [
'myMenuApp.controllers',
'ngAnimate',
'ui.router',
'ngMaterial',
'ngAria',
])
.config(function ($mdThemingProvider) {
$mdThemingProvider.theme('default')
.primaryPalette('light-blue', {
'default': '300'
})
.accentPalette('deep-orange', {
'default': '500'
});
})
.config(['$stateProvider', '$urlRouterProvider', '$logProvider',
function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state('home', {
url: '/',
views: {
'@': {
templateUrl: 'home.view.html',
controller: 'HomeCtrl as vm'
}
}
})
.state('home.beers', {
url: 'beers',
abstract: true
})
.state('home.beers.ipas', {
url: '/ipas',
views: {
'content@home': {
templateUrl: 'beers.ipa.view.html'
}
}
})
.state('home.beers.porters', {
url: '/porters',
views: {
'content@home': {
templateUrl: 'beers.porters.view.html'
}
}
})
.state('home.beers.wheat', {
url: '/porters',
views: {
'content@home': {
templateUrl: 'beers.wheat.view.html'
}
}
})
}])
//take all whitespace out of string
.filter('nospace', function () {
return function (value) {
return (!value) ? '' : value.replace(/ /g, '');
};
})
//replace uppercase to regular case
.filter('humanizeDoc', function () {
return function (doc) {
if (!doc) return;
if (doc.type === 'directive') {
return doc.name.replace(/([A-Z])/g, function ($1) {
return '-' + $1.toLowerCase();
});
}
return doc.label || doc.name;
};
});
})();
/* Styles go here */
body {
font-family: 'Ubuntu', sans-serif;
color: orangered;
}
.fill-height{
display: flex;
flex: 1;
min-height: 100%;
}
.fill-height > div{
min-height: 100%;
display: flex;
}
md-sidenav {
overflow: visible;
}
.side-menu, .side-menu ul {
list-style: none;
padding: 0;
margin-top: 0;
}
.side-menu .md-button.active {
color: deeppink;
}
.side-menu .menu-toggle-list a.md-button {
display: block;
padding: 0 16px 0 32px;
text-transform: none;
text-rendering: optimizeLegibility;
font-weight: 500;
}
.side-menu .menu-toggle-list .md-button {
display: block;
padding: 0 16px 0 32px;
text-transform: none;
}
.side-menu > li {
border-top: 1px solid rgba(0, 0, 0, 0.12);
}
.md-button-toggle .md-toggle-icon.toggled {
transform: rotateZ(180deg);
-webkit-transform: rotateZ(180deg);
}
.side-menu .md-button-toggle .md-toggle-icon {
background-size: 100% auto;
display: inline-block;
margin: auto 0 auto auto;
width: 15px;
color: darkgray;
transition: transform .3s ease-in-out;
-webkit-transition: -webkit-transform .3s ease-in-out;
}
.menu-toggle-list.ng-hide {
max-height: 0;
}
.side-menu .md-button {
border-radius: 0;
color: inherit;
cursor: pointer;
font-weight: 400;
line-height: 40px;
margin: 0;
max-height: 40px;
overflow: hidden;
padding: 0px 16px;
text-align: left;
text-decoration: none;
white-space: normal;
width: 100%;
}
.menu-toggle-list {
background: #fff;
max-height: 300px;
overflow: hidden;
position: relative;
z-index: 1;
-webkit-transition: 0.75s cubic-bezier(0.35, 0, 0.25, 1);
-webkit-transition-property: max-height;
-moz-transition: 0.75s cubic-bezier(0.35, 0, 0.25, 1);
-moz-transition-property: max-height;
transition: 0.75s cubic-bezier(0.35, 0, 0.25, 1);
transition-property: max-height;
}
.side-menu menu-toggle span{
padding-top: 12px;
padding-bottom: 12px;
}
###How to Create a Angular Material Side Menu
This plunker goes with the blog post
[BrilliantBritz.com](http://brilliantbritz.com/2015/06/17/creating-your-own-angular-material-right-navigation-menu/)
(function(){
'use strict';
angular.module('common.services')
.factory('menu', [
'$location',
'$rootScope',
function ($location) {
var sections = [{
name: 'Getting Started',
state: 'home.gettingstarted',
type: 'link'
}];
sections.push({
name: 'Beers',
type: 'toggle',
pages: [{
name: 'IPAs',
type: 'link',
state: 'home.beers.ipas',
icon: 'fa fa-group'
}, {
name: 'Porters',
state: 'home.beers.porters',
type: 'link',
icon: 'fa fa-map-marker'
},
{
name: 'Wheat',
state: 'home.beers.wheat',
type: 'link',
icon: 'fa fa-plus'
}]
});
sections.push({
name: 'Munchies',
type: 'toggle',
pages: [{
name: 'Cheetos',
type: 'link',
state: 'munchies.cheetos',
icon: 'fa fa-group'
}, {
name: 'Banana Chips',
state: 'munchies.bananachips',
type: 'link',
icon: 'fa fa-map-marker'
},
{
name: 'Donuts',
state: 'munchies.donuts',
type: 'link',
icon: 'fa fa-map-marker'
}]
});
var self;
return self = {
sections: sections,
toggleSelectSection: function (section) {
self.openedSection = (self.openedSection === section ? null : section);
},
isSectionSelected: function (section) {
return self.openedSection === section;
},
selectPage: function (section, page) {
page && page.url && $location.path(page.url);
self.currentSection = section;
self.currentPage = page;
}
};
function sortByHumanName(a, b) {
return (a.humanName < b.humanName) ? -1 :
(a.humanName > b.humanName) ? 1 : 0;
}
}])
})();
angular.module('common.directives')
.run(['$templateCache', function ($templateCache) {
$templateCache.put('partials/menu-toggle.tmpl.html',
'<md-button class="md-button-toggle"\n' +
' ng-click="toggle()"\n' +
' aria-controls="docs-menu-{{section.name | nospace}}"\n' +
' flex layout="row"\n' +
' aria-expanded="{{isOpen()}}">\n' +
' {{section.name}}\n' +
' <span aria-hidden="true" class=" pull-right fa fa-chevron-down md-toggle-icon"\n' +
' ng-class="{\'toggled\' : isOpen()}"></span>\n' +
'</md-button>\n' +
'<ul ng-show="isOpen()" id="docs-menu-{{section.name | nospace}}" class="menu-toggle-list">\n' +
' <li ng-repeat="page in section.pages">\n' +
' <menu-link section="page"></menu-link>\n' +
' </li>\n' +
'</ul>\n' +
'');
}])
.directive('menuToggle', ['$timeout', function ($timeout ) {
return {
scope: {
section: '='
},
templateUrl: 'partials/menu-toggle.tmpl.html',
link: function (scope, element) {
var controller = element.parent().controller();
scope.isOpen = function () {
return controller.isOpen(scope.section);
};
scope.toggle = function () {
controller.toggleOpen(scope.section);
};
var parentNode = element[0].parentNode.parentNode.parentNode;
if (parentNode.classList.contains('parent-list-item')) {
var heading = parentNode.querySelector('h2');
element[0].firstChild.setAttribute('aria-describedby', heading.id);
}
}
};
}])
(function(){
'use strict';
angular.module('common.services', []);
angular.module('myMenuApp.controllers', ['common.directives']);
angular.module('common.directives', ['common.services']);
})();
(function(){
'use strict';
angular.module('common.directives')
.run(['$templateCache', function ($templateCache) {
$templateCache.put('partials/menu-link.tmpl.html',
'<md-button ng-class="{\'{{section.icon}}\' : true}" ui-sref-active="active" \n' +
' ui-sref="{{section.state}}" ng-click="focusSection()">\n' +
' {{section | humanizeDoc}}\n' +
' <span class="md-visually-hidden "\n' +
' ng-if="isSelected()">\n' +
' current page\n' +
' </span>\n' +
'</md-button>\n' +
'');
}])
.directive('menuLink', function () {
return {
scope: {
section: '='
},
templateUrl: 'partials/menu-link.tmpl.html',
link: function ($scope, $element) {
var controller = $element.parent().controller();
$scope.focusSection = function () {
// set flag to be used later when
// $locationChangeSuccess calls openPage()
controller.autoFocusContent = true;
};
}
};
})
})();
(function(){
'use strict';
angular.module('myMenuApp.controllers')
.controller('HomeCtrl', [
'$rootScope',
'$log',
'$state',
'$timeout',
'$location',
'menu',
function ($rootScope, $log, $state, $timeout, $location, menu) {
var vm = this;
var aboutMeArr = ['Family', 'Location', 'Lifestyle'];
var budgetArr = ['Housing', 'LivingExpenses', 'Healthcare', 'Travel'];
var incomeArr = ['SocialSecurity', 'Savings', 'Pension', 'PartTimeJob'];
var advancedArr = ['Assumptions', 'BudgetGraph', 'AccountBalanceGraph', 'IncomeBalanceGraph'];
//functions for menu-link and menu-toggle
vm.isOpen = isOpen;
vm.toggleOpen = toggleOpen;
vm.autoFocusContent = false;
vm.menu = menu;
vm.status = {
isFirstOpen: true,
isFirstDisabled: false
};
function isOpen(section) {
return menu.isSectionSelected(section);
}
function toggleOpen(section) {
menu.toggleSelectSection(section);
}
}])
})();
<div layout="row">
<md-sidenav class="md-sidenav-left md-whiteframe-z1" md-component-id="left" md-is-locked-open="$mdMedia('gt-sm')">
<md-toolbar md-scroll-shrink>
<div class="md-toolbar-tools">
<h3>
<span>My App Title</span>
</h3>
</div>
</md-toolbar>
<md-content role="navigation">
<ul class="side-menu">
<li ng-repeat="section in vm.menu.sections" class="parent-list-item"
ng-class="{'parentActive' : vm.isSectionSelected(section)}">
<h2 class="menu-heading" ng-if="section.type === 'heading'"
id="heading_{{ section.name | nospace }}">
{{section}}
</h2>
<menu-link section="section" ng-if="section.type === 'link'"></menu-link>
<menu-toggle section="section" ng-if="section.type === 'toggle'"></menu-toggle>
</li>
</ul>
</md-content>
</md-sidenav>
<md-content flex>
<ui-view name="content"></ui-view>
</md-content>
</div>
<div class="row">
<h1 class="col-lg-12">IM THE IPA BEER PAGE</h1>
</div>
<div class="row">
<div class="col-lg-12">
<ul>
<li>Lost Rhino Face Plant</li>
<li>3 Brothers Hoptimization</li>
</ul>
</div>
</div>
<div class="row">
<h1 class="col-lg-12">IM THE PORTERS BEER PAGE</h1>
</div>
<div class="row">
<div class="col-lg-12">
<ul>
<li>Founders Porter</li>
<li>Black Butte Porter</li>
</ul>
</div>
</div>
<div class="row">
<h1 class="col-lg-12">IM THE WHEAT BEER PAGE</h1>
</div>
<div class="row">
<div class="col-lg-12">
<ul>
<li>312 Urban Wheat</li>
<li>A Little Sumpin' Sumpin' Ale</li>
</ul>
</div>
</div>