90

Can anyone tell me how to include a controller from one directive in another angularJS directive. for example I have the following code

var app = angular.module('shop', []).
config(['$routeProvider', function ($routeProvider) {
    $routeProvider.when('/', {
        templateUrl: '/js/partials/home.html'
    })
        .when('/products', {
        controller: 'ProductsController',
        templateUrl: '/js/partials/products.html'
    })
        .when('/products/:productId', {
        controller: 'ProductController',
        templateUrl: '/js/partials/product.html'
    });
}]);

app.directive('mainCtrl', function () {
    return {
        controller: function ($scope) {}
    };
});

app.directive('addProduct', function () {
    return {
        restrict: 'C',
        require: '^mainCtrl',
        link: function (scope, lElement, attrs, mainCtrl) {
            //console.log(cartController);
        }
    };
});

By all account I should be able to access the controller in the addProduct directive but I am not. Is there a better way of doing this?

Eliran Malka
  • 15,821
  • 6
  • 77
  • 100
Subtubes
  • 15,851
  • 22
  • 70
  • 105
  • 5
    `require` ensures the presence of another directive and then includes its controller. `^require` checks elements above the current one in addition to the current element. So you have to use the two directives together for this to work. Otherwise, just define a controller with `app.controller` and then use it in both directives. Either way, can you put this into a simple Plunker along with your HTML code? – Josh David Miller Mar 28 '13 at 01:53

2 Answers2

188

I got lucky and answered this in a comment to the question, but I'm posting a full answer for the sake of completeness and so we can mark this question as "Answered".


It depends on what you want to accomplish by sharing a controller; you can either share the same controller (though have different instances), or you can share the same controller instance.

Share a Controller

Two directives can use the same controller by passing the same method to two directives, like so:

app.controller( 'MyCtrl', function ( $scope ) {
  // do stuff...
});

app.directive( 'directiveOne', function () {
  return {
    controller: 'MyCtrl'
  };
});

app.directive( 'directiveTwo', function () {
  return {
    controller: 'MyCtrl'
  };
});

Each directive will get its own instance of the controller, but this allows you to share the logic between as many components as you want.

Require a Controller

If you want to share the same instance of a controller, then you use require.

require ensures the presence of another directive and then includes its controller as a parameter to the link function. So if you have two directives on one element, your directive can require the presence of the other directive and gain access to its controller methods. A common use case for this is to require ngModel.

^require, with the addition of the caret, checks elements above directive in addition to the current element to try to find the other directive. This allows you to create complex components where "sub-components" can communicate with the parent component through its controller to great effect. Examples could include tabs, where each pane can communicate with the overall tabs to handle switching; an accordion set could ensure only one is open at a time; etc.

In either event, you have to use the two directives together for this to work. require is a way of communicating between components.

Check out the Guide page of directives for more info: http://docs.angularjs.org/guide/directive

Josh David Miller
  • 120,525
  • 16
  • 127
  • 95
  • 4
    Is it possible to require a sibling directive controller? Basically I need to share the same instance of a controller or service between sibling directives (as in DOM siblings, not on the same DOM element) that is repeated using ng-repeat. Imagine each repeated item has a directive that needs a shared state or logic between them. – CMCDragonkai Aug 08 '13 at 07:53
  • 2
    @CMCDragonkai There's no way to do that, but there are two common ways of accomplishing the same thing. The first is if the siblings are all of the same "type" then the element above the ngRepeat can be like a container directive and all the sub-elements can then require that directive instead, all sharing the same controller. The more common solution - and often more canonical - is to use a shared service. Can you elaborate on what these siblings do and what they need to share? – Josh David Miller Aug 08 '13 at 17:04
  • Yep ended up doing the first option. Using a container directive controller. Works great. It's for Masonry. – CMCDragonkai Aug 09 '13 at 05:38
  • This is a great answer and has solidified my understanding of how all of this works. Thanks! (As a note, this may be a newer feature, but you can use `require` to specify a single directive, or an array of directives; each directive can be prefixed with a caret(`^`) for more granular requirements.) –  Nov 27 '14 at 19:56
  • Using same controller in two directives does not give each directive it's own instance. – jsbisht Feb 14 '16 at 17:28
30

There is a good stackoverflow answer here by Mark Rajcok:

AngularJS directive controllers requiring parent directive controllers?

with a link to this very clear jsFiddle: http://jsfiddle.net/mrajcok/StXFK/

<div ng-controller="MyCtrl">
    <div screen>
        <div component>
            <div widget>
                <button ng-click="widgetIt()">Woo Hoo</button>
            </div>
        </div>
    </div>
</div>

JavaScript

var myApp = angular.module('myApp',[])

.directive('screen', function() {
    return {
        scope: true,
        controller: function() {
            this.doSomethingScreeny = function() {
                alert("screeny!");
            }
        }
    }
})

.directive('component', function() {
    return {
        scope: true,
        require: '^screen',
        controller: function($scope) {
            this.componentFunction = function() {
                $scope.screenCtrl.doSomethingScreeny();
            }
        },
        link: function(scope, element, attrs, screenCtrl) {
            scope.screenCtrl = screenCtrl
        }
    }
})

.directive('widget', function() {
    return {
        scope: true,
        require: "^component",
        link: function(scope, element, attrs, componentCtrl) {
            scope.widgetIt = function() {
                componentCtrl.componentFunction();
            };
        }
    }
})


//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});

function MyCtrl($scope) {
    $scope.name = 'Superhero';
}
Community
  • 1
  • 1
Joseph Oster
  • 5,507
  • 1
  • 20
  • 11
  • 4
    For me, what made Mark Rajcok's example click the most was paying attention to how the controller methods are created. Typically you see controller methods created via $scope.methodName = function(){...}, but in order for this to work, you must use this.methodName for the methods you want accessible. I didn't notice that at first. – coblr Mar 19 '14 at 00:57