Reputation: 31
What's going on?
Everything works and populates correctly from what I can tell. The only thing that's misbehaving is the dropdown. For some reason it won't fire and display all of it's submenus/dividers.
app.directives.js
angular.module('mb.essentials', [])
.directive('mbAttribute', function ($log) {
return {
restrict: 'A',
scope: {
mbAttribute: '='
},
link: function(scope, element, attrs) {
if (angular.isUndefined(scope.mbAttribute)) {
$log.error(
'You must specify at least one attribute, condition pair!\n' +
'Eg. mb-attribute="{\'alert-warning\': alert.alertType}"');
}
$.each(scope.mbAttribute, function (key, value) {
scope.$watch(function () {
return value;
},
function() {
if (value) {
element.attr(key, '');
} else {
element.removeAttr(key, '');
}
});
});
}
};
})
.directive('mbNavbar', function () {
return {
restrict: 'AE',
require: '^dropdown',
transclude: true,
scope: {
brand: '=',
menus: '=',
affixed: '=',
inverse: '=',
search: '=',
searchFn: '&',
navFn: '&'
},
controller: function ($scope, $element, $attrs, $sce) {
$scope.isCollapsed = true;
$scope.defaults = {
brand: '<span class="glyphicon glyphicon-certificate"></span>',
menus: [],
search: {
show: false
}
}; // end defaults
if (angular.isUndefined($attrs.navFn)) {
$scope.navfn = function (action) {
if (angular.isObject(action))
$scope.$emit('nav.menu', action);
else
$scope.$emit('nav.menu', {'action': action});
}
};
if (angular.isUndefined($attrs.searchFn)) {
$scope.searchFn = function () {
$scope.$emit('nav.search.execute');
}
};
$scope.trustedBrand = angular.isDefined($attrs.brand) ? $sce.trustAsHtml($scope.brand) : $sce.trustAsHtml($scope.defaults.brand);
$scope.hasMenus = function () {
return angular.isDefined($scope.menus);
};
$scope.hasDropdownMenu = function (menu) {
return (angular.isDefined(menu.menu) && angular.isArray(menu.menu));
};
$scope.isDivider = function (item) {
return (angular.isDefined(item.divider) && angular.equals(item.divider, true));
}
$scope.navAction = function (action) {
$scope.navFn({'action': action});
};
},
template:
'<nav class="navbar" ng-class="{\'navbar-inverse\': inverse,\'navbar-default\': !inverse,\'navbar-fixed-top\': affixed == \'top\',\'navbar-fixed-bottom\': affixed == \'bottom\'}" role="navigation">' +
'<div class="container-fluid">' +
'<div class="navbar-header">' +
'<button type="button" class="navbar-toggle" ng-click="isCollapsed = !isCollapsed">' +
'<span class="sr-only">Toggle Navigation</span>' +
'<span class="icon-bar"></span>' +
'<span class="icon-bar"></span>' +
'<span class="icon-bar"></span>' +
'</button>' +
'<a class="navbar-brand" ng-bind-html="trustedBrand"></a>' +
'</div>' +
'<div collapse="isCollapsed" class="collapse navbar-collapse">' +
'<ul class="nav navbar-nav" ng-if="hasMenus()">' +
'<li ng-repeat="menu in menus" mb-attribute="{\'dropdown\': hasDropdownMenu(menu)}">' +
'<a ng-if="!hasDropdownMenu(menu)" ng-click="navAction(menu.action)">{{menu.title}}</a>' +
'<a ng-if="hasDropdownMenu(menu)" class="dropdown-toggle" dropdown-toggle>' +
'{{menu.title}} <b class="caret"></b>' +
'</a>' +
'<ul ng-if="hasDropdownMenu(menu)" class="dropdown-menu">' +
'<li ng-repeat="item in menu.menu" ng-class="{\'nav-divider\': isDivider(item)}">' +
'<a ng-if="!isDivider(item)" ng-click="navAction(item.action)">{{item.title}}</a>' +
'</li>' +
'</ul>' +
'</li>' +
'</ul>' +
'<form ng-if="search.show" class="navbar-form navbar-right" role="search">' +
'<div class="form-group">' +
'<div class="input-group">' +
'<input type="text" class="form-control" placeholder="Search" ng-model="search.terms" />' +
'<span class="input-group-btn">' +
'<button class="btn btn-default" type="button">' +
'<span class="glyphicon glyphicon-search"></span>' +
'</button>' +
'</span>' +
'</div>' +
'</div>' +
'</form>' +
'</div>' +
'</div>' +
'</nav>'
};
});
Upvotes: 1
Views: 374
Reputation: 31
The Issue
Basically angular compiles the DOM then the mb-attribute directive adds the dropdown directive. Essentially Angular and UI Bootstrap have no idea the directive even exists.
The "Solution"
I put this in quotes because it isn't entirely a solution.
Rather then conditionally checking if the dropdown directive should be added. We just add it to every element, regardless of whether or not its a dropdown menu.
The reason this works in this case is because UI Bootstraps dropdown directive requires it to be paired with a dropdown-toggle as well as a dropdown-menu in order to operate properly.
angular.directives.js
angular.module('mb.essentials', [])
.directive('mbNavbar', function () {
return {
restrict: 'AE',
require: '^dropdown',
scope: {
brand: '=',
menus: '=',
affixed: '=',
inverse: '=',
search: '=',
searchFn: '&',
navFn: '&'
},
controller: function ($scope, $element, $attrs, $sce) {
$scope.isCollapsed = true;
$scope.defaults = {
brand: '<span class="glyphicon glyphicon-certificate"></span>',
menus: [],
search: {
show: false
}
} // end defaults
if (angular.isUndefined($attrs.navFn)) {
$scope.navfn = function (action) {
if (angular.isObject(action))
$scope.$emit('nav.menu', action);
else
$scope.$emit('nav.menu', {'action': action});
}
}
if (angular.isUndefined($attrs.searchFn)) {
$scope.searchFn = function () {
$scope.$emit('nav.search.execute');
}
}
$scope.trustedBrand = angular.isDefined($attrs.brand) ? $sce.trustAsHtml($scope.brand) : $sce.trustAsHtml($scope.defaults.brand);
$scope.hasMenus = function () {
return angular.isDefined($scope.menus);
}
$scope.hasDropdownMenu = function (menu) {
return (angular.isDefined(menu.menu) && angular.isArray(menu.menu));
}
$scope.isDivider = function (item) {
return (angular.isDefined(item.divider) && angular.equals(item.divider, true));
}
$scope.navAction = function (action) {
$scope.navFn({'action': action});
}
},
template:
'<nav class="navbar" ng-class="{\'navbar-inverse\': inverse,\'navbar-default\': !inverse,\'navbar-fixed-top\': affixed == \'top\',\'navbar-fixed-bottom\': affixed == \'bottom\'}" role="navigation">' +
'<div class="container-fluid">' +
'<div class="navbar-header">' +
'<button type="button" class="navbar-toggle" ng-click="isCollapsed = !isCollapsed">' +
'<span class="sr-only">Toggle Navigation</span>' +
'<span class="icon-bar"></span>' +
'<span class="icon-bar"></span>' +
'<span class="icon-bar"></span>' +
'</button>' +
'<a class="navbar-brand" ng-bind-html="trustedBrand"></a>' +
'</div>' +
'<div collapse="isCollapsed" class="collapse navbar-collapse">' +
'<ul class="nav navbar-nav" ng-if="hasMenus()">' +
'<li ng-repeat="menu in menus" dropdown>' +
'<a ng-if="!hasDropdownMenu(menu)" ng-click="navAction(menu.action)">{{menu.title}}</a>' +
'<a ng-if="hasDropdownMenu(menu)" class="dropdown-toggle" dropdown-toggle>' +
'{{menu.title}} <b class="caret"></b>' +
'</a>' +
'<ul ng-if="hasDropdownMenu(menu)" class="dropdown-menu">' +
'<li ng-repeat="item in menu.menu" ng-class="{\'nav-divider\': isDivider(item)}">' +
'<a ng-if="!isDivider(item)" ng-click="navAction(item.action)">{{item.title}}</a>' +
'</li>' +
'</ul>' +
'</li>' +
'</ul>' +
'<form ng-if="search.show" class="navbar-form navbar-right" role="search">' +
'<div class="form-group">' +
'<div class="input-group">' +
'<input type="text" class="form-control" placeholder="Search" ng-model="search.terms" />' +
'<span class="input-group-btn">' +
'<button class="btn btn-default" type="button">' +
'<span class="glyphicon glyphicon-search"></span>' +
'</button>' +
'</span>' +
'</div>' +
'</div>' +
'</form>' +
'</div>' +
'</div>' +
'</nav>'
};
});
For the Future
I'm going to use this solution, but I'm not going to mark it as the solution. I figure if in the future someone comes up with a real solution then I can mark it as such.
Upvotes: 1