简体   繁体   中英

AngularJS: Requiring a parent directive from child directive

Please consider this Plunk .

I'm trying to set up a test case for complex directive access, but I get an error calling a method from the parent directive:

Parent directive

app.directive('topParentDirective', [
    '$compile',
    function($compile){
      return {
        restrict: 'E',
        transclude: true,
        template: '<h3>I\'m the parent directive.</h3><div ng-transclude></div>',
        controller: function($scope) {
          $scope.ActivateMe = function(callerName) {
            alert('Parent activated from caller ' + callerName);
          };
        }
      };
    }
]);

Child directive

app.directive('interactingChildDirective', [
    '$compile',
    function($compile){
      return {
        scope: {
          name: '@'
        },
        restrict: 'E',
        require: ['^topParentDirective'],
        templateUrl: 'interactingChildDirective.html',
        link: function($scope, $elem, $attrs, $ctrl) {
          var self = {};

          console.log($ctrl);

          $scope.CallTopParent = function() {
            $ctrl.ActivateMe($attrs.name);
          };
        }
      };
    }
]);

InteractingChildDirective.html

Contains:

My name is {{name}}, <button ng-click="CallTopParent()">Click me</button>!

Html

<body ng-app="ngApp">
<div ng-controller="myController">
  <top-parent-directive>
    <interacting-child-directive name="Child 1"></interacting-child-directive>
  </top-parent-directive>
</div>
</body>

Issue

TypeError: $ctrl.ActivateMe is not a function
    at n.$scope.CallTopParent 

Which is the case because $ctrl doesn't seem to be correct.

How can I fix this? It's likely something ridiculously easy ...

It should be

    controller: function($scope) {
      this.ActivateMe = function(callerName) {
        alert('Parent activated from caller ' + callerName);
      };
    }

Because $ctrl gets required controller's this .

Because you have nested the child in the parents controller you can access it's scope by using

$scope.$parent

in your case:

$scope.$parent.ActivateMe($attrs.name);

Plunker: http://plnkr.co/edit/YyppT9pWnn1PFWJXBAOF?p=info

The answer by estus, combined by the comments, works. To be complete, a working sample of the scenario I was aiming for:

Plunkr sample .

Updated Html

<body ng-app="ngApp">
    <div ng-controller="myController">
      <top-parent-directive>

        <interacting-child-directive name="Child 1">

          <meaningless-level-directive header="Sub 1">
            <interacting-child-directive name="Child 3"/>
          </meaningless-level-directive>

        </interacting-child-directive>

        <interacting-child-directive name="Child 2">

          <meaningless-level-directive header="Sub 2">
            <interacting-child-directive name="Child 4"/>
          </meaningless-level-directive>

        </interacting-child-directive>

      </top-parent-directive>
    </div>
</body>

meaninglessLevelDirective

As the name suggests this is just to add an extra level:

app.directive('meaninglessLevelDirective', [
  '$compile',
  function($compile){
    return {
      scope: {
        header: '@'
      },
      restrict: 'E',
      transclude: true,
      templateUrl: 'meaninglessLevelDirective.html',
      controller: function($scope){
      }
    };
  }
]);

meaninglessLevelDirective.html

<div class="meaninglessLevelStyle">
  {{header}}
  <div style="padding: 10px" ng-transclude>
  </div>  
</div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM