6

I try implement angular ng-repeat directive and I don't understand why this code not work right.

.directive("myRepeat", function() {
    return {
        transclude: "element",
        priority: 1000,
        compile: function(tElem, tAttrs) {
            var myLoop = tAttrs.myRepeat,
                    match = myLoop.match(/^\s*(.+)+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),
                    indexString = match[1],
                    collectionString = match[2],
                    parent = tElem.parent();

            return function($scope, iElem, iAttrs, controller, $transclude) {


                    $scope.$watchCollection(collectionString, function(newCollection) {
                    var i, block, elements = [];

                    // check if elements have already been rendered
                    if (elements.length) {
                        // if so remove them from DOM, and destroy their scope
                        for (i = 0; i < elements.length; i++) {
                            elements[i].el.remove();
                            elements[i].scope.$destroy();
                        }
                        elements = [];
                    }

                    for (i = 0; i < newCollection.length; i++) {
                        $transclude(function(clone, scope) {
                            scope[indexString] = newCollection[i];
                            parent.append(clone);
                            block = {};
                            block.el = clone;
                            block.scope = scope;
                            elements.push(block);
                        });
                    }
                });
            }
        }
    }
})

and HTML fragment

<ul ng-controller="MyCtrl">
  <li my-repeat="city in cities">{{city.name}}</li>
</ul>

My problem is that LI elements rendered normal, but they are not contain city name. Please explain me why so occurs. I understand how work ng-transclude in primitive case, when we have template with element with ng-transclude and in our directive definition specify transclude: true, but I don't understand how that work with transclude: "element". P.S. Sorry for my english. I beginner :)

3
  • 1
    FYI, using $transclude from compile is deprecated. See docs.angularjs.org/api/ng/service/$compile Commented May 16, 2015 at 2:46
  • Found someone else's clone of ng-repeat here (it uses the non-deprecated form of the transclude fn). See this plunker comparing that implementation (which works) to yours (hopefully you can see the difference). Commented May 16, 2015 at 3:15
  • linker argument in compile function in your case is transclude function? In my case I return from compile function LINK function with transclude fn argument. This is not deprecated or I mistaken? Commented May 16, 2015 at 5:54

1 Answer 1

1

I notice that your indexString is not correct when I write it to console. change: match = myLoop.match(/^\s*(.+)+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/) to match = myLoop.split(' ')

Full code that works for me:

var app = angular.module('app', []);
app.controller("MyCtrl", function($scope){
  $scope.cities = [{
    name:'a'
  }, {name: 'b'},
  {name: 'c'}]
})

app.directive("myRepeat", function() {
    return {
        transclude: "element",
        priority: 1000,
        compile: function(tElem, tAttrs) {
            var myLoop = tAttrs.myRepeat,
                    match = myLoop.split(' '),
                    indexString = match[0],
                    collectionString = match[2],
                    parent = tElem.parent();
            console.log("match: " + match);
            console.log("index string: " + indexString);
            console.log("coll: " + collectionString);
            var elements = [];
            return function($scope, iElem, iAttrs, controller, $transclude) {


                    $scope.$watchCollection(collectionString, function(newCollection) {
                    var i;

                    // check if elements have already been rendered
                    if (elements.length) {
                        // if so remove them from DOM, and destroy their scope
                        for (i = 0; i < elements.length; i++) {
                            elements[i].el.remove();
                            elements[i].scope.$destroy();
                        }
                        elements = [];
                    }

                    for (i = 0; i < newCollection.length; i++) {
                        $transclude(function(clone, scope) {
                            scope[indexString] = newCollection[i];
                            parent.append(clone);
                            block = {};
                            block.el = clone;
                            block.scope = scope;
                            elements.push(block);
                        });
                    }
                });
            }
        }
    }
})
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.