Call a controller function from a directive without isolated scope in AngularJS

Jim Cooper picture Jim Cooper · Jul 11, 2013 · Viewed 91.2k times · Source

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 "&" in the isolated to access the function on the parent scope, but using isolated scope when it isn't necessary has consequences. Consider the following HTML:

<button ng-hide="hideButton()" confirm="Are you sure?" confirm-action="doIt()">Do It</button>

In this simple example, I want to show a JavaScript confirm dialog and only call doIt() if they click "OK" in the confirmation dialog. This is simple using an isolated scope. The directive would look like this:

.directive('confirm', function () {
    return {
        restrict: 'A',
        scope: {
            confirm: '@',
            confirmAction: '&'
        },
        link: function (scope, element, attrs) {
            element.bind('click', function (e) {
                if (confirm(scope.confirm)) {
                    scope.confirmAction();
                }
            });
        }
    };
})

But the problem is, because I'm using isolated scope, ng-hide in the example above no longer executes against the parent scope, but rather in the isolated scope (since using an isolated scope on any directive causes all directives on that element to use the isolated scope). Here is a jsFiddle of the above example where ng-hide is not working. (Note that in this fiddle, the button should hide when you type "yes" in the input box.)

The alternative would be to NOT use an isolated scope, which actually is what I really want here since there is no need for this directive's scope to be isolated. 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.

Please note, the answer I am really looking for is how to call functions on the outer scope WITHOUT using an isolated scope. And I am not interested in making this confirm dialog work in another way, because the point of this question is to figure out how to make calls to the outer scope and still be able to have other directives work against the parent scope.

Alternatively, I would be interested to hear of solutions that use an isolated scope if other directives will still work against the parent scope, but I don't think this is possible.

Answer

Mark Rajcok picture Mark Rajcok · Jul 11, 2013

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