2

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.

3 Answers 3

5

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.

Sign up to request clarification or add additional context in comments.

8 Comments

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.
Using this method it behaves the same way as the ng-show\ng-hide strategy. On logout, the login menu appears.
Try putting up a fiddle or plunkr.
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.
I think it's more of a Bootstrap issue. plnkr.co/edit/wkUClK9OBLK8EWI5D8Fn?p=preview
|
1

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>

Comments

0

Why don't you assign functions to your commands:

var openLoginDialog = function() { ... };
var logout = function() { ... };

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

2 Comments

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.
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 . :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.