13

I have a custom navigation directive that needs an optional "disable" attribute, and I'm not sure if it's even possible.

In my main controller:

.controller('NavCtrl', ['UserResource','RoleResource'], function(UserResource,RoleResource){
      var user = UserResource.getUser();
      var roles = RoleResource.getRoles();
      UserService.init(user, roles); //????

});

In my directive:

.directive('navItem', function(){
    return{
        restrict: 'A',
        scope: {
            text: '@',
            href: '@',
            id: '@',
            disable: '&'

        },
        controller: function($scope, $element, $attrs){
            $scope.disabled = ''; //Not sure I even need a controller here
        },
        replace: true,
        link: function(scope, element, attrs){
            scope.$eval(attrs.disable);
        },
        template: '<li class="{{disabled}}"><a href="{{href}}" id="{{id}}">{{text}}</a></li>'

    }

});

In my HTML, I want to do something like this:

<div data-nav-item text="My Text" href="/mytemplate.html" id="idx"
     disable="UserService.hasRole('ADMIN,BILLING') && someOtherFn(xxx) || ...">
Brian McCutchon
  • 8,354
  • 3
  • 33
  • 45
boyceofreason
  • 181
  • 1
  • 2
  • 5

4 Answers4

8

You could make what you have work by chaning your $eval call to

scope.$parent.$eval(attrs.disable);

because you need to evaluate the expression contained in attrs.disable in the parent scope, not the directive's isolate scope. However, since you are using the '&' syntax, it will evaluate the expression in the parent scope automatically. So just do the following instead:

if(angular.isDefined(attrs.disable)) {
    scope.disable();
}

Fiddle.

Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • Sorry, i accidentally pressed ctrl+s.. the fiddle got updated with my changes. Could you please update the fiddle link. Sorry once again. – Rajkamal Subramanian Mar 06 '13 at 04:07
  • @rajkamal, the link above to the fiddle should still take people to the original version, even if you saved a new one. You would have also have had to click the "Base" button to cause people to see your version, so no harm done. – Mark Rajcok Mar 06 '13 at 15:47
  • Wow, seeing the scope.disable(); call really helped me. I didn't grasp the full power of '&' until I saw that. – Dana Cartwright Feb 07 '14 at 14:58
0

One way of doing the same thing is like this http://jsfiddle.net/fFsRr/7.

You may replace the todisableornot="rights[1]" with your expression like this todisableornot="UserService.hasRole('ADMIN,BILLING') && someOtherFn(xxx) || ...".

Now as Mark Rajcok said the property todisableornot will be evaluated in the parent scope whenever you call that property. So

<li ng-class="{adminRole:todisableornot()}"><a href="{{href}}" id="{{id}}">{{text}}</a></li> will apply class adminRole if the todisableornot property evaluates to true (in the context of parent scope).

You can test this by changing the $scope.rights =[true, false];

Rajkamal Subramanian
  • 6,884
  • 4
  • 52
  • 69
0

A more appropriate implementation for this specific problem would be to have an optional binding to a simple model property that has its value assigned from your complex statement. The Angular documentation repeatedly mentions that the best practice is to bind to model properties and not to functions. The "&" binding for directives is primarily useful for implementing callbacks, where you need to passing data from the directive to its parent.

Implementing an optional binding looks like this:

Code:

angular.module("testApp", [])
.controller("testCtrl", ["$scope", function testCtrl($scope) {
    $scope.myFlag = false;
}])
.directive("testDir", function testDir() {
    return {
        restrict: "EA",
        scope: {
            identity: "@",
            optional: "=?"
        },
        link: function testDirLink($scope, $element, $attrs) {
            if (typeof $scope.optional == "undefined") {
                $scope.description = "optional was undefined";
            }
            else {
                $scope.description = "optional = '" + $scope.optional + "'";
            }
        },
        template: "<div>{{identity}} - {{description}}</div>"
    };
});

HTML:

<div ng-app="testApp" ng-controller="testCtrl">
    <test-dir identity="one" optional="myFlag"></test-dir>
    <test-dir identity="two"></test-dir>
</div>

Fiddle: http://jsfiddle.net/YHqLk/

Jack A.
  • 4,245
  • 1
  • 20
  • 34
0

Recently I faced this issue and found that I had multiple references (script tag) of the directive file in index.html. Once I removed these additional references the issue vanished.

Lance Roberts
  • 22,383
  • 32
  • 112
  • 130