Skip to content
Advertisement

ng-repeat with ng-transclude inside a directive

I want to create a list with custom behavior when it’s content changes. I try to create a directive for this but I get a bit lost with how to combine the ng-transclude with the ng-repeat directive. Can somebody put me on track?

Html:

<div ng-app="myApp">
  <div ng-controller="ctrl">
    <mylist items="myItem in items">
       <span class="etc">{{myItem}}</span>
    </mylist>
  </div>
</div>

Javascript:

angular.module('myApp', [])    

.controller('ctrl', function ($scope) {
  $scope.items = ['one', 'two', 'three'];
})    

.directive('mylist', function () {
  return {
    restrict:'E',
    transclude: 'element',
    replace: true,
    scope: true,
    template: [
      '<ul>',
        '<li ng-repeat="WhatGoesHere in items" ng-transclude></li>',
      '</ul>'
    ].join(''),
    link: function (scope, element, attr) {
      var parts = attr.items.split(' in ');
      var itemPart = parts[0];
      var itemsPart = parts[1];
      scope.$watch(itemsPart, function (value) {
        scope.items = value; 
      });      
    }
  }
});

I’ve got part of this somewhat working here

EDIT:

Criteria:

  • The template of the item must be defined in the view, not in the directive and it must have access to an item property in a child scope. Ideally I want to define this like it is done in the ng-repeat directive
  • The directive must have access to the list so I can set proper watches and change things. If possible I would like to have easy access to the generated DOM items (I can also do it with element[0].querySelectorAll('ul>li') or something, It only has to work on Chrome).
  • If possible I would like to reuse the logic in the ng-repeat directive because it does already do a lot of what I want. Preferably I don’t want to copy the code. I just want to augment its behavior, not change it

Advertisement

Answer

Solved the problem myself:

I am able to do it in the compile step (jsfiddle) by adding the ng-repeat attribute when the template is compiled and feeding it the content of my attribute.

Html:

<div ng-app="myApp">
  <div ng-controller="ctrl">
    <mylist element="myItem in items">{{myItem}}</mylist>
  </div>
</div>

Javascript:

var myApp = angular.module('myApp', [])

.controller('ctrl', function ($scope) {
  $scope.items = ['one', 'two', 'three'];
})

.directive('mylist', function ($parse) {
  return {
    restrict:'E',
    transclude: 'element',
    replace: true,
    scope: true,
    template: [
      '<ul>',
      '<li ng-transclude></li>',
      '</ul>'
    ].join(''),
    compile: function (tElement, tAttrs, transclude) {
      var rpt = document.createAttribute('ng-repeat');
      rpt.nodeValue = tAttrs.element;
      tElement[0].children[0].attributes.setNamedItem(rpt);
      return function (scope, element, attr) {
        var rhs = attr.element.split(' in ')[1];
        scope.items = $parse(rhs)(scope);
        console.log(scope.items);
      }        
    }
  }
});
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement