Chrillewoodz
Chrillewoodz

Reputation: 28328

How to give an ng-repeat an id?

I have a tree structure which requires a different set of styling for each level in three tree. Therefore I need some way of keeping track of which level an item is in, so I thought that if there is some way to set a unique ID for an ng-repeat (NOT the items inside) dynamically, I could easily perform some maths to give the correct stylings inline.

Now I got a recursive tree structure using three files; a wrapper, a new level and a branch (which is the same as a new level). treeWrapper and treeLevel is directive which uses these templates.

I've performed an ng-include inside the tree-branch.html file in order to make it recursive. The first ng-include inside tree-level.html is just to "kickstart" the recursiveness.

So my question is simply how to set an id for each ng-repeat in the tree? Is this even possible?

tree-wrapper.html:

<div class="tree-wrapper">
    <tree-level ctrl="ctrl" tree="tree"></tree-level>
</div>

tree-level.html:

<ul class="tree-list">
    <li class="tree-item" ng-repeat="branch in tree track by $index"
        ng-include="'/app/views/components/tree/tree-branch.html'"></li>
</ul>

tree-branch.html:

<div ng-click="ctrl.toggleBranch(branch)" ng-bind="branch.name"></div>
<ul class="tree-list" ng-if="branch.branches">
    <li class="tree-item" style="padding-left: {{$index * 5}}" ng-repeat="branch in branch.branches" 
        ng-include="'/app/views/components/tree/tree-branch.html'"></li>
</ul>

Upvotes: 1

Views: 972

Answers (1)

Cyril Gandon
Cyril Gandon

Reputation: 17048

This can be easily done by using directives instead of inclusion.

A tree-branch template will looks like this:

<ul class="branch-level-{{vm.level}}">
   <li ng-repeat="branch in vm.branch.branches">
       <tree-branch level="vm.level + 1" branch="branch"></tree-branch>
   </li>
</ul>

And the directive definition looks like

{
    scope : {
        level : '=',
        branch : '='
    }
}

The kickstart in tree-level.html can be simplify:

<tree-branch level="0" branch="tree"></tree-branch>

Note that recursion with directives will blow up the compiler, you have to handle it yourself. See this post to know how to do it: Recursion in Angular directives

/* 
 * An Angular service which helps with creating recursive directives.
 * @author Mark Lagendijk
 * @license MIT
 */
angular.module('RecursionHelper', []).factory('RecursionHelper', ['$compile',
  function($compile) {
    return {
      /**
       * Manually compiles the element, fixing the recursion loop.
       * @param element
       * @param [link] A post-link function, or an object with function(s) registered via pre and post properties.
       * @returns An object containing the linking functions.
       */
      compile: function(element, link) {
        // Normalize the link parameter
        if (angular.isFunction(link)) {
          link = {
            post: link
          };
        }

        // Break the recursion loop by removing the contents
        var contents = element.contents().remove();
        var compiledContents;
        return {
          pre: (link && link.pre) ? link.pre : null,
          /**
           * Compiles and re-adds the contents
           */
          post: function(scope, element) {
            // Compile the contents
            if (!compiledContents) {
              compiledContents = $compile(contents);
            }
            // Re-add the compiled contents to the element
            compiledContents(scope, function(clone) {
              element.append(clone);
            });

            // Call the post-linking function, if any
            if (link && link.post) {
              link.post.apply(null, arguments);
            }
          }
        };
      }
    };
  }
]);

angular.module('recursionDemo', ['RecursionHelper'])
  .controller("TreeController", function($scope) {
    $scope.treeFamily = {
      name: "Parent",
      children: [{
        name: "Child1",
        children: [{
          name: "Grandchild1",
          children: []
        }, {
          name: "Grandchild2",
          children: [{
            name: "GrandGrandchild1",
            children: []
          }]
        }, {
          name: "Grandchild3",
          children: []
        }]
      }, {
        name: "Child2",
        children: []
      }]
    };
  })
  .directive("tree", function(RecursionHelper) {
    return {
      restrict: "E",
      scope: {},
      bindToController: {
        family: '=',
        level: '='
      },
      controller: function() {},
      controllerAs: 'vm',
      template: '{{vm.family.name}} Level = {{vm.level}} ' +
        '<ul>' +
        '    <li ng-repeat="child in vm.family.children">' +
        '        <tree family="child" level="vm.level+1"></tree>' +
        '    </li>' +
        '</ul>',
      compile: function(element) {
        return RecursionHelper.compile(element, function(scope, iElement, iAttrs, controller, transcludeFn) {
          // Define your normal link function here.
          // Alternative: instead of passing a function,
          // you can also pass an object with 
          // a 'pre'- and 'post'-link function.
        });
      }
    };
  });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div ng-app="recursionDemo" ng-controller="TreeController">
  <tree family="treeFamily" level="0"></tree>
</div>

Upvotes: 2

Related Questions