RafaelVidaurre / angular-permission

Simple route authorization via roles/permissions
MIT License
1.13k stars 212 forks source link

A different approach for ng-if #435

Closed Eduardo-Julio closed 6 years ago

Eduardo-Julio commented 6 years ago

So a lot of people have commented about the lack of a ng-if type of behavior, this is a basic feature that almost everyone expect from a module that manages roles and permissions.

Yes, I read the docs and I know about the PermPermissionStrategies, but this is just going to make us duplicate the behavior of ng-if and we are probably going to get it wrong, I tried and couldn't make it work like ng-if.

What we can do is use the ng-if directive from angular and pass values from the permissions module so ng-if evaluates them an acts accordingly.

<div permissions ng-if="valueFromPermissions"></div>

I did some research and it seems some people have done it.

https://stackoverflow.com/questions/26907440/enable-angulars-built-in-directives-like-ng-if-to-have-access-to-a-variable-f

lobosg commented 6 years ago

I see this useful +1

blowsie commented 6 years ago

I personally created a component to handle this quite some time ago.

has-permission.component.js

(function() {
  'use strict';

  angular
    .module('app')
    .component('hasPermission', {
      bindings: {
        permission: '<'
      },
      templateUrl: 'app/common/permissions/has-permission/has-permission.html',
      transclude: true,
      controller: Controller
    });

  /** @ngInject */
  function Controller(PermPermissionMap, PermAuthorization) {
    let ctrl = this;

    ctrl.$onInit = function() {
      let PermissionMap = new PermPermissionMap({
        only: ctrl.permission
      });

      let authorizationResult = PermAuthorization.authorizeByPermissionMap(PermissionMap);

      authorizationResult
        .then(function() {
          ctrl.hasPermission = true;
        })
        .catch(function(rejectedPermission) {
          ctrl.hasPermission = false;
        });
    }

  }

})();

has-permission.html

<div ng-if="$ctrl.hasPermission" ng-transclude=""></div>

Usage

 <has-permission permission="'panel--read'" class="col-md-6">
      <div class="panel panel-app">
        <div class="panel-heading">My Panel</div>
        <div class="panel-content">
          Must have permissions
        </div>
      </div>
    </has-permission>
Eduardo-Julio commented 6 years ago

Thanks for your help, I also created a similar solution but I managed to do it using ng if in a more "direct" way.

Example

<div has-permission="'admin'">
    Only users with the permssion "admin" see this
</div>
<div has-permission="['admin', 'user']">
    Only users with the permssion "admin" and "user" can see this
</div>
angular.module('app').directive('hasPermission', function(ngIfDirective, PermAuthorization, PermPermissionMap, PermPermissionStore, $rootScope) {
    var ngIf = ngIfDirective[0];

    return {
        transclude: ngIf.transclude,
        priority: ngIf.priority - 1,
        terminal: ngIf.terminal,
        restrict: ngIf.restrict,
        scope:{
            hasPermission: '@'
        },
        link: function(scope, element, attributes) {

            scope.initialNgIf = attributes.ngIf, scope.ifEvaluator;

            scope.checkAuth = function() {
                // Check if the user has the permission defined on scope.hasPermission
                var checkAuthPromise = PermAuthorization.authorizeByPermissionMap(new PermPermissionMap({only: eval(scope.hasPermission)}));
                checkAuthPromise.then(function () {
                    scope.isPermitted = true
                }).catch(function () {
                    scope.isPermitted = false
                })
            }

            // Check on every StateChange fired by ui-permission in case the user has gained / lost his previous permissions
            $rootScope.$on('$stateChangePermissionStart', function(event, toState, toParams, options) {
                scope.checkAuth()
            });

            // The value evaluated by ng-if
            attributes.ngIf = function() {
                return scope.isPermitted
            };

            ngIf.link.apply(ngIf, arguments);
        }
    };
});

I dont like that the directive depends on a promise to check the permission, this is the only way I could find to make it work with ng-if but Im sure someone can make it cleaner (or event better, provide a function and not a promise to check the permissions).

morteza-gho commented 6 years ago

This is a nagularjs service ↓

globalService.checkHasItem = (mainArray, array) => {

      // mainArray: all roles will be in this array, you get these from server, for example → ['role1', 'role2', 'role3', 'role4', 'role5', 'role6', ...]
      // array: some roles, for example → ['role3', 'role4']

      let hasItem = false;

      // with this ↓ I check if user has role
      array.map((item) => {
         // use this for filters → https://github.com/a8m/angular-filter#contains
         if ($filter('contains')(mainArray, item)) {
            hasItem = true;
         }
      });

      return hasItem;
};

in generlCtrl.js ↓

$rootScope.checkPerms = (perms) {
      return globalService.checkHasItem(userPerms, perms);
 };

Now in view <button ng-if="checkPerms(['role4'])">just user with 'role4' can click this button</button>