Directives Talking to Controllers

The initial setup:

<body ng-app="coolApp" ng-controller="FunCtrl as fun">
  <div entering>Hover over me for fun times</div>
</body>
function FunCtrl() {
  var self = this;

  self.start = function() {
    console.log("Fun times have been started!");
  }

}


angular.module('coolApp', [])
.controller('FunCtrl', FunCtrl)
.directive("entering", function(){
 return function(scope, element, attrs) {
      element.bind("mouseenter", function(){
        scope.fun.start();
      })
    }
});

We get the intended functionality of the enter directive mouseover calling the start() method on the fun controller (note that FunCtrl is available to us as fun with controller as). But that’s not the best way to do it, because you’re not sure that scope in the controller is the same as scope in the directive, and you’re not sure that the start() method call in the directive is bound to the declaration in the fun controller.

Ideally, we want to be able to call the fun.start() method from the view without the directive function having to know about the fun.start() method in the controller.

Instead of invoking the method on the scope object in the directive, we can utilize the scope.$apply() method.

scope.fun.start();

becomes

scope.$apply("fun.start()");

scope.$apply() parses the passed string and finds the method within scope. That’s the 10,000 foot view, anyway. Jim Hoskins has an excellent blog entry on it here: http://jimhoskins.com/2012/12/17/angularjs-and-apply.html Using $scope.apply() is slightly improved from before, but it is better practice to decouple the fun.start() method entirely by passing it to the directive as a string parameter in the view, and retrieving it from the attrs parameter in the directive.

The directive with the string attribute:

<div entering="fun.start()">Hover over me for fun times</div>

This makes the string parameter available to us as attrs.entering in the directive

scope.$apply(attrs.entering);

Now we can reuse the entering directive to call different methods in the controller.

The controller becomes:

function FunCtrl() {
  var self = this;

  self.start = function() {
    console.log("Fun times have been started!");
  }

  self.end = function() {
    console.log("Fun time is over.");
  }

}

And the directive value becomes:

<div entering="fun.end()">Hover over me for fun times</div>

Which allows us to call the fun.end() method in the view without modifying the directive.