Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access parent directive's controller by requiring it recursively?

Tags:

I'm trying to reach the controller of a parent "box" directive recursively:

<body ng-app="main">

<!-- no nesting: parent is the just body -->
<box></box>

<script type="text/javascript">
angular.module('main', [])
.directive('box', function() {
    return {
        restrict: 'E',
        controller: function() { },
        require: '?^box',  // find optional PARENT "box" directive
        link: function(scope, iElement, iAttrs, controller) {
            // controller should be undefined, as there is no parent box
            alert('Controller found: ' + (controller !== undefined));
        }
    };
});
</script>
</body>

I'd expect controller variable to be undefined in the link function, but I get the controller of the actual box directive.

So my question is ... how to gain access to the PARENT controller in case like this:

<box>
    <box></box>
</box>

http://jsfiddle.net/gjv9g/1/

like image 961
maX Avatar asked Oct 13 '13 00:10

maX


People also ask

How do you access child controller scope in parent controller?

In angular there is a scope variable called $parent (i.e. $scope. $parent). $parent is used to access parent scope from child controller in Angular JS.

Which event should be used for passing data from child controller to parent controller?

Using $broadcast and $emit $broadcast always use to pass data from Parent Controller to child controller (Down direction) and $emit service use to pass data. From child controller to parent controller (Up direction).

What is restrict option in directive?

Note: When you create a directive, it is restricted to attribute and elements only by default. In order to create directives that are triggered by class name, you need to use the restrict option. The restrict option is typically set to: 'A' - only matches attribute name. 'E' - only matches element name.

What is attrs in AngularJS?

Using attrs you are able to access the attributes defined in your html tag like <fm-rating ng-model="$parent.restaurant.price" symbol="$" readonly="true"> So in this case you will have access to the symbol and readonly attributes.


2 Answers

Since Angular 1.3 you can use two accents ^^ to search for a directive in parent elements "only".

Quote from the Angular Docs on require:

  • (no prefix) - Locate the required controller on the current element. Throw an error if not found.
  • ? - Attempt to locate the required controller or pass null to the link fn if not found.
  • ^ - Locate the required controller by searching the element and its parents. Throw an error if not found.
  • ^^ - Locate the required controller by searching the element's parents. Throw an error if not found.
  • ?^ - Attempt to locate the required controller by searching the element and its parents or pass null to the link fn if not found.
  • ?^^ - Attempt to locate the required controller by searching the element's parents, or pass null to the link fn if not found.

In your case, replace require: '?^box', with require: '?^^box',

like image 146
Joel Kornbluh Avatar answered Sep 29 '22 06:09

Joel Kornbluh


OK, found it...

if you want to get hold of the controller of a parent element:

...
link: function(scope, iElement, iAttrs, controller) {
    // http://docs.angularjs.org/api/angular.element
    // jQuery/jqLite Extras:
    //
    // 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').
    var parentCtrl = iElement.parent().controller('box');
}
...

This returns either controller of parent directive or, one level higher, the controller of parent's parent directive, If you need to make sure you get a controller of a DIRECT parent, I found this (maybe there is a better solution, I don't know):

...
controller: function($scope, $element) {
    // store the element in controller, we'll need it later
    this.$element = $element;
},
// works in both pre and post link functions
link: function() {
    var parentElement = $element.parent();
    var parentCtrl = parentElement.controller('box');

    var hasDirectBoxParent = parentCtrl && parentCtrl.$element[0] === parentElement[0];

}
...

Example 1:

<box id="a">
    <box id="b"></box>
<box>

When the link function is invoked on "box a", parentCtrl is undefined in both cases. When the link function is invoked on "box b", parentCtrl is the controller of "box a" in both cases.

Example 2:

<box id="a">
    <div>
        <box id="b"></box>
    </div>
<box>

When the link function is invoked on "box a", parentCtrl is undefined in both cases. When the link function is invoked on "box b", parentCtrl is still the controller of "box a" in both cases, but hasDirectBoxParent is false, so you can distinguish parent from a grandparent.

like image 29
maX Avatar answered Sep 29 '22 08:09

maX