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) || ...">
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.
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];
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/
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.