AngularJS : call a controller function from my directive

All we need is an easy explanation of the problem, so here it is.

I am trying to call a controller function from my directive, but it kept saying “my function is not defined”. I am not familiar with angularJs directive. I tried to use link:function in my directive, but I really cannot figure it out. Please help.. thank you ๐Ÿ™‚

html:

<body ng-app="myApp">
    <div ng-controller="myCtrl">
      <button class="btn" ng-click="changeDataObj()">Change Data Object</button>
      <div my-Tree list="list" >

      </div>            
    </div>
  </body>

js:

angular.module('myApp', [])
    .controller('myCtrl', myCtrl)
    .directive('myTree', ['$compile', myTree]);

function myCtrl($timeout, $scope) {        
    $scope.reset = true;
    $scope.list = [
        {
            name: 'Laptop',
            children: [
                {
                    name: 'Apple',
                    children: [
                        { name: 'Macbook Air' },
                        { name: 'Macbook Pro' }
                    ]
                },
                {
                    name: 'Microsoft',
                    children: [
                        { name: 'Surface Pro' }
                    ]
                }
            ]
        },
        {
            name: 'Desktop',
            children: [
                { name: 'Dell' }
            ]
        }
    ];

    $scope.selectANode = function (name) {
        alert(name);
    }       
}
function myTree($compile) {
    return {
        restrict: 'EA',
        scope: {
            list: '='
        },
        compile: function (tElem) {
            return {
                post: function (scope, iElem, iAttrs) {

                    var htmlString = [''];

                    addChildren(htmlString, scope.list, true);

                    function addChildren(htmlString, array, first) {    
                        if (!angular.isArray(array)) return;

                        if (first) { // root level parent must always be display:block
                            htmlString[0] += '<ul>';
                        } else {
                            htmlString[0] += '<ul style="display:none;">';
                        }

                        for (var i = 0; i < array.length; i++) {
                            htmlString[0] += '<li><span ng-click="selectANode(\'' + array[i].name + '\')">' + array[i].name + '</span><i class="navFinder__toggleBtn fa fa-folder-o"></i>';
                            addChildren(htmlString, array[i].children);
                            htmlString[0] += '</li>';
                        }
                        htmlString[0] += '</ul>';
                    }

                    var compiledContents = $compile(htmlString[0]);
                    compiledContents(scope, function (clone) {
                        iElem.append(clone);
                    })

                    iElem.find('i').on('click', function (event) {
                        event.target.className = (event.target.className == 'fa fa-folder-open-o navFinder__toggleBtn') ? 'fa fa-folder-o navFinder__toggleBtn' : 'fa fa-folder-open-o navFinder__toggleBtn'; // change icon
                        event.target.nextSibling.style.display = (event.target.nextSibling.style.display == 'none') ? 'block' : 'none'; // expand and collapse
                    });    
                }
            };
        }
    };
}

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

Your method on ng-click should stay in compile->post

 scope.selectANode = function (name) {
        alert(name);
 }

Plunker 1


But if you want to call the controller’s method, you need to add additional code to bind it:

 scope: {
            list: '=',
            selectNode: '&'
        },

// ...
scope.selectANode = function (name) {
    scope.selectNode({data: name});
}  

and your HTML:

<div my-Tree list="list" select-node="selectANode(data)">

Plunker 2

Enjoy ๐Ÿ™‚

Note: Use and implement method 1 because this method fully tested our system.
Thank you ๐Ÿ™‚

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply