mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
fix(ng:repeat): use transclusion
This commit is contained in:
parent
7bd69d0f5b
commit
3df7b8e57f
3 changed files with 126 additions and 130 deletions
218
src/widgets.js
218
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];
|
||||
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
|
||||
// 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);
|
||||
}
|
||||
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);
|
||||
// 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;
|
||||
}
|
||||
if (index === last.index) {
|
||||
// do nothing
|
||||
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);
|
||||
});
|
||||
// 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();
|
||||
}
|
||||
|
||||
//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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastOrder = nextOrder;
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
}];
|
||||
lastOrder = nextOrder;
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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>');
|
||||
}));
|
||||
|
||||
|
|
|
|||
|
|
@ -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>' +
|
||||
|
|
|
|||
Loading…
Reference in a new issue