aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMisko Hevery2012-02-09 16:35:23 -0800
committerMisko Hevery2012-02-21 22:46:01 -0800
commit3df7b8e57f125160738a811e97ca7819a78c48ff (patch)
tree554d50307333155e7b829bf22621ffdba1a3976e
parent7bd69d0f5b1dd9aa04ac19393991566785ec81c2 (diff)
downloadangular.js-3df7b8e57f125160738a811e97ca7819a78c48ff.tar.bz2
fix(ng:repeat): use transclusion
-rw-r--r--src/widgets.js222
-rw-r--r--test/BinderSpec.js36
-rw-r--r--test/widgetsSpec.js2
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>' +