How to require a controller in an angularjs directive

134,881

Solution 1

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

Solution 2

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';
}
Share:
134,881
Subtubes
Author by

Subtubes

Updated on April 19, 2020

Comments

  • Subtubes
    Subtubes about 4 years

    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?

  • CMCDragonkai
    CMCDragonkai almost 11 years
    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.
  • Josh David Miller
    Josh David Miller almost 11 years
    @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?
  • CMCDragonkai
    CMCDragonkai almost 11 years
    Yep ended up doing the first option. Using a container directive controller. Works great. It's for Masonry.
  • coblr
    coblr over 10 years
    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.
  • jedd.ahyoung
    jedd.ahyoung over 9 years
    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.)
  • jsbisht
    jsbisht over 8 years
    Using same controller in two directives does not give each directive it's own instance.