diff options
| author | Misko Hevery | 2012-02-09 16:35:23 -0800 | 
|---|---|---|
| committer | Misko Hevery | 2012-02-21 22:46:01 -0800 | 
| commit | 3df7b8e57f125160738a811e97ca7819a78c48ff (patch) | |
| tree | 554d50307333155e7b829bf22621ffdba1a3976e | |
| parent | 7bd69d0f5b1dd9aa04ac19393991566785ec81c2 (diff) | |
| download | angular.js-3df7b8e57f125160738a811e97ca7819a78c48ff.tar.bz2 | |
fix(ng:repeat): use transclusion
| -rw-r--r-- | src/widgets.js | 222 | ||||
| -rw-r--r-- | test/BinderSpec.js | 36 | ||||
| -rw-r--r-- | test/widgetsSpec.js | 2 | 
3 files changed, 128 insertions, 132 deletions
| diff --git a/src/widgets.js b/src/widgets.js index 760a7256..cd282746 100644 --- a/src/widgets.js +++ b/src/widgets.js @@ -310,131 +310,127 @@ var htmlAnchorDirective = valueFn({        </doc:scenario>      </doc:example>   */ -var ngRepeatDirective = ['$compile', function($compile) { -  return { -    priority: 1000, -    terminal: true, -    compile: function(element, attr) { +var ngRepeatDirective = valueFn({ +  transclude: 'element', +  priority: 1000, +  terminal: true, +  compile: function(element, attr, linker) { +    return function(scope, iterStartElement, attr){        var expression = attr.ngRepeat; -      attr.$set(attr.$attr.ngRepeat); -      element.replaceWith(jqLite('<!-- ng:repeat: ' + expression + ' -->')); -      var linker = $compile(element); -      return function(scope, iterStartElement, attr){ -        var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/), -          lhs, rhs, valueIdent, keyIdent; -        if (! match) { -          throw Error("Expected ng:repeat in form of '_item_ in _collection_' but got '" + -            expression + "'."); -        } -        lhs = match[1]; -        rhs = match[2]; -        match = lhs.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/); -        if (!match) { -          throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '" + -            keyValue + "'."); -        } -        valueIdent = match[3] || match[1]; -        keyIdent = match[2]; - -        // Store a list of elements from previous run. This is a hash where key is the item from the -        // iterator, and the value is an array of objects with following properties. -        //   - scope: bound scope -        //   - element: previous element. -        //   - index: position -        // We need an array of these objects since the same object can be returned from the iterator. -        // We expect this to be a rare case. -        var lastOrder = new HashQueueMap(); -        scope.$watch(function(scope){ -          var index, length, -              collection = scope.$eval(rhs), -              collectionLength = size(collection, true), -              childScope, -              // Same as lastOrder but it has the current state. It will become the -              // lastOrder on the next iteration. -              nextOrder = new HashQueueMap(), -              key, value, // key/value of iteration -              array, last,       // last object information {scope, element, index} -              cursor = iterStartElement;     // current position of the node - -          if (!isArray(collection)) { -            // if object, extract keys, sort them and use to determine order of iteration over obj props -            array = []; -            for(key in collection) { -              if (collection.hasOwnProperty(key) && key.charAt(0) != '$') { -                array.push(key); -              } +      var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/), +        lhs, rhs, valueIdent, keyIdent; +      if (! match) { +        throw Error("Expected ng:repeat in form of '_item_ in _collection_' but got '" + +          expression + "'."); +      } +      lhs = match[1]; +      rhs = match[2]; +      match = lhs.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/); +      if (!match) { +        throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '" + +          keyValue + "'."); +      } +      valueIdent = match[3] || match[1]; +      keyIdent = match[2]; + +      // Store a list of elements from previous run. This is a hash where key is the item from the +      // iterator, and the value is an array of objects with following properties. +      //   - scope: bound scope +      //   - element: previous element. +      //   - index: position +      // We need an array of these objects since the same object can be returned from the iterator. +      // We expect this to be a rare case. +      var lastOrder = new HashQueueMap(); +      scope.$watch(function(scope){ +        var index, length, +            collection = scope.$eval(rhs), +            collectionLength = size(collection, true), +            childScope, +            // Same as lastOrder but it has the current state. It will become the +            // lastOrder on the next iteration. +            nextOrder = new HashQueueMap(), +            key, value, // key/value of iteration +            array, last,       // last object information {scope, element, index} +            cursor = iterStartElement;     // current position of the node + +        if (!isArray(collection)) { +          // if object, extract keys, sort them and use to determine order of iteration over obj props +          array = []; +          for(key in collection) { +            if (collection.hasOwnProperty(key) && key.charAt(0) != '$') { +              array.push(key);              } -            array.sort(); -          } else { -            array = collection || [];            } +          array.sort(); +        } else { +          array = collection || []; +        } -          // we are not using forEach for perf reasons (trying to avoid #call) -          for (index = 0, length = array.length; index < length; index++) { -            key = (collection === array) ? index : array[index]; -            value = collection[key]; -            last = lastOrder.shift(value); -            if (last) { -              // if we have already seen this object, then we need to reuse the -              // associated scope/element -              childScope = last.scope; -              nextOrder.push(value, last); - -              if (index === last.index) { -                // do nothing -                cursor = last.element; -              } else { -                // existing item which got moved -                last.index = index; -                // This may be a noop, if the element is next, but I don't know of a good way to -                // figure this out,  since it would require extra DOM access, so let's just hope that -                // the browsers realizes that it is noop, and treats it as such. -                cursor.after(last.element); -                cursor = last.element; -              } +        // we are not using forEach for perf reasons (trying to avoid #call) +        for (index = 0, length = array.length; index < length; index++) { +          key = (collection === array) ? index : array[index]; +          value = collection[key]; +          last = lastOrder.shift(value); +          if (last) { +            // if we have already seen this object, then we need to reuse the +            // associated scope/element +            childScope = last.scope; +            nextOrder.push(value, last); + +            if (index === last.index) { +              // do nothing +              cursor = last.element;              } else { -              // new item which we don't know about -              childScope = scope.$new(); +              // existing item which got moved +              last.index = index; +              // This may be a noop, if the element is next, but I don't know of a good way to +              // figure this out,  since it would require extra DOM access, so let's just hope that +              // the browsers realizes that it is noop, and treats it as such. +              cursor.after(last.element); +              cursor = last.element;              } +          } else { +            // new item which we don't know about +            childScope = scope.$new(); +          } -            childScope[valueIdent] = value; -            if (keyIdent) childScope[keyIdent] = key; -            childScope.$index = index; -            childScope.$position = index === 0 ? -                'first' : -                (index == collectionLength - 1 ? 'last' : 'middle'); - -            if (!last) { -              linker(childScope, function(clone){ -                cursor.after(clone); -                last = { -                    scope: childScope, -                    element: (cursor = clone), -                    index: index -                  }; -                nextOrder.push(value, last); -              }); -            } +          childScope[valueIdent] = value; +          if (keyIdent) childScope[keyIdent] = key; +          childScope.$index = index; +          childScope.$position = index === 0 ? +              'first' : +              (index == collectionLength - 1 ? 'last' : 'middle'); + +          if (!last) { +            linker(childScope, function(clone){ +              cursor.after(clone); +              last = { +                  scope: childScope, +                  element: (cursor = clone), +                  index: index +                }; +              nextOrder.push(value, last); +            });            } +        } -          //shrink children -          for (key in lastOrder) { -            if (lastOrder.hasOwnProperty(key)) { -              array = lastOrder[key]; -              while(array.length) { -                value = array.pop(); -                value.element.remove(); -                value.scope.$destroy(); -              } +        //shrink children +        for (key in lastOrder) { +          if (lastOrder.hasOwnProperty(key)) { +            array = lastOrder[key]; +            while(array.length) { +              value = array.pop(); +              value.element.remove(); +              value.scope.$destroy();              }            } +        } -          lastOrder = nextOrder; -        }); -      }; -    } -  }; -}]; +        lastOrder = nextOrder; +      }); +    }; +  } +});  /** diff --git a/test/BinderSpec.js b/test/BinderSpec.js index f7a1c1b3..5e27fd0f 100644 --- a/test/BinderSpec.js +++ b/test/BinderSpec.js @@ -109,8 +109,8 @@ describe('Binder', function() {      expect(sortedHtml(form)).toBe(          '<ul>' +            '<#comment></#comment>' + -          '<li ng:bind="item.a">A</li>' + -          '<li ng:bind="item.a">B</li>' + +          '<li ng:bind="item.a" ng:repeat="item in model.items">A</li>' + +          '<li ng:bind="item.a" ng:repeat="item in model.items">B</li>' +          '</ul>');      items.unshift({a: 'C'}); @@ -118,9 +118,9 @@ describe('Binder', function() {      expect(sortedHtml(form)).toBe(          '<ul>' +            '<#comment></#comment>' + -          '<li ng:bind="item.a">C</li>' + -          '<li ng:bind="item.a">A</li>' + -          '<li ng:bind="item.a">B</li>' + +          '<li ng:bind="item.a" ng:repeat="item in model.items">C</li>' + +          '<li ng:bind="item.a" ng:repeat="item in model.items">A</li>' + +          '<li ng:bind="item.a" ng:repeat="item in model.items">B</li>' +          '</ul>');      items.shift(); @@ -128,8 +128,8 @@ describe('Binder', function() {      expect(sortedHtml(form)).toBe(          '<ul>' +            '<#comment></#comment>' + -          '<li ng:bind="item.a">A</li>' + -          '<li ng:bind="item.a">B</li>' + +          '<li ng:bind="item.a" ng:repeat="item in model.items">A</li>' + +          '<li ng:bind="item.a" ng:repeat="item in model.items">B</li>' +          '</ul>');      items.shift(); @@ -147,7 +147,7 @@ describe('Binder', function() {      expect(sortedHtml(element)).toBe(          '<ul>' +            '<#comment></#comment>' + -          '<li><span ng:bind="item.a">A</span></li>' + +          '<li ng:repeat="item in model.items"><span ng:bind="item.a">A</span></li>' +          '</ul>');    })); @@ -249,15 +249,15 @@ describe('Binder', function() {      expect(sortedHtml(element)).toBe(          '<div>'+            '<#comment></#comment>'+ -          '<div name="a">'+ +          '<div name="a" ng:repeat="m in model">'+              '<#comment></#comment>'+ -            '<ul name="a1"></ul>'+ -            '<ul name="a2"></ul>'+ +            '<ul name="a1" ng:repeat="i in m.item"></ul>'+ +            '<ul name="a2" ng:repeat="i in m.item"></ul>'+            '</div>'+ -          '<div name="b">'+ +          '<div name="b" ng:repeat="m in model">'+              '<#comment></#comment>'+ -            '<ul name="b1"></ul>'+ -            '<ul name="b2"></ul>'+ +            '<ul name="b1" ng:repeat="i in m.item"></ul>'+ +            '<ul name="b2" ng:repeat="i in m.item"></ul>'+            '</div>' +          '</div>');    })); @@ -341,8 +341,8 @@ describe('Binder', function() {      expect(d2.hasClass('e')).toBeTruthy();      expect(sortedHtml(element)).toBe(          '<div><#comment></#comment>' + -        '<div class="o" ng:class-even="\'e\'" ng:class-odd="\'o\'"></div>' + -        '<div class="e" ng:class-even="\'e\'" ng:class-odd="\'o\'"></div></div>'); +        '<div class="o" ng:class-even="\'e\'" ng:class-odd="\'o\'" ng:repeat="i in [0,1]"></div>' + +        '<div class="e" ng:class-even="\'e\'" ng:class-odd="\'o\'" ng:repeat="i in [0,1]"></div></div>');    }));    it('BindStyle', inject(function($rootScope, $compile) { @@ -469,8 +469,8 @@ describe('Binder', function() {      expect(sortedHtml(element)).toBe(          '<ul>' +            '<#comment></#comment>' + -          '<li ng:bind=\"k + v\">a0</li>' + -          '<li ng:bind=\"k + v\">b1</li>' + +          '<li ng:bind=\"k + v\" ng:repeat="(k,v) in {a:0,b:1}">a0</li>' + +          '<li ng:bind=\"k + v\" ng:repeat="(k,v) in {a:0,b:1}">b1</li>' +          '</ul>');    })); diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js index 65ab59e5..fcd6fc04 100644 --- a/test/widgetsSpec.js +++ b/test/widgetsSpec.js @@ -332,7 +332,7 @@ describe('widget', function() {    }); -  describe('@ng:repeat', function() { +  describe('ng:repeat', function() {      it('should ng:repeat over array', inject(function($rootScope, $compile) {        element = $compile(          '<ul>' + | 
