Code


<!DOCTYPE html> <html> <head> <script src="angular.js"></script> <script src="script.js"></script> </head> <body ng-app="mainModule"> <div ng-controller="mainController"> <div basket-container> <div orange-fruit> <button fruits-checker pear-fruit>Check what we have (1)</button><br /> <button fruits-checker pear-fruit apricot-fruit>Check what we have (2)</button><br /> </div> <div> <button fruits-checker pear-fruit>Check what we have (3)</button><br /> </div> </div> <br /> <strong>What do we have?</strong> {{whatWeHave}} </div> </body> </html>
angular.module("mainModule", []) .controller("mainController", function ($scope) { $scope.whatWeHave = "nothing"; }) .directive("pearFruit", function () { return { controller: function () { this.getFruitName = function () { return "pear"; }; } }; }) .directive("apricotFruit", function () { return { controller: function () { this.getFruitName = function () { return "apricot"; }; } }; }) .directive("orangeFruit", function () { return { controller: function () { this.getFruitName = function () { return "orange"; }; } }; }) .directive("basketContainer", function () { return { controller: function () { this.getBasketSize = function () { return "medium"; }; } }; }) .directive("fruitsChecker", function () { return { require: ['pearFruit', '?apricotFruit', '^basketContainer', '^?orangeFruit', 'fruitsChecker'], controller: function ($scope) { this.setWhatWeHave = function (value) { $scope.whatWeHave = value; $scope.$root.$digest(); }; }, link: function (scope, element, attrs, requiredControllers) { element.on("click", function () { var pearFruitController = requiredControllers[0]; var apricotFruitController = requiredControllers[1]; var basketContainerController = requiredControllers[2]; var orangeFruitController = requiredControllers[3]; var fruitsCheckerController = requiredControllers[4]; var result = ""; // The controller of pearFruit is required // and must be available. result += "a " + pearFruitController.getFruitName(); // The controller of apricotFruit is optional // and might not be available. if (apricotFruitController) { result += ", an " + apricotFruitController.getFruitName(); } // The controller of basketContainer is required // and must be available, while the controller of // orangeFruit is optional. if (orangeFruitController) { result += " and a basket of " + basketContainerController.getBasketSize() + " size with an " + orangeFruitController.getFruitName() + " inside"; } else { result += " and an empty basket of " + basketContainerController.getBasketSize() + " size"; } fruitsCheckerController.setWhatWeHave(result); }); } }; });

Example


Description


In a custom directive we can define our own controller. A controller is useful if we want to expose the API of our directive and make it available to other directives.

A controller can be defined with a factory function assigned to the controller property of the directive's definition object. We can use Dependency Injection with this factory function so if for example we need to access the directive's scope inside it, we can have it by adding $scope among the controller's function arguments, however Dependency Injection here is limited to the $scope, $element (the current DOM node's element), $attrs (the current DOM node's attributes) and $transclude (the transclusion function) arguments. Functions and properties can be exposed by the controller by adding them to the this object inside the controller's factory function.

If we need the controller of another directive inside our custom directive, we can use the require property of our definition object. We can assign a single string or an array of strings to the require property if we need a single controller or multiple controllers respectively. Each string can have one of the following forms:

  • dirName

    The name of the directive with the controller we need. This directive must be available on the same DOM node of our custom directive otherwise AngularJS will throw an error.

  • ?dirName

    The name of the directive with the controller we optionally need preceded by the ? symbol. This directive can be available on the same DOM node of our custom directive or not, it's optional.

  • ^dirName

    The name of the directive with the controller we need preceded by the ^ symbol. This directive must be available on the same DOM node of our custom directive or in any ancestor node otherwise AngularJS will throw an error. The controller will be returned from the first DOM node that contains the directive while traversing the DOM tree from the current node to the root.

  • ^?dirName

    The name of the directive with the controller we optionally need preceded by the ^? symbols. This directive can be available on the same DOM node of our custom directive or in any ancestor node or not available at all, it's optional. The controller will be returned from the first DOM node that contains the directive, if any, while traversing the DOM tree from the current node to the root.

The required controllers can be accessed in the fourth parameter of the link function. This parameter will be a single controller if we've specified just one value in the require property of our directive's definition object, or will be an array if we require multiple controllers. In the second case, each position in the link's function array will correspond to the controller that we've required in the same position of the require property array. If a controller is optional and is not found in the DOM, then it's position in the array will contain an undefined item.

All the possible ways of requesting a controller can be seen in the example. In the fruitsChecker directive we require the controllers of all the other directives and some of them are optional. We even require fruitsChecker's own controller. The link function has four arguments and the last one is called requiredControllers. The controllers are in the same position in the requiredControllers array as they appear in the require property. Some of them are optional so we check if they're defined in the array before using them. By pressing the available buttons you can see the different results depending on the DOM node we're in.