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>' + |
