Optional expression attribute in AngularJS directi

2020-03-09 09:35发布

问题:

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

回答1:

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.



回答2:

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];



回答3:

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/



回答4:

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.