Inheritance is a powerful way to extend directives functionality and at the same time, improve code reuse. Lets see how it works in Angular with a simple example. The first step is to create a directive called “outer” which is going to be our top-level directive in our hierarchy.

angular.module('myApp') .directive('outer', function(){ return { restrict: 'E', controllerAs: 'outer', require: 'outer', template: '<p>outer</p>', scope: true, controller: function(){ this.sayHello = function(){ console.log('outer: hello'); }; }, link: function(scope, element, attrs, outerCtrl){ element.on('click', function(event){ outerCtrl.sayHello(); }); } }; });

Our first directive defines a controller and a link function, this way we can separate concerns: the controller deals with the logic of the directive while the link function is in charge of DOM manipulation. Another benefit of this approach is that the controller of our directive could be shared to child directives.

Because we need to access the logic of the controller inside our link function, we need to set the directive property require: ‘outer’ defining that we are going to need to “inject” the controller of the directive called “outer”, that in this case happens to be the controller of the directive that we are creating. The injected controller is available in the link function as the fourth argument outerCtrl

At this point when we click our directive, the message “outer: hello” is shown in the browser console.

The HTML and CSS of this example are the following:

<body ng-app="myApp"> <outer></outer> <!-- script tags --> </body>

outer { display: block; background-color: yellow; border: 5px solid yellow; }

Now lets create another directive that we are going to call “inner”.

angular.module('myApp') .directive('inner', function(){ return { restrict: 'E', template: '<p>inner</p>', require: ['^outer', 'inner'], scope: true, controller: function(){ this.sayWelcome = function(){ console.log('inner: welcome'); }; }, link: function(scope, element, attrs, ctrls){ var outerCtrl = ctrls[0]; var innerCtrl = ctrls[1]; element.on('click', function(event){ innerCtrl.sayWelcome(); outerCtrl.sayHello(); event.stopPropagation(); }); } }; });

Notice the line require: [‘^outer’, ‘inner’] , here the directive is requiring two different controllers: “outer” and “inner”. Because the “outer” string is prefixed with the symbol “^” our directive will look for that controller in his parents. Now, in the link function we are receiving an array as the fourth argument with both controllers in the same order that were defined in the require array.

When we click the inner directive, it’s going to call first the function “sayWelcome” that is defined in his controller and then will call the “sayHello” function defined in the parent controller. We are stopping the event propagation so the parent “click” method is not called, thus simplifying our example.

In order for this example to work, we need to change our outer directive’s template so it will be referencing the inner directive: template: ‘<p>outer</p><inner></inner>’ .

We also need to add a new CSS rule for our inner directive.

inner { display: block; color: white; background-color: red; border: 5px solid red; }

It’s worth noting that the search of a parent controller using the prefix “^” is not limited to the immediate parent, it will search the DOM hierarchy searching for a controller o directive (with a controller defined) called “outer”.

This is how it looks having a 3-level hierarchy of directives:

angular.module('myApp') .directive('outer', function(){ return { restrict: 'E', require: 'outer', template: '<p>outer</p><middle></middle>', scope: true, controller: function(){ this.sayHello = function(){ console.log('outer: hello'); }; }, link: function(scope, element, attrs, outerCtrl){ element.on('click', function(event){ outerCtrl.sayHello(); }); } }; });

angular.module('myApp') .directive('middle', function(){ return { restrict: 'E', template: '<p>middle</p><inner></inner>', require: ['^outer', 'middle'], scope: true, controller: function(){ this.sayBye = function(){ console.log('middle: bye'); }; }, link: function(scope, element, attrs, ctrls){ var outerCtrl = ctrls[0]; var middleCtrl = ctrls[1]; element.on('click', function(event){ middleCtrl.sayBye(); outerCtrl.sayHello(); event.stopPropagation(); }); } }; });

angular.module('myApp') .directive('inner', function(){ return { restrict: 'E', template: '<p>inner</p>', require: ['^outer', '^middle', 'inner'], scope: true, controller: function(){ this.sayWelcome = function(){ console.log('inner: welcome'); } }, link: function(scope, element, attrs, ctrls){ var outerCtrl = ctrls[0]; var middleCtrl = ctrls[1]; var innerCtrl = ctrls[2]; element.on('click', function(event){ innerCtrl.sayWelcome(); middleCtrl.sayBye(); outerCtrl.sayHello(); event.stopPropagation(); }); } } });

And the CSS files:

outer { display: block; background-color: yellow; border: 5px solid yellow; } middle { display: block; color: white; background-color: blue; border: 5px solid blue; } inner { display: block; color: white; background-color: red; border: 5px solid red; }

Now, when clicking the inner directive…

This is one way to implement a “classic” inheritance model in Angular, in the next post I will show how to emulate an abstract/concrete class inheritance model using directives.