3

I’m building a directive, I’m calling ‘requires-authorization’ to wrap an ng-if directive. I’d like to use it as follows:

<requires-authorization role='SuperUser'>
<!— super secret user stuff goes here, within
   the scope of this view's controller —>
</requires-authorization>

I’ve gotten as far as:

angular.module('myApp').directive('requiresAuthorization', function() {
   return {
    template: '<div ng-if=\'iAmInRole\' ng-transclude></div>',
    restrict: 'E',
    transclude: true,
    scope: {
        role: '@'
    },
    controller: function($scope, UserService) {
       $scope.iAmInRole = (UsersService.myRoles.indexOf($scope.role) !== -1);
    }
  };
});

This works, but the content contained within the directive loses its scope, specifically the scope of the controller of the view it's found within. What am I overlooking?

jsfiddle for reference: http://jsfiddle.net/HbAmG/8/ Notice how the auth value isn't displayed inside the directive, but is available outside directive.

2
  • Comma is missing after the template value only in your snippet or in your code as well? Commented Apr 21, 2014 at 16:21
  • Yeah, I also forgot to include the scope block. Editing now... Commented Apr 21, 2014 at 16:22

3 Answers 3

4

Both ng-if and ng-transclude directives perform transclusion in your directive. In this case build-in transclude mechanism does not work fine and you should implement ngIf of yourself to make it work as expected:

JavaScript

app.directive('requiresAuthorization', function () {
    return {
        template: '<div ng-transclude></div>',
        restrict: 'E',
        transclude: true,
        scope: {
            role: '@'
        },
        controller: function ($scope) {
            $scope.iAmInRole = true;
        },
        link: function(scope, element, attr, ctrl, transcludeFn) {
            transcludeFn(function(clone) { // <= override default transclude
                element.empty();
                if(scope.iAmInRole) { // <= implement ngIf by yourself
                  element.append(clone);
                }
            });
        }
    };
});

Plunker: http://plnkr.co/edit/lNIPoJg786O0gVOoro4z?p=preview

If ng-show is an option for you to use instead of ng-if it may be a very simple workaround as well. The only side effect is that hidden data will be presented in the DOM and hidden using CSS .ng-hide {display: none !important;}.

JSFiddle: http://jsfiddle.net/WfgXH/3/

This post may also be useful for you since it describes the similar issue: https://stackoverflow.com/a/22886515/1580941

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

3 Comments

You get the transclude function passed into the link function, so there's no need for the controller. And be careful: @ExpertSystem might downvote your answer ;)
@zeroflagL Thanks, I've just forgot about the option of using fifth argument of linking function. Updating the post accordingly.
@Vadim I don't suppose this is documented anywhere? I've gone through much of the angularjs guide and couldn't find any notes on nesting transclusions or not to use ng-if in directives, etc...
2

Once you define the scope property in your directive, it becomes an isolated scope. With no access to the outside (well, in a way, the only way is ugly and should be avoided), except to the stuff you pass into it via the scope property.

You'll need to either pass them into the directive: updated your jsfiddle

<requires-authorization role='Admin' data-auth-value='authValue' data-unauth-value='unAuthValue'>
  <div>Inside directive. For Admin eyes only</div>
  <p>{{authValue}}</p>
</requires-authorization>

// your directive scope
scope: {
  role: '@',
  authValue: '=',
  unauthValue: '='
}

Or create a service/factory to act as a middle man to communicate.

1 Comment

This option is rather simple, but be aware of a serious limitation: it does not propagate the whole outer scope to transcluded one, and each time someone wants to use another property of outer scope in transcluded context - implementation of directive should be changed, making it not reusable for general purpose.
2

You use ng-if. It does transclusion as well, unfortunately using a child scope of it's own scope, which in turn is the isolate scope.

Below are the screenshots from Batarang. The first is your code with ng-if. 4 is the isolate scope, 6 the transcluded content.

Scopes with ng-if

The same without ng-if. The transcluded content is now 5 and a sibling of the isolate scope and, more importantly, child of the controller's scope.

Scopes without ng-if

7 Comments

Do you have a recommendation/work-around on what to use instead?
@JagWire Forget ng-if. Easy solution: use ng-show. Advanced solution: requiresAuthorization does what ng-if does itself. That's what's usually done by those kind of directives.
@zeroflagL: I really wonder how you came u with all that stuff. It has nothing to do with ngIf.
@zeroflagL: I did (and I read your answer) and still didn't learn anything.
@ExpertSystem Respect! It takes courage to admit that. I've added screenshots. And if you don't mind, please, give me my points back now.
|

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.