0

I've encountered a situation that seems to be the opposite of every similar question here on StackOverflow.

I've got a <scroll> directive, that simply wraps whatever content it has with a scrollable div of some sort. It looks more or less like this:

.directive('scroll', ['$document','$parse', function ($document,$parse) {
return {
    restrict: 'E',
    replace: true,
    transclude: true,
    scope:false,
    template:
        '<div class="scroll">' +
            '<div class="content" data-ng-transclude>' +
            '</div>' +
        '</div>',
    link: function (scope, element, attr) {
        // some code here....
    }
};
}]);

This works great in itself.

Now, I've got another directive called <editor> that has an isolated scope and uses <scroll> within it's template. <editor> looks more or less like:

.directive('editor', ['$document', function ($document){
return {
    restrict: 'EA',
    replace: true,
    transclude: false,
    scope:true,
    template:
        '<div>' +
            '....<scroll>....</scroll>....' +
        '</div>',
    link: function (scope, element, attrs) {
        .....
    }
};
}]);

Now, here's the deal: I need to access <editor>'s scope from within <scroll>'s link function (where it says "some code here"), but for some reason I can't. The scope variable in <scroll>'s link function is pretty much empty, and scope.$parent gives me the controller above, skipping the <editor> that's supposed to be in the way.

I've tried playing with ng-transclude in different places in the <editor> and tried using $emit and I'm really clueless about this - I would think that scope isolation would isolate "me and everything below me" from what's above, but it seems that scope isolation just takes "me" out of the scope tree...

Hope this is clear enough, thanks.

motig88
  • 402
  • 5
  • 16
  • Have a look at [John Lindquist's video](http://www.egghead.io/video/2CdivtU5ytY) about communicating between directives. This could get you going in the right direction. – Jonathan Palumbo Aug 05 '13 at 17:03
  • You should try not to do it this way because now you're limiting your scroll directive is hard coded to your editor directive. This is a classic case of "coupling" and limits the re-usability of your directives. That being said, there are a ton of ways to do this. The best way is probably using $emit or $broadcast to dispatch events. If you do this properly you can remain "decoupled" and still use your scroll elsewhere. – Jonathan Rowny Aug 05 '13 at 17:28
  • It just hit me why this happens, even though I don't have a solution yet: The thing is, the inner directive's `link` method fires before the parent's, so that's why it seems I can't access the parent's scope from within the `link` function but can from markup with `{{}}`. Maybe the solution is to have the inner directive `$watch` for the upper directive to be ready before using the `$parse(...)(scope)` to get to it. – motig88 Aug 05 '13 at 17:39

1 Answers1

0

The code shown for the editor directive is not creating an isolate scope. Instead, it is creating a new child scope that prototypically inherits from the parent/containing scope.

Since the scroll directive has scope: false, it will use the child scope that editor creates -- no need for $parent.

Yes, the link function for editor will run after the link function for scroll. However, the controller function for editor (if you define one) will run before the link function for scroll:

app.directive('scroll', ['$document','$parse', function ($document,$parse) {
return {
    restrict: 'E',
    replace: true,
    transclude: true,
    scope:false,
    template:
        '<div class="scroll">' +
            '<div class="content" data-ng-transclude>' +
            '</div>' +
        '</div>',
    link: function (scope, element, attr) {
        // some code here....
        console.log('link_editor=',scope.link_editor);  // value is "undefined"
        console.log('ctrl_editor=',scope.ctrl_editor);  // value is "true"
    }
};
}]);
app.directive('editor', ['$document', function ($document){
return {
    restrict: 'EA',
    replace: true,
    transclude: false,
    scope:true,
    template:
        '<div>' +
            'stuff <scroll>content to scroll</scroll> more stuff' +
        '</div>',
    link: function (scope, element, attrs) {
        scope.link_editor = true;
    },
    controller: function($scope) {
        $scope.ctrl_editor = true;
    }
};
}]);
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • Thanks! Looks like this might be a better solution than the workaround I did (which is, if a certain property of `` that I need is undefined at the time of ` -> link` I `$watch` for the property of `` I need and then run my code). I'll try it out first think tomorrow morning, it might simplify the code though the workaround will probably stay since I can't rely on whoever uses `` to implement a controller. – motig88 Aug 05 '13 at 19:26
  • @Mark Rajcok, forgive a semi-side q: why would editor.link() run after scroll.link(). AJS docs imply that parent nodes are $compiled and $linked before children, i thought, and here we have so would the order not be: 1) editor.compile(), 2) e.controller(), 3) e.link(), 4) scroll.compile(), 5) scroll.controller; 6) scroll.link()? Does transclusion or scope declarations affect order in this case? – Nikita Jan 28 '14 at 15:30
  • *update* on above: I was wrong - see [here](http://stackoverflow.com/a/21410877/182808). Not yet clear on how/whether transclude alters this. – Nikita Jan 28 '14 at 16:13