Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access directive's isolate scope from within transcluded content

I'm not sure if this is actually possible, but I'm essentially wanting a reverse of the '&' isolate scope in AngularJS. Here is a Plunkr to demonstrate.

Basically, I have a custom directive set up that delivers some reusable HTML. It makes use of ng-transclude to allow some external content to be rendered within it. However, I have found a situation where I would like to access a function that has been set up on the isolate scope for the directive, from within the transcluded section of code.

So essentially I have something that looks like:

<div ng-controller="MainController">

    <!-- The directive -->
    <div some-custom-directive>

        <!-- Button 1 that invokes a method within the controller scope -->
        <button id="button1" ng-click="invoke1()">Invoke from Controller</button>

        <!-- Button 2 that invokes a method on the isolate scope for the custom directive -->
        <button id="button2" ng-click="invoke2()">Invoke from Isolate Scope</button>
    </div>
</div>

Does anyone know if this is indeed possible?

Update

As per @Mark Rajcok's answer, the $$prevSibling found on the $scope can be used to access the isolate scope of the directive from within the transcluded content. However, I have updated the above Plunkr to attempt this from within an ng-repeat directive, which does not work. I am assuming the items within that repeat do not inherit the scope.

like image 935
Samuel Slade Avatar asked Apr 23 '13 14:04

Samuel Slade


3 Answers

Although possible, the solution I present is a hack, as it uses a scope internal variable, $$prevSibling.

Inside your transcluded content, you are inside the transcluded scope. The directive's isolate and transcluded scopes are siblings. To get from the transcluded scope to the isolate scope, you can use $$prevSibling. (To get from the isolate scope to the transcluded scope, you can use $$nextSibling.)

So, this hack will work:

<a href="" ng-click="$$prevSibling.action()">Invoke the Directive Action</a>

To call a method on the controller scope, you need to specify that using & as @ganaraj already demonstrated:

<content-presenter controller-action="action()">

Then in your directive:

scope: { controllerAction: '&' },
template: ... +
   '<button ng-click="controllerAction()">Trigger Ctrl action()</button>' ...

Plunker


With ng-repeat (see Samuel's comment), each item creates a child scope of the transcluded scope. In the picture below, I only show one item to keep the picture smaller. Reverse the $$nextSibling brown arrow for $$prevSibling.

enter image description here

So the hack to get to method action() defined on the isolate scope from an ng-repeat child scope is

<div ng-repeat="item in items" ng-click="$parent.$$prevSibling.action()">

Update for Angular v1.3+:

Since Angular v1.3, the transcluded scope is now a child of the directive's isolate scope – they are no longer siblings. So to get from the transcluded scope to the isolate scope, use $parent.

With ng-repeat, each item still creates a child scope of the transcluded scope:

enter image description here

But to get to method action() defined on the isolate scope from an ng-repeat child scope, we now use

<div ng-repeat="item in items" ng-click="$parent.$parent.action()">
like image 123
Mark Rajcok Avatar answered Oct 20 '22 10:10

Mark Rajcok


Here is a plunk that solves your current problem. I am not sure what you are attempting to do. But as far as I know, there is no way of calling something in the isolate scope from an external scope. Ofcourse, you could setup a two way bound variable between the isolate scope and the external scope, change the variable in the external scope and $watch for it on the isolate scope ( this will work like an eventing mechanism )... That is one way of doing what you are attempting to do.. if you insist on it.

Alternatively, there is a mechanism to call a function on the external scope from the isolate scope. Its kind of like a callback.

See this http://plnkr.co/edit/5MT4vo9qXtV6nQikfEiH?p=preview

like image 1
ganaraj Avatar answered Oct 20 '22 10:10

ganaraj


It is my understanding that adding ng-repeat to an element creates a new scope, in order for the repeating content to be correctly bound. You may need an additional $parent in that chain, such as $parent.$$nextSibling, in order to step up to the level that is adjacent to the directive's isolate scope.

like image 1
K. Adam Avatar answered Oct 20 '22 10:10

K. Adam