Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit f2b7fff

Browse files
zhenbzhamhevery
authored andcommitted
fix(ngRepeat): now works with primitive types
closes #933
1 parent 42c38b2 commit f2b7fff

File tree

4 files changed

+156
-1
lines changed

4 files changed

+156
-1
lines changed

src/apis.js

+10
Original file line numberDiff line numberDiff line change
@@ -97,5 +97,15 @@ HashQueueMap.prototype = {
9797
return array.shift();
9898
}
9999
}
100+
},
101+
102+
/**
103+
* return the first item without deleting it
104+
*/
105+
peek: function(key) {
106+
var array = this[key = hashKey(key)];
107+
if (array) {
108+
return array[0];
109+
}
100110
}
101111
};

src/ng/directive/ngRepeat.js

+27-1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ var ngRepeatDirective = ngDirective({
8888
// We need an array of these objects since the same object can be returned from the iterator.
8989
// We expect this to be a rare case.
9090
var lastOrder = new HashQueueMap();
91+
var indexValues = [];
9192
scope.$watch(function(scope){
9293
var index, length,
9394
collection = scope.$eval(rhs),
@@ -117,7 +118,20 @@ var ngRepeatDirective = ngDirective({
117118
for (index = 0, length = array.length; index < length; index++) {
118119
key = (collection === array) ? index : array[index];
119120
value = collection[key];
120-
last = lastOrder.shift(value);
121+
122+
// if collection is array and value is object, it can be shifted to allow for position change
123+
// if collection is array and value is not object, need to first check whether index is same to
124+
// avoid shifting wrong value
125+
// if collection is not array, need to always check index to avoid shifting wrong value
126+
if (lastOrder.peek(value)) {
127+
last = collection === array ?
128+
((isObject(value)) ? lastOrder.shift(value) :
129+
(index === lastOrder.peek(value).index ? lastOrder.shift(value) : undefined)) :
130+
(index === lastOrder.peek(value).index ? lastOrder.shift(value) : undefined);
131+
} else {
132+
last = undefined;
133+
}
134+
121135
if (last) {
122136
// if we have already seen this object, then we need to reuse the
123137
// associated scope/element
@@ -137,6 +151,12 @@ var ngRepeatDirective = ngDirective({
137151
cursor = last.element;
138152
}
139153
} else {
154+
if (indexValues.hasOwnProperty(index) && collection !== array) {
155+
var preValue = indexValues[index];
156+
var v = lastOrder.shift(preValue);
157+
v.element.remove();
158+
v.scope.$destroy();
159+
}
140160
// new item which we don't know about
141161
childScope = scope.$new();
142162
}
@@ -158,10 +178,16 @@ var ngRepeatDirective = ngDirective({
158178
index: index
159179
};
160180
nextOrder.push(value, last);
181+
indexValues[index] = value;
161182
});
162183
}
163184
}
164185

186+
var i, l;
187+
for (i = 0, l = indexValues.length - length; i < l; i++) {
188+
indexValues.pop();
189+
}
190+
165191
//shrink children
166192
for (key in lastOrder) {
167193
if (lastOrder.hasOwnProperty(key)) {

test/ApiSpecs.js

+4
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ describe('api', function() {
3131
map.push('key', 'a');
3232
map.push('key', 'b');
3333
expect(map[hashKey('key')]).toEqual(['a', 'b']);
34+
expect(map.peek('key')).toEqual('a');
35+
expect(map[hashKey('key')]).toEqual(['a', 'b']);
3436
expect(map.shift('key')).toEqual('a');
37+
expect(map.peek('key')).toEqual('b');
38+
expect(map[hashKey('key')]).toEqual(['b']);
3539
expect(map.shift('key')).toEqual('b');
3640
expect(map.shift('key')).toEqual(undefined);
3741
expect(map[hashKey('key')]).toEqual(undefined);

test/ng/directive/ngRepeatSpec.js

+115
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,89 @@ describe('ngRepeat', function() {
3737
}));
3838

3939

40+
it('should ngRepeat over array of primitive correctly', inject(function($rootScope, $compile) {
41+
element = $compile(
42+
'<ul>' +
43+
'<li ng-repeat="item in items" ng-init="suffix = \';\'" ng-bind="item + suffix"></li>' +
44+
'</ul>')($rootScope);
45+
46+
Array.prototype.extraProperty = "should be ignored";
47+
// INIT
48+
$rootScope.items = [true, true, true];
49+
$rootScope.$digest();
50+
expect(element.find('li').length).toEqual(3);
51+
expect(element.text()).toEqual('true;true;true;');
52+
delete Array.prototype.extraProperty;
53+
54+
$rootScope.items = [false, true, true];
55+
$rootScope.$digest();
56+
expect(element.find('li').length).toEqual(3);
57+
expect(element.text()).toEqual('false;true;true;');
58+
59+
$rootScope.items = [false, true, false];
60+
$rootScope.$digest();
61+
expect(element.find('li').length).toEqual(3);
62+
expect(element.text()).toEqual('false;true;false;');
63+
64+
$rootScope.items = [true];
65+
$rootScope.$digest();
66+
expect(element.find('li').length).toEqual(1);
67+
expect(element.text()).toEqual('true;');
68+
69+
$rootScope.items = [true, true, false];
70+
$rootScope.$digest();
71+
expect(element.find('li').length).toEqual(3);
72+
expect(element.text()).toEqual('true;true;false;');
73+
74+
$rootScope.items = [true, false, false];
75+
$rootScope.$digest();
76+
expect(element.find('li').length).toEqual(3);
77+
expect(element.text()).toEqual('true;false;false;');
78+
79+
// string
80+
$rootScope.items = ['a', 'a', 'a'];
81+
$rootScope.$digest();
82+
expect(element.find('li').length).toEqual(3);
83+
expect(element.text()).toEqual('a;a;a;');
84+
85+
$rootScope.items = ['ab', 'a', 'a'];
86+
$rootScope.$digest();
87+
expect(element.find('li').length).toEqual(3);
88+
expect(element.text()).toEqual('ab;a;a;');
89+
90+
$rootScope.items = ['test'];
91+
$rootScope.$digest();
92+
expect(element.find('li').length).toEqual(1);
93+
expect(element.text()).toEqual('test;');
94+
95+
$rootScope.items = ['same', 'value'];
96+
$rootScope.$digest();
97+
expect(element.find('li').length).toEqual(2);
98+
expect(element.text()).toEqual('same;value;');
99+
100+
// number
101+
$rootScope.items = [12, 12, 12];
102+
$rootScope.$digest();
103+
expect(element.find('li').length).toEqual(3);
104+
expect(element.text()).toEqual('12;12;12;');
105+
106+
$rootScope.items = [53, 12, 27];
107+
$rootScope.$digest();
108+
expect(element.find('li').length).toEqual(3);
109+
expect(element.text()).toEqual('53;12;27;');
110+
111+
$rootScope.items = [89];
112+
$rootScope.$digest();
113+
expect(element.find('li').length).toEqual(1);
114+
expect(element.text()).toEqual('89;');
115+
116+
$rootScope.items = [89, 23];
117+
$rootScope.$digest();
118+
expect(element.find('li').length).toEqual(2);
119+
expect(element.text()).toEqual('89;23;');
120+
}));
121+
122+
40123
it('should ngRepeat over object', inject(function($rootScope, $compile) {
41124
element = $compile(
42125
'<ul>' +
@@ -47,6 +130,38 @@ describe('ngRepeat', function() {
47130
expect(element.text()).toEqual('misko:swe;shyam:set;');
48131
}));
49132

133+
134+
it('should ngRepeat over object with primitive value correctly', inject(function($rootScope, $compile) {
135+
element = $compile(
136+
'<ul>' +
137+
'<li ng-repeat="(key, value) in items" ng-bind="key + \':\' + value + \';\' "></li>' +
138+
'</ul>')($rootScope);
139+
$rootScope.items = {misko:'true', shyam:'true', zhenbo: 'true'};
140+
$rootScope.$digest();
141+
expect(element.find('li').length).toEqual(3);
142+
expect(element.text()).toEqual('misko:true;shyam:true;zhenbo:true;');
143+
144+
$rootScope.items = {misko:'false', shyam:'true', zhenbo: 'true'};
145+
$rootScope.$digest();
146+
expect(element.find('li').length).toEqual(3);
147+
expect(element.text()).toEqual('misko:false;shyam:true;zhenbo:true;');
148+
149+
$rootScope.items = {misko:'false', shyam:'false', zhenbo: 'false'};
150+
$rootScope.$digest();
151+
expect(element.find('li').length).toEqual(3);
152+
expect(element.text()).toEqual('misko:false;shyam:false;zhenbo:false;');
153+
154+
$rootScope.items = {misko:'true'};
155+
$rootScope.$digest();
156+
expect(element.find('li').length).toEqual(1);
157+
expect(element.text()).toEqual('misko:true;');
158+
159+
$rootScope.items = {shyam:'true', zhenbo: 'false'};
160+
$rootScope.$digest();
161+
expect(element.find('li').length).toEqual(2);
162+
expect(element.text()).toEqual('shyam:true;zhenbo:false;');
163+
}));
164+
50165

51166
it('should not ngRepeat over parent properties', inject(function($rootScope, $compile) {
52167
var Class = function() {};

0 commit comments

Comments
 (0)