kbd
kbd

Reputation: 4449

Calling a Directive from another Directive

Please consider this tryout on Plunkr.

I have a simple set up:

<body ng-app="myApp">
    <div ng-controller="myController">
      <parent-directive></parent-directive>
      <child-directive></child-directive>
    </div>
</body>

With the parent directive defined like:

app.directive("parentDirective", [
    "$compile",
    function (
        $compile) {
        return {
            scope: {
                person: "="
            },
            restrict: "E",
            template: "<h3>I'm a parent</h3>",
            controller: [
                "$scope",
                function ($scope) {
                    // --- PRIVATE --- //

                    var self = {};

                    $scope.ClickMe = function() {
                      alert('Parent clicked');
                    };
                }],
            link: function ($scope, $elem, $attrs) {
            }
        };
    }]);

And child directive defined like:

app.directive("childDirective", [
    "$compile",
    function (
        $compile) {
        return {
            scope: {
                person: "="
            },
            restrict: "E",
            require: "^?parentDirective",
            template: "<h3>I'm a child, click <button ng-click='ClickMe()'>here</button></h3>",
            controller: [
                "$scope",
                function ($scope) {
                    // --- PRIVATE --- //

                    var self = {};

                    $scope.ClickMe = function() {
                      alert('child clicked');
                      $scope.parentDirective.ClickMe();
                    };
                }],
            link: function ($scope, $elem, $attrs) {
            }
        };
    }]);

The child click is handled, but the click defined on the `parent', returns undefined:

TypeError: Cannot read property 'ClickMe' of undefined

looking at the console.

Any idea what's going wrong?

Upvotes: 1

Views: 1284

Answers (3)

Harbinger
Harbinger

Reputation: 604

I might be thinking about your problem a little differently but I would take a look at $broadcast. The idea is you can broadcast an event and have "n" number of directives in your case listening for that event.

http://plnkr.co/edit/wBmX2TvC3yMXwItfxkgl

brodcast:

$scope.ClickMe = function() {
                  alert('child clicked');
                  $rootScope.$broadcast('child-click');;
                };

listen:

$scope.$on('child-click', function (event, args) {
                    alert('Parent clicked');
                });

Upvotes: 0

user1364910
user1364910

Reputation:

Any idea what's going wrong?

  1. You cannot require a sibling directive.
  2. The required directives controller methods dont get exposed automagically onto your scope.
  3. You should expose methods on the controller itself, not on the assigned $scope.

You can require a directive that is defined on the same element as the requiring directive, or on a parent element.

<child-directive parent-directive></child-directive>

<parent-directive>
  <child-directive></child-directive>
</parent-directive>

When you require the controller (aka. exposed API) of another directive, it doesn't magically end up on the $scope of the requiring directive.

It does however end up in your link function as the fourth argument.

Like so:

.directive('child', function () {
  return {
    require: '?^parentDirective',
    link: function (scope, el, attrs, parentDirectiveController) {
      scope.clickMe = function () {
        parentDirectiveController.clickMe();
      };
    }
  };
});

Expose the methods you want available in other directives onto this instead of $scope, as the $scope way of doing it won't work the way you intend it to when you have isolated scopes.

.directive('parent', 
  controller: function () {
    this.clickMe = function () {};
  }
}

To get your example working;

<parent>
  <child></child>
</parent>

.directive('parent', function () {
  return {
    controller: function () {
      this.clickMe = function () {};
    }
  };
}

.directive('child', function () {
  return {
    require: '^?parent',
    link: function (scope, el, attrs, parentCtrl) {
      scope.clickMe = function () {
        parentCtrl.clickMe();
      };
    } 
  };
}); 

Simplified (& working) version of your plunker: http://plnkr.co/edit/nao4EvbptQm7gDKkmZS2?p=preview

Upvotes: 1

abeach3
abeach3

Reputation: 36

Put your child directive in your parent directive template. Then use $scope.$parent.ClickMe(). Here's what it would look like.

Simple setup:

<body ng-app="myApp">
    <div ng-controller="myController">
        <parent-directive></parent-directive>
    </div>
</body> 

Parent directive:

app.directive("parentDirective", [
    function () {
        return {
            scope: {},
            restrict: "E",
            template: "<h3>I'm a parent</h3><child-directive></child-directive>",
            controller: [
                "$scope",
                function ($scope) {
                    $scope.ClickMe = function() {
                        alert('Parent clicked');
                    };
                }
            ]
        };
    }
]);

Child directive:

app.directive("childDirective", [
    function () {
        return {
            restrict: "E",
            template: "<h3>I'm a child, click <button ng-click='ClickMe()'>here</button></h3>",
            controller: [
                "$scope",
                function ($scope) {
                    $scope.ClickMe = function() {
                        alert('child clicked');
                        $scope.$parent.ClickMe();
                    };
                }
            ]
        };
    }
]);

Upvotes: 0

Related Questions