I cannot seem to find a way to call a function on the parent scope from within a directive without using isolated scope. I know that if I use isolated scope I can just use \
Since the directive is only calling a function (and not trying to set a value on a property), you can use $eval instead of $parse (with a non-isolated scope):
scope.$apply(function() {
scope.$eval(attrs.confirmAction);
});
Or better, simply just use $apply, which will $eval()uate its argument against the scope:
scope.$apply(attrs.confirmAction);
Fiddle
You want to make use of the $parse service in AngularJS.
So for your example:
.directive('confirm', function ($parse) {
return {
restrict: 'A',
// Child scope, not isolated
scope : true,
link: function (scope, element, attrs) {
element.bind('click', function (e) {
if (confirm(attrs.confirm)) {
scope.$apply(function(){
// $parse returns a getter function to be
// executed against an object
var fn = $parse(attrs.confirmAction);
// In our case, we want to execute the statement in
// confirmAction i.e. 'doIt()' against the scope
// which this directive is bound to, because the
// scope is a child scope, not an isolated scope.
// The prototypical inheritance of scopes will mean
// that it will eventually find a 'doIt' function
// bound against a parent's scope.
fn(scope, {$event : e});
});
}
});
}
};
});
There is a gotcha to do with setting a property using $parse, but I'll let you cross that bridge when you come to it.
Explicitly call hideButton
on the parent scope.
Here's the fiddle: http://jsfiddle.net/pXej2/5/
And here is the updated HTML:
<div ng-app="myModule" ng-controller="myController">
<input ng-model="showIt"></input>
<button ng-hide="$parent.hideButton()" confirm="Are you sure?" confirm-action="doIt()">Do It</button>
</div>
The only problem I have is, how do I call a method on the parent scope if I don't pass it in on on isolated scope?
Here is a jsfiddle where I am NOT using isolated scope and the ng-hide is working fine, but, of course, the call to confirmAction() doesn't work and I don't know how to make it work.
First a small point: In this case the outer controller's scope is not the parent scope of the directive's scope; the outer controller's scope is the directive's scope. In other words, variable names used in the directive will be looked up directly in the controller's scope.
Next, writing attrs.confirmAction()
doesn't work because attrs.confirmAction
for this button,
<button ... confirm-action="doIt()">Do It</button>
is the string "doIt()"
, and you can't call a string, e.g. "doIt()"()
.
Your question really is:
How do I call a function when I have its name as a string?
In JavaScript, you mostly call functions using dot notation, like this:
some_obj.greet()
But you can also use array notation:
some_obj['greet']
Note how the index value is a string. So, in your directive you can simply do this:
link: function (scope, element, attrs) {
element.bind('click', function (e) {
if (confirm(attrs.confirm)) {
var func_str = attrs.confirmAction;
var func_name = func_str.substring(0, func_str.indexOf('(')); // Or func_str.match(/[^(]+/)[0];
func_name = func_name.trim();
console.log(func_name); // => doIt
scope[func_name]();
}
});
}