2

I'm wondering if there is a way to require the controller of a directive that exists/is nested somewhere as a common parent's child directive in AngularJS.

Directive Structure

Suppose I have the following structure for my directives:

<parent-directive>
    <ul>
        <li some-nested-directive ng-repeat="dir in directives"></li>
    </ul>
    <settings-menu></settings-menu>
</parent-directive>

Directive Definition

/* 
* some-nested-directive directive definition
*/
.directive('someNestedDirective', function(){
    // ...
    return {
       restrict: 'A',
       controller: someNestedDirectiveController
    };
});

/* 
* settings-menu directive definition
*/
.directive('settingsMenu', function(){
    return {
        restrict: 'AE',
        require: [], // how to require the nested-directive controller here?
        link: function(scope, iElement, attrs, ctrls){
            // ...
        }
    };
})

I've already checked out this SO question which states how to require controllers of directives that exist along the same line in a hierarchy.

But my question is regarding a way to do the same in a hierarchy of directives that NOT necessarily exist along the same line. And if this is not possible, what is a proper workaround for it. Any help would be appreciated.

EDIT

Also, can any of the prefixes for require (or a combination of them) be used to achieve the same?

1
  • Have you considered using service to communicate between the directives? Because as far as I know there no direct way to achieve what you want. Commented Dec 30, 2015 at 10:17

2 Answers 2

2

One approach is to use parent directive as a way to pass references between controllers:

var mod = angular.module('test', []);

mod.directive('parent', function() {
  return {
    restrict: 'E',
    transclude: true,
    template: '<div>Parent <div ng-transclude=""></div></div>',
    controller: function ParentCtrl() {}
  }
});

mod.directive('dirA', function() {
  return {
    restrict: 'E',
    template: '<div>Dir A <input type="text" ng-model="name"></div>',
    require: ['dirA', '^^parent'],
    link: function(scope, element, attrs, ctrls) {
      //here we store this directive controller into parent directive controller instance
      ctrls[1].dirA = ctrls[0];
    },
    controller: function DirACtrl($scope) {
      $scope.name = 'Dir A Name';
      this.name = function() {
        return $scope.name;
      };
    }
  }
});

mod.directive('dirB', function() {
  return {
    restrict: 'E',
    template: '<div>Dir A <button ng-click="click()">Click</button></div>',
    require: ['dirB', '^^parent'],
    link: function(scope, element, attrs, ctrls) {
      //let's assign parent controller instance to this directive controller instance
      ctrls[0].parent = ctrls[1];
    },
    controller: function DirBCtrl($scope) {
      var ctrl = this;
      $scope.click = function() {
        //access dirA controller through parent
        alert(ctrl.parent.dirA.name());
      };
    }
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>


<div ng-app='test'>
  <parent>
    <dir-a></dir-a>
    <dir-b></dir-b>
  </parent>
</div>

When using above approach it also makes sense to encapsulate how the dirA controller is stored inside parent controller i.e. by using a getter property or by exposing only the required properties of dirA controller.

Sign up to request clarification or add additional context in comments.

7 Comments

Its good approach, but would you think a service will be better to communicate?
Using service may work also but you have to remember that you have to remember that service is basically a singleton so it's going to be harder to use when there are many instances of directives.
Yes, I know that the services are singleton. So you can take the benefit and create messaging service by using Pub/Sub design pattern and control the communication between the directives. And if you don't want to create the messaging service, you can use angular $broadcast/$emit to achieve the same thing.
You can use Pub/Sub but I can't imagine a simple solution where you would have 4 instances of directives and you would only like them to communicate in pairs (DirA_1, DirB_1 under Parent 1) and (DirA_2, DirB_2 under Parent B).
In your case (DirA_1, DirB_1 under Parent 1), basically use require property in both DirA_1, Die_B to get there parent controller because they are in the same hierarchy. But what I understand from the question is imagine your example (DirA_1, DirB_1 under Parent 1) and there is another directive not nested under the parent and want to require the controller of one of the directives that are nested under it. That's why I suggest to use service like messaging. take a look at the plunker I made. plnkr.co/edit/R6hXCY4XmLMUhtpl7OSQ?p=preview
|
0

I aggree with miensol's reply and I recommend that approach but in some cases you may need something like that;

<parent-directive>
<ul>
    <some-nested-directive id="snd1" ng-repeat="dir in directives"></some-nested-directive>
</ul>
<settings-menu some-nested-directive-id="snd1"></settings-menu>

You can access the scope of some-nested-directive using its id from the settings-menu;

$("#" + scope.someNestedDirectiveId).scope()

Once I used this approach to cascade the values of a dropdown according to the choise of another independent dropdown.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.