0

I'm having an issue with the way AngularJS handles transcluded scopes for directives. It is known that a transclude's scope is a sibling of its directive's scope, not a child. Shown here

However, I have a situation with a child directive:

 <div price-chart>
      <div volume-indicator></div>
 </div>

The priceChart directive has a transclude: true, template: "<div ng-transclude></div>" but the (transcluded) volumeIndicator requires the parent to be a priceChart, not the sibling.

function VolumeIndicatorDirective() {
    return {
        restrict: "EA",
        controller: "VolumeIndicatorController",
        require: ["^priceChart", "volumeIndicator"],
        compile: function compile(element, attrs, transclude) {
            return {
                pre: function preLink($scope, element, attrs, controllers) {
                    var priceChart = controllers[0];
                    var volumeIndicator = controllers[1];
                    priceChart.addIndicator(volumeIndicator);
                },
                post: angular.noop
            };
        }
    };
}

If Angular had a sibling selector for controllers that would solve the issue:

require: ["+priceChart", "volumeIndicator"],

However, this doesn't exist so what can I do?

As per comment from zeroflagL I tried element.parent().controller() to access the directive controller but it seems to get the nearest ng-controller specifically and skips over directive controllers.

Community
  • 1
  • 1
parliament
  • 21,544
  • 38
  • 148
  • 238
  • From the docs: "Locate the required controller by searching the ELEMENT'S parent" - The scope doesn't matter. – a better oliver May 14 '14 at 07:31
  • thanks for the comment. I tried this and it seems to skip over directive controllers and gets the nearest parent `ng-controller`. Tried element.controller() and every possible parent (parent(), parent().parent(), etc). updated OP – parliament May 14 '14 at 07:50
  • What I meant was that your code should work. What error do you get? And as for `element.parent().controller()` - It has to be `element.parent().controller('priceChart')`. – a better oliver May 14 '14 at 07:55

1 Answers1

2

According to the docs, require's '^' syntax will try to "locate the required controller by searching the element's parents". Scope prototypal inheritance doesn't play a role here, so your code should work as expected.

And indeed it does !

gkalpak
  • 47,844
  • 8
  • 105
  • 118
  • You're right, actually it was an even higher level parent directive causing the issue by use of "templateUrl" http://jsfiddle.net/hG2c7/5/. This causes the directive to load asynchronously and breaks the parent/child linking order, making volumeIndicator link before its parent, and therefore missing the controller. This is described very well in this answer http://stackoverflow.com/a/22081483/1267778 – parliament May 14 '14 at 18:51
  • Unfortuantely templateUrl still loads async even when it's included using a static template script tag. The way I got around this by firing an event from the parent when it was done loading and listening to the event on the child to proceed. – parliament May 14 '14 at 18:54