pstobiecki
pstobiecki

Reputation: 1874

Two attribute directives with new/isolated scope on single element

I'd like to use an AngularJS directive to disable specific parts of forms throughout the application based on whether the user has a specific role (given as attribute value; may be different per element). The directive's code goes like this:

/** @ngInject */
function PermissionDirective() {
    var directive = {
        restrict: 'A',
        scope: true,
        bindToController: true,
        controller: controller,
        controllerAs: 'permission',
        link: link
    };

    function link(scope, element, attrs) {
        scope.soPermission = attrs.soPermission;
    }

    return directive;
}

/** @ngInject */
function controller($scope, $attrs, ...) {
    var permission = this;
    permission.granted = false;
    $scope.soPermission = $attrs.soPermission;
    init();

    function init() {
        var requiredPermission = $scope.soPermission;

        permission.granted = // determine if user can use element
    }
}

Note that it needs its own scope to work. The usage in HTML is:

<button type="button" ng-disabled="... || !permission.granted" so-permission="WRITE">

All's fine as long as so-permission is the only directive in the element with a new scope. However, some elements also use a confirm modal which also seems to require a new/isolated scope. This results in a Multiple directives... Angular error.

My question is: how do you work around this? How do I rework the so-permission directive to get rid of the internal scope and still keep it working (since I can't touch the confirm thing as it's a library)?

Upvotes: 1

Views: 166

Answers (3)

frankrue
frankrue

Reputation: 74

What are the different permissions you intend to give to the form elements? Maybe stepping back and looking at it from a simpler, HTML perspective is possible?

Instead of creating a directive, could you simply assign the readonly attribute using ng-attr-readonly with a function to determine if it should be present or not. That function could exist in your form's controller.

<input type="text" ng-attr-readonly="getPermissions()" />

And the function:

this.getPermissions = function() {
  if (this.granted === 'READ') return undefined;
  return 'readonly';
}

Would this approach work for you situation?

UPDATE: Make this function into a Factory and re-use it.

angular.module("myApp").factory("PermissionsFactory",PermissionsFactory);
function PermissionsFactory() {

  factory = {
    getPermissions: getPermissions
  }
  return factory;

  function getPermissions(ctrl) {
    if (ctrl.granted === 'READ') return undefined;
    return 'readonly';
  } 

}

Then, in your controller (assuming you have injected it):

this.getPermissions = PermissionsFactory.getPermissions

Finally, in your HTML, be sure to pass the controller into the function, since the function is no longer part of the controller and will need the scope of the controller to check the permissions:

<input type="text" ng-attr-readonly="getPermissions(permission)" />

Upvotes: 1

Valery Kozlov
Valery Kozlov

Reputation: 1577

you should use $parse service, to communicate with scope: /

** @ngInject */
        function PermissionDirective() {
        var directive = {
        restrict: 'A',
        link: link
    };

function link(scope, element, attrs) {
    var getter = $parse(attrs.soPermission);
    var setter = getter.setter;
    setter(scope, val); // set value to scope
    var value = getter(scope); //get value from scope
}

return directive;
}

also you could use scope.$watch, to sync values in dynamic.

Upvotes: 0

atul
atul

Reputation: 562

In angular js, we can have at the most one isolated directive on a single element. I would suggest try to decorate your directive. Check this article for more details Decorating Directives

Upvotes: 0

Related Questions