How to communicate between directives in AngularJS

How to communicate between directives in AngularJS

require property

Note: I wrote this article in 2016 and it serves for archival purposes. There’s a new version of Angular and AngularJS is now in Long Term Support mode.

Photo by [Markus Winkler](https://cdn.hashnode.com/res/hashnode/image/upload/v1621430194121/lbJtRcyUj.html) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)Photo by Markus Winkler on Unsplash

I have stumbled across a problem at work recently when I needed directives to get some information from outside (parent element or controller). Imagine several buttons that represent directives. When I click a button it should increment its value by 1. This is fairly simple but I needed the buttons to be dependent on each other so they increment values continuously just by 1 and prevent further clicking. The following image illustrates what I mean:

incrementing values in buttons in any orderincrementing values in buttons in any order

I didn’t know how to tackle this problem but my colleague came to my rescue. He introduced me to a ‘require’ property of the directive and told me I have to create a parent directive with a controller which we will use in our link function later on.

Here’s a code for the button directive (let’s call it priorityNumDirective):

function priorityNumDirective() {
  return {
    restrict : ‘E’,
    scope: true,
    template: ‘<button class=\’btn btn-default\’ type=\’submit\’ ng-    
    click=\’increaseTapIndex()\’>{{tapIndex}}</button>’,
    **require: [‘^prioritynumContainer’,’ngModel’],**
    link : function (scope, elem, attr) {
      // logic to implement
    }
 };

I assume you are quite familiar with directives and know the other properties, but I am gonna quickly summarize what we are doing.

  • restrict: ‘E’ : we are gonna use elements

  • scope: true : it creates a new scope for the directive (also inherit parent scope)

  • template : defined button element with a function for incrementing values

  • *require: [‘^prioritynumContainer’,’ngModel’] We require 2 controllers here. The first one is a controller of a parent directive which we are going to create in a minute. The second one is ngModel controller that provides methods we are gonna use. *A ^ prefix make the directive look for the controller on its own element or its parents; without any prefix, the directive would look on its own element only.

Let’s create the parent directive called priorityNumContainerDirective and its controller:

function priorityNumContainerDirective() {
  return {
    restrict : ‘A’,
    controller: priorityNumController
  };
}

function priorityNumController() {
  var priorities = [];    
  return {
    getMaxPriority : function(index) {
      var prioLength = 0;
      priorities.push(index);
      prioLength = priorities.length;
      return prioLength; 
    }
  };
}

When we click a button we call a method from parent controller which returns a ‘max’ numberWhen we click a button we call a method from parent controller which returns a ‘max’ number

So we created a parent directive and its controller. We return a function getMaxPriority which we can use in the link function of the button directive (priorityNumDirective). We require 2 controllers as I described above, but we also have to use the 4th parameter in the link function in order to use them. Check out the following example:

function priorityNumDirective() {
  return {
    restrict : ‘E’,
    scope: true,
    template: ‘<button class=\’btn btn-default\’ type=\’submit\’ ng-    
    click=\’increaseTapIndex()\’>{{tapIndex}}</button>’,
    **require: [‘^prioritynumContainer’,’ngModel’],**
    link : function (scope, elem, attr, **controllerArray**) {
      **var prioritynum = controllerArray[0];
      var ngModelCtrl = controllerArray[1];**
      scope.incremented = false;
      scope.tapIndex = 0;
      scope.increaseTapIndex = function() {
        if(scope.incremented === false) {
          scope.incremented = true;
          scope.tapIndex = 
          **prioritynum.getMaxPriority(scope.tapIndex);**
          ngModelCtrl.$setViewValue(scope.tapIndex);
        };
      }
    };

This link function above isn’t complete, but it should be clear by now how to require controllers from parent directive and thus communicate with children directives. Hope it helps and you learnt something new as I did :-)