Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive custom directives using ngRepeat

I'm trying to create a treeview using AngularJS.

Here's my code:

module.directive('treeview', function () {
    return {
        restrict: 'E',
        templateUrl: "/templates/ui/controls/treeview.htm",
        replace: true,
        transclude: true,
        scope: {},
        link: function (scope, element, attrs) {
            console.log("treeview directive loaded");
        },
        controller: function ($scope, $rootScope) {
            $rootScope.depth = 0;
            $scope.items = [
                { text: "face" },
                { text: "palm" },
                {
                    text: "cake",
                    childitems: [
                        { text: "1 face" },
                        { text: "1 palm" },
                        { text: "1 cake" }
                    ]
                }
            ];
        }
    };
});

module.directive('treeviewItem', function () {
    return {
        restrict: 'E',
        templateUrl: "/templates/ui/controls/treeview-item.htm",
        replace: true,
        scope: {
            item: "="
        },
        link: function (scope, element, attrs) {
            console.log("treeview item directive loaded");
        }
    };
});

Treeview template:

<div class="sl-treeview">
    <ul class="clear" ng-transclude>
        <treeview-item ng-repeat="item in items" item="item"></treeview-item>
    </ul>
</div>

Treeview Item template:

<li>
    <i class="icon-plus-sign"></i>
    <a href="/">
        <i class="icon-folder-close"></i>
        {{item.text}}
    </a>
    <!-- This ul is the issue - it crashes the page -->
    <ul>
        <treeview-item ng-repeat="childitem in item.childitems" item="childitem"></treeview-item>
    </ul>
</li>

In the treeview directive $scope.items is hardcoded for development - eventually I hope this will come from a controller/service pulling data from the server. However it represents the sort of basic structure I'm looking for.

When I run this without the nested ul in treeviewItem it gives me the first three items just fine. When i add the ul in to try and get the controls to bind with child items it hands the page and stops working.

JSFiddle without nested ul - working:

http://jsfiddle.net/BdmV3/

JSFiddle with nested ul - not working (and may hang your browser!):

http://jsfiddle.net/SKPpv/

How should I go about making a control that uses a custom directive and ngRepeat to create potentially infinite levels of recursion? Why isn't my approach working?

like image 796
Jon Avatar asked Aug 11 '13 16:08

Jon


1 Answers

The problem is that you're trying to define your directive recursively, when angular was trying to compile the template, it saw treeview directive, it called treeview's compile function, then it saw treeviewItem directive, it called treeviewItem's compile function, then it saw treeviewItem directive, it called treeviewItem's compile function,then it saw treeviewItem directive, it called treeviewItem's compile function...

See the problem? The calls to compile functions couldn't stop. So, you need to pull the recursive definition out of your template, but use $compile to build DOM manually:

module.directive('treeviewItem', function ($compile) {
    return {
        restrict: 'E',
        template: '<li><i class="icon-plus-sign"></i><a href="/"><i class="icon-folder-close"></i>{{item.text}}</a></li>',
        replace: true,
        scope: {
            item: "="
        },
        link: function (scope, element, attrs) {
            element.append($compile('<ul><treeview-item ng-repeat="childitem in item.childitems" item="childitem"></treeview-item></ul>')(scope));

            console.log("treeview item directive loaded");
        }
    };
});

http://jsfiddle.net/SKPpv/3/

Alternatively, I found a solution to display tree-like data on SO https://stackoverflow.com/a/11861030/69172. The solution however uses ngInclude instead of directives.

like image 139
Ye Liu Avatar answered Oct 31 '22 18:10

Ye Liu