dynamic menu items using AngularJS

15,874

Solution 1

Use ng-switch instead of ng-show/hide. ng-switch adds/removes elements from the DOM.

<ul class="dropdown-menu pull-right" role="menu">
    <li ng-switch="user">
       <a ng-switch-when="true" ng-click="logout()">Logout</a>
       <a ng-switch-default     ng-click="openLoginDialog()">Login</a>
    </li>
</ul>

Update to handle @andyczerwonka's comment. Move the ng-switch up one or two levels. If spans inside ul don't bother you:

<ul class="dropdown-menu pull-right" role="menu" ng-switch="user">
  <span ng-switch-when="true">
    <li><a  ng-click="logout()">Logout</a></li>
    <li><a  ng-click="preferences()">Preferences</a></li>
  </span>
  <span ng-switch-default>
    <li><a ng-click="openLoginDialog()">Login</a></li>
  </span>
</ul>

Otherwise, how about one span outside the ul:

<span ng-switch="user">
  <ul class="dropdown-menu pull-right" role="menu" ng-switch-when="true">
      <li><a  ng-click="logout()">Logout</a></li>
      <li><a  ng-click="preferences()">Preferences</a></li>
    </ul>
  <ul class="dropdown-menu pull-right" role="menu" ng-switch-default>
      <li><a ng-click="openLoginDialog()">Login</a></li>
  </ul>
</span>

Fiddle showing both methods.

Solution 2

In my project I use the following setup:

angular.module('mymodule')
.controller('MenuCtrl', function ($scope, $translate, UserService) {

Define actions with roles that are allowed to use these actions:

  $scope.actions = [ {
    name : "menu.login",
    href : "#/login",
    roles : []
  }, {
    name : "menu.logout",
    href : "#/logout",
    roles : [ 'READ_ONLY', 'ADMIN' ]
  }, {
    name : "menu.foo",
    href : "#/foo",
    roles : [ 'READ_ONLY', 'ADMIN' ]
  }, {
    name : "menu.adminArea",
    href : "#/adminArea",
    roles : [ 'ADMIN' ]
  } ];

Define an isAllowed function that determines if an action can be used by the current user:

  $scope.isAllowed = function(action) {
    if (action.roles && action.roles.length) {
      for ( var i = 0; i < action.roles.length; ++i) {
        if (jQuery.inArray(action.roles[i], UserService.roles) >= 0) {
          return true;
        }
      }
      return false;
    } else {
      return !(UserService.roles && UserService.roles.length);
    }
  };

Then use $roueChangeSuccess event to determine which actions should be displayed for the current user:

  $scope.initView = function() {
    for(var i=0; i<$scope.actions.length; ++i) {
      var action = $scope.actions[i];
      if($scope.isAllowed(action)) {
        action.isRendered = true;
      } else {
        action.isRendered = false;
      }
    }
  };

  $scope.$on('$routeChangeSuccess', function() {
    $scope.initView();
  });

});

Finally, this is my menu template:

<nav ng-controller="MenuCtrl">
  <ul>
    <span ng-repeat="action in actions" ng-switch on="action.isRendered">
      <li ng-switch-when="true"><a href="{{action.href}}">{{action.name | translate}
      </a></li>
    </span>
  </ul>
</nav>
Share:
15,874
andyczerwonka
Author by

andyczerwonka

Business and Software Architect, Entrepreneur, Polyglot Programmer, Scala Enthusiast

Updated on June 13, 2022

Comments

  • andyczerwonka
    andyczerwonka almost 2 years

    Let's say I have the following menu structure:

        <li class="dropdown"><img role="button" class="dropdown-toggle" data-toggle="dropdown" ng-src="{{avatarUrl}}" />
          <ul class="dropdown-menu pull-right" role="menu">
            <li ng-hide="user"><a ng-click="openLoginDialog()">Login</a></li>
            <li ng-show="user"><a ng-click="logout()">Logout</a></li>
          </ul>
        </li>
    

    I get the correct menu, but because I'm using ng-show/ng-hide, when I change user = false; programmatically in the controller, the login menu appears. I get why this is happening, but I'm not sure what approach to take when using Angular to prevent it. I tried an ng-repeat:

        <li class="dropdown"><img role="button" class="dropdown-toggle" data-toggle="dropdown" ng-src="{{avatarUrl}}" />
          <ul class="dropdown-menu pull-right" role="menu">
            <li ng-repeat="action in actions"><a ng-click="{{action.command}}">{{action.name}}</li>
          </ul>
        </li>
    

    with:

    $scope.actions = [ {
      name : "Login",
      command : "openLoginDialog()"
    }, {
      name : "Logout",
      command : "logout()"
    } ];
    

    But with that strategy, nothing happens with I click the menu item. I'm not sure what the appropriate Angular approach is to what I'm sure is a pedestrian use case.

  • andyczerwonka
    andyczerwonka over 11 years
    This doesn't work for me because ng-switch won't work if I have more than two menu items. Let's say I want to show one menu item when the user is not logged in, e.g. login, and two when he is, e.g. Preferences and Logout. I don't think ng-switch will handle this case.
  • andyczerwonka
    andyczerwonka over 11 years
    I did, but the items won't execute when I add them this way. They show up just fine, but they won't execute. I'm not sure why.
  • andyczerwonka
    andyczerwonka over 11 years
    Using this method it behaves the same way as the ng-show\ng-hide strategy. On logout, the login menu appears.
  • Mark Rajcok
    Mark Rajcok over 11 years
    Try putting up a fiddle or plunkr.
  • andyczerwonka
    andyczerwonka over 11 years
    Here's an example of me trying to use ng-repeat, but the actions don't execute. Here's a video of the menu appearing on logout using the strategy you mention above. I'll try and extract it into an example.
  • andyczerwonka
    andyczerwonka over 11 years
    I think it's more of a Bootstrap issue. plnkr.co/edit/wkUClK9OBLK8EWI5D8Fn?p=preview
  • Mark Rajcok
    Mark Rajcok over 11 years
    Try this plnkr.co/edit/fmwJVl5wTcasUnjv9Oiz?p=preview You need to do $scope.loggedIn = true/false; In the controller methods, I added some jQuery to click the menu button to automatically close it.
  • andyczerwonka
    andyczerwonka over 11 years
    I was actually able to get it to work by simply moving the ng-switch to a higher level. For some reason, the login menu does not appear anymore. I'm not fond of the solution, but it works. github.com/andyczerwonka/dolphin/blob/…
  • andyczerwonka
    andyczerwonka over 11 years
  • Fracedo
    Fracedo over 9 years
    I think its a problem with the angularjs version you have used . Here is the plunker, where I used angular version 1.1.5 and its working fine . :)