1

In angularjs I have been trying to access main controller $scope variable in my directive isolated scope.

My html code,

  <body ng-controller="MainCtrl">
    <div id="TestContainer" class="TestContainer" ng-init=Intialfunc()>
             <collection collection='testdata'>{{testdata}}</collection>             
        </div>
  </body>

My directive code,

    var app = angular.module('plunker', []);

app.directive('collection', function () {       
    return {
        restrict: "E",
        replace: true,
        scope: {collection: '='},
        //controller: 'TreeController',
        //bindToController: true,       
        template: "<ul><member ng-repeat='member in collection' member='member'></member></ul>"         
    }
})

app.directive('member', function ($compile) {
    var linkerfunc = function(scope, element, attrs) {  
                    var collectionSt = '<collection collection="member.children"></collection>';
                    $compile(collectionSt)(scope, function(cloned, scope)   {                                           
                        element.append(cloned); 
                     });                    
    }
    return {
        restrict: "E",
        replace: true,
        scope: {member: '=', ShowDetailsCtrlFunc : '&'},
        template: "<li><span ng-click=ShowDetailsCtrlFunc()>{{member.NodeName}}</span></li>",       
        controller: 'MainCtrl',
        //controllerAs: 'MainCtrl',
        //bindToController: true,
        link: linkerfunc        
    }
})

My controller code,

app.controller('MainCtrl', function ($scope) {      

    $scope.Intialfunc = function() { 
        $scope.testdata = []
        var myjsondata = JSON.parse('{ "NodeName": "Parent", "children": [ { "NodeName": "mychild", "children": [ { "NodeName": "chld1", "children": [] } ] } ] }');
        $scope.testdata.push(myjsondata);
            console.log($scope.testdata) //This one is showing
        }       

    $scope.ShowDetailsCtrlFunc = function(element,event) {
            console.log("in function ShowDetailsCtrlFunc"); // coming to this fucntion on click.        
            console.log($scope.testdata) // but this one is not showing . shows undefined.
            //event.stopImmediatePropagation();         
      };
});

it is coming to the function but not showing the controller $scope. I have created a plunker ,

plunker

Please help me. I have been struggling for many days.

usersam
  • 1,125
  • 4
  • 27
  • 54
  • Please post relevant controller code and html. – rmlan Jan 24 '18 at 16:24
  • Hi @rmlan , i have added code and created plunker also. – usersam Jan 24 '18 at 16:30
  • Possible duplicate of [Call function on directive parent scope with directive scope argument](https://stackoverflow.com/questions/23477859/call-function-on-directive-parent-scope-with-directive-scope-argument) – rmlan Jan 24 '18 at 16:33
  • Take a look at the duplicate I have linked, but understand that because you have wrapped the `member` directive in the `collection` directive, you will need to utilize that solution in both. – rmlan Jan 24 '18 at 16:34
  • Thanks @rmlan. I followed the link and plunkr you mentioned. It worked when i created controller in direction and put $scope.ShowDetailsCtrlFunc fucntion in it. I am able to access $scope.testdata but can not access the html element now. – usersam Jan 26 '18 at 07:01
  • That is not what I was suggesting you do. I've added an answer that might get you going in the right direction. – rmlan Jan 26 '18 at 13:10

3 Answers3

2

You need to add a function expression to both of your directives' isolate scopes in order to properly call a function in your parent scope. Taking your original code, it should look something like this:

var app = angular.module('plunker', []);

app.directive('collection', function () {       
    return {
        restrict: "E",
        //replace: true, <- this is deprecated and should no longer be used
        scope: {
            collection: '=',
            onMemberClick: '&'
        },      
        template: "<ul><member ng-repeat='member in collection' member='member' on-click='onMemberClick()'></member></ul>"         
    }
})

app.directive('member', function ($compile) {
    return {
        restrict: "E",
        //replace: true, <- this is deprecated and should no longer be used
        scope: {
            member: '=', 
            onClick : '&'
        },
        template: "<li><span ng-click='onClick()'>{{member.NodeName}}</span></li>"       
    }
});

And you original html should look something like this:

<body ng-controller="MainCtrl">
  <div id="TestContainer" class="TestContainer" ng-init=Intialfunc()>
    <collection collection='testdata' on-member-click='ShowDetailsCtrlFunc ()'>{{testdata}}</collection>             
  </div>
</body>

Argument binding

If you would like to actually know which member was clicked, you'll need to bind arguments to your function calls.

var app = angular.module('plunker', []);

app.directive('collection', function () {       
    return {
        restrict: "E",
        scope: {
            collection: '=',
            onMemberClick: '&'
        },      
        template: "<ul><member ng-repeat='member in collection' member='member' on-click='onMemberClick({member: member})'></member></ul>"         
    }
})

app.directive('member', function ($compile) {
    return {
        restrict: "E",
        scope: {
            member: '=', 
            onClick : '&'
        },
        template: "<li><span ng-click='onClick({member: member})'>{{member.NodeName}}</span></li>"       
    }
});

Html:

<body ng-controller="MainCtrl">
  <div id="TestContainer" class="TestContainer" ng-init=Intialfunc()>
    <collection collection='testdata' on-member-click='ShowDetailsCtrlFunc (member)'>{{testdata}}</collection>             
  </div>
</body>

MainCtrl:

app.controller('MainCtrl', function ($scope) {      

    $scope.Intialfunc = function() { 
        $scope.testdata = []
        var myjsondata = JSON.parse('{ "NodeName": "Parent", "children": [ { "NodeName": "mychild", "children": [ { "NodeName": "chld1", "children": [] } ] } ] }');
        $scope.testdata.push(myjsondata);
            console.log($scope.testdata) //This one is showing
        }       

    $scope.ShowDetailsCtrlFunc = function(member) {
            console.log("In show details function");
            console.log(member);       
      };
});

plunker

rmlan
  • 4,387
  • 2
  • 21
  • 28
  • hi @rmlan. don't have words to say thanks to you. It is showing member on click as expected. But 1. it is not showing treeview, only showing parent. 2. what about the linker function.3. I wish we could access $scope.testdata along with member because i need to show member in forms in right panel which would be editable. I am good in javascript but new to angular. i am struggling and learning day by day. Sorry if missing something basic. – usersam Jan 26 '18 at 15:56
  • You're welcome. However, if you have a separate question, you need to post it as such. This answer addresses the question you asked here. – rmlan Jan 26 '18 at 16:04
  • i just wanted to get console.log($scope.testdata) in $scope.ShowDetailsCtrlFunc. What you suggested , can be done just by passing member in same function ShowDetailsCtrlFunc(member). No need to do so many changes. – usersam Jan 26 '18 at 20:56
  • I'm not sure what you mean by that, but ok? Perhaps you could post a plunker with what you ended up with? – rmlan Jan 29 '18 at 13:19
  • hi @rmlan, I have modified the plunkr you shared to (http://plnkr.co/edit/1W5foYuKfwJQxrmHpL6d?p=preview) to show the tree view but surprisingly ng-click working only for "Parent" node – usersam Feb 03 '18 at 18:04
  • So it looks like you implemented my answer. Why did you accept it and then unaccept it? The ng-click isn't working for any of the child nodes because you are compiling a template referencing ShowDetailsCtrlFunc with a scope in which it does not exist. You need to still use the `onClick({member: member})` in this template. See http://plnkr.co/edit/SWNjdDGP8rRRegOBurQx?p=preview – rmlan Feb 04 '18 at 16:09
  • This is the exactly what i want. Thanks a lot. – usersam Feb 07 '18 at 16:49
  • my apology. i missed that acceptance part. I was trying a different approach in parallel and it worked but with a small issue. for learning i raise another ques. https://stackoverflow.com/questions/48669479/angularjs-directive-controller-function-executes-many-time – usersam Feb 07 '18 at 17:14
0

Lets Begin with the query you have. You want to call a function from link inside the directive even when the scope is isolated. It's simple you want to access parent scope. Here's the code you can use to access parent scope.

    scope.$parent.yourFun();

    //or you can do this by the code give below.
    //Inside Directive Use this.
    scope:{
        fun:"&"
    },

    //now you can call this function inside link
    link:function(scope, element,attr){
        scope.fun();

    }
Ayush Agrawal
  • 369
  • 3
  • 9
-1

In your app.directive, just put scope : false.

Your directive will use the same scope as his parent scope.

  • The question specifically mentions that OP is using an isolated scope. – rmlan Jan 24 '18 at 16:46
  • yes but if he wants to access a data from many scopes, why making isolated scopes ? – Alexandre Picard Jan 24 '18 at 16:50
  • Just for the sake of argument, lets say that OP is just trying to learn how to use isolate scopes. That aside, this is answer is encouraging a bad practice, as it completely couples this directive to the parent scope. – rmlan Jan 24 '18 at 16:52
  • That's not a bad practice if you use it well and could be very useful. – Alexandre Picard Jan 24 '18 at 17:08
  • 1
    In this instance, it would be a bad practice because it makes assumptions about a function existing in the parent scope. You should never "assume" a variable or function exists in a parent scope when using `scope: false`. That is inarguably a bad practice. – rmlan Jan 24 '18 at 17:49