Wednesday, July 22, 2015

AngularJS: Communicating Between Controllers and Directives with Isolated Scope

Overview

The problem addressed in this post is a rather common one when developing AngularJS applications. This isn't a particularly difficult problem, but it was one that I struggled with when I started working on medium-sized AngularJS applications... mainly because I didn't have a strong understanding of directives and scope at the time.

Scenario

I have a controller which declares a function called doSomething() on $scope.

angular.module("myApp", [])
    .controller("myController", function ($scope) {
        $scope.doSomething = function() { 
            //something 
        };
    });

I have a custom directive being applied within my controller block.

<div ng-controller="myController">
    <div my-directive></div>
</div>

I want to call my doSomething() function when some event (such as an ng-click) transpires inside my directive. How can I define (and then apply) my directive to accomplish this?

Solution

To do this, I create a directive with an isolated scope. Within the scope declaration, I use '=' to bind a scope property to a function that can then be passed in as an attribute.

angular.module("myApp", [])
    .directive("myDirective", function () {
        return {
            scope: {
                clickFn: '='
            },
            template: "<button ng-click='clickFn()'>Click Me</button>",
            ...
        };
    });

When I apply the directive to an HTML element, I provide an expression (in this case, a function call) to the click-fn attribute.

<div ng-controller="myController">
    <div my-directive click-fn="doSomething()"></div>
</div>

You can use this alternate syntax if you want your directive attribute and scope property to have different names:

scope: {
    onSomethingHappens: '=attrFunction'
},
template: "<button ng-click='onSomethingHappens()'>Click Me</button>",
...
<div ng-controller="myController">
    <div my-directive attr-function="doSomething()"></div>
</div>

Alternate Approach: Use a Service

An alternative to the approach outlined above is to move the function you want to execute into a service. AngularJS services are singletons which can be passed into controllers and directives through dependency injection. I'm not wild about this approach, but it will get the job done. I suppose it is possible to run into scenarios where you need to do this, perhaps because you don't want your directive to have an isolated scope. However, you should be defining an isolated scope if you want your directives to be truly reusable.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.