AngularJS directive controllers requiring parent d

2020-01-30 04:24发布

问题:

I might be thinking about this completely backwards, but I'm trying to make three nested directives, lets call them: screen, component and widget. I want widget to be able to trigger some behavior in component, which in turn triggers some behavior in screen. So:

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

.directive('component', function() {
    return {
        scope: true,
        controller: function() {
            this.componentFunction = function() {
                WHAT.doSomethingScreeny();
            }
        }
    }
})

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

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

I can require parent components in a widget's link fn using require: "^component", but how do I further give components controller access to its containing screen?

What I need is the WHAT in component so when you click the widget's button it alerts "screeny!".

Thanks.

回答1:

Here are two ways you could solve your problem:

  1. Since you are using scope: true, all scopes prototypically inherit. So if you define your methods on $scope instead of on this in the screen controller, then both component and widget will have access to function doSomethingScreeny.
    Fiddle.
  2. Define a link function on component and require: '^screen'. In the link function, save the screenCtrl to a scope property, then you can access it in the directive's controller (inject $scope).
    Fiddle.


回答2:

Most of this stuffs fails when you want to directly access properties or methods from the parent controller on controller creation. I found another solution by using dependency injection and using the $controller service.

.directive('screen', function ($controller) {
    return {
       require: '^parent',
       scope: {},
       link: function (scope, element, attr, controller) {
           $controller('MyCtrl', {
                $scope: scope,
                $element: element,
                $attr, attr, 
                controller: controller
           });
       }
    }
})

.controller('MyCtrl, function ($scope, $element, $attr, controller) {});

This method is better testable and does not pollute your scope with unwanted controllers.



回答3:

return { scope: true } or return { scope: false } is not affect to $scope variable in controller: function($scope) {} in each directive, but directive tag have to putted into the ng-controller or ng-app tag.

JSFiddle

JSFiddle



回答4:

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

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

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

.directive('widget', function() {
  return {
    scope: true,
    controller: function($scope, $element) {
      $scope.widgetFunction = function() {
        $element.controller('component').componentFunction();
      }
    }
  }
})

.controller('MyCtrl', function($scope) {
  $scope.name = 'Superhero';
})
<body ng-app="myApp">

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

</body>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>

If you want to access a function defined in screen directive controller from component directive controller (not a link function), you can use $element.controller('screen').doSomethingScreeny() (from component directive).

JSFiddle

Angular documentation:

  • controller(name) - retrieves the controller of the current element or its parent. By default retrieves controller associated with the ngController directive. If name is provided as camelCase directive name, then the controller for this directive will be retrieved (e.g. 'ngModel').