OLD | NEW |
1 library ng_repeat_spec; | 1 library ng_repeat_spec; |
2 | 2 |
3 import '../_specs.dart'; | 3 import '../_specs.dart'; |
4 | 4 |
| 5 // Mock animate instance that throws on move |
| 6 class MockAnimate extends Animate { |
| 7 Animation move(Iterable<Node> nodes, Node parent, |
| 8 {Node insertBefore}) { |
| 9 throw "Move should not be called"; |
| 10 } |
| 11 } |
| 12 |
5 main() { | 13 main() { |
6 describe('NgRepeater', () { | 14 describe('NgRepeater', () { |
7 var element, $compile, scope, $exceptionHandler, directives; | 15 Element element; |
8 | 16 var compile, scope, exceptionHandler, directives; |
9 beforeEach(inject((Injector injector, Scope $rootScope, Compiler compiler, D
irectiveMap _directives) { | 17 |
10 $exceptionHandler = injector.get(ExceptionHandler); | 18 beforeEach((Injector injector, Scope rootScope, Compiler compiler, Directive
Map _directives) { |
11 scope = $rootScope; | 19 exceptionHandler = injector.get(ExceptionHandler); |
12 $compile = (html) { | 20 scope = rootScope; |
13 element = $(html); | 21 compile = (html, [scope]) { |
14 var blockFactory = compiler(element, _directives); | 22 element = e(html); |
15 var block = blockFactory(injector, element); | 23 var viewFactory = compiler([element], _directives); |
| 24 var blockInjector = injector; |
| 25 if (scope != null) { |
| 26 viewFactory.bind(injector)(scope); |
| 27 } else { |
| 28 viewFactory(injector, [element]); |
| 29 } |
16 return element; | 30 return element; |
17 }; | 31 }; |
18 directives = _directives; | 32 directives = _directives; |
19 })); | 33 }); |
20 | 34 |
21 it(r'should set create a list of items', inject((Scope scope, Compiler compi
ler, Injector injector) { | 35 it(r'should set create a list of items', (Scope scope, Compiler compiler, In
jector injector) { |
22 var element = $('<div><div ng-repeat="item in items">{{item}}</div></div>'
); | 36 var element = es('<div><div ng-repeat="item in items">{{item}}</div></div>
'); |
23 BlockFactory blockFactory = compiler(element, directives); | 37 ViewFactory viewFactory = compiler(element, directives); |
24 Block block = blockFactory(injector, element); | 38 View view = viewFactory(injector, element); |
25 scope.context['items'] = ['a', 'b']; | 39 scope.context['items'] = ['a', 'b']; |
26 scope.apply(); | 40 scope.apply(); |
27 expect(element.text()).toEqual('ab'); | 41 expect(element).toHaveText('ab'); |
28 })); | 42 }); |
| 43 |
| 44 |
| 45 it(r'should set create a list of items', (Scope scope, Compiler compiler, In
jector injector) { |
| 46 scope.context['items'] = []; |
| 47 scope.watch('1', (_, __) { |
| 48 scope.context['items'].add('a'); |
| 49 scope.context['items'].add('b'); |
| 50 }); |
| 51 var element = es('<div><div ng-repeat="item in items">{{item}}</div></div>
'); |
| 52 ViewFactory viewFactory = compiler(element, directives); |
| 53 View view = viewFactory(injector, element); |
| 54 scope.apply(); |
| 55 expect(element).toHaveText('ab'); |
| 56 }); |
29 | 57 |
30 | 58 |
31 it(r'should set create a list of items from iterable', | 59 it(r'should set create a list of items from iterable', |
32 inject((Scope scope, Compiler compiler, Injector injector) { | 60 (Scope scope, Compiler compiler, Injector injector) { |
33 var element = $('<div><div ng-repeat="item in items">{{item}}</div></div>'
); | 61 var element = es('<div><div ng-repeat="item in items">{{item}}</div></div>
'); |
34 BlockFactory blockFactory = compiler(element, directives); | 62 ViewFactory viewFactory = compiler(element, directives); |
35 Block block = blockFactory(injector, element); | 63 View view = viewFactory(injector, element); |
36 scope.context['items'] = ['a', 'b'].map((i) => i); // makes an iterable | 64 scope.context['items'] = ['a', 'b'].map((i) => i); // makes an iterable |
37 scope.apply(); | 65 scope.apply(); |
38 expect(element.text()).toEqual('ab'); | 66 expect(element).toHaveText('ab'); |
39 })); | 67 }); |
40 | 68 |
41 | 69 |
42 it(r'should iterate over an array of objects', () { | 70 it(r'should iterate over an array of objects', () { |
43 element = $compile( | 71 element = compile( |
44 '<ul>' + | 72 '<ul>' |
45 '<li ng-repeat="item in items">{{item.name}};</li>' + | 73 '<li ng-repeat="item in items">{{item.name}};</li>' |
46 '</ul>'); | 74 '</ul>'); |
47 | 75 |
48 // INIT | 76 // INIT |
49 scope.context['items'] = [{"name": 'misko'}, {"name":'shyam'}]; | 77 scope.context['items'] = [{"name": 'misko'}, {"name":'shyam'}]; |
50 scope.apply(); | 78 scope.apply(); |
51 expect(element.find('li').length).toEqual(2); | 79 expect(element.querySelectorAll('li').length).toEqual(2); |
52 expect(element.text()).toEqual('misko;shyam;'); | 80 expect(element.text).toEqual('misko;shyam;'); |
53 | 81 |
54 // GROW | 82 // GROW |
55 scope.context['items'].add({"name": 'adam'}); | 83 scope.context['items'].add({"name": 'adam'}); |
56 scope.apply(); | 84 scope.apply(); |
57 expect(element.find('li').length).toEqual(3); | 85 expect(element.querySelectorAll('li').length).toEqual(3); |
58 expect(element.text()).toEqual('misko;shyam;adam;'); | 86 expect(element.text).toEqual('misko;shyam;adam;'); |
59 | 87 |
60 // SHRINK | 88 // SHRINK |
61 scope.context['items'].removeLast(); | 89 scope.context['items'].removeLast(); |
62 scope.context['items'].removeAt(0); | 90 scope.context['items'].removeAt(0); |
63 scope.apply(); | 91 scope.apply(); |
64 expect(element.find('li').length).toEqual(1); | 92 expect(element.querySelectorAll('li').length).toEqual(1); |
65 expect(element.text()).toEqual('shyam;'); | 93 expect(element.text).toEqual('shyam;'); |
66 }); | 94 }); |
67 | 95 |
68 | 96 |
69 it(r'should gracefully handle nulls', () { | 97 it(r'should gracefully handle nulls', () { |
70 element = $compile( | 98 element = compile( |
71 '<div>' + | 99 '<div>' |
72 '<ul>' + | 100 '<ul>' |
73 '<li ng-repeat="item in null">{{item.name}};</li>' + | 101 '<li ng-repeat="item in null">{{item.name}};</li>' |
74 '</ul>' + | 102 '</ul>' |
75 '</div>'); | 103 '</div>'); |
76 scope.apply(); | 104 scope.apply(); |
77 expect(element.find('ul').length).toEqual(1); | 105 expect(element.querySelectorAll('ul').length).toEqual(1); |
78 expect(element.find('li').length).toEqual(0); | 106 expect(element.querySelectorAll('li').length).toEqual(0); |
| 107 }); |
| 108 |
| 109 |
| 110 it('should support formatters', () { |
| 111 element = compile( |
| 112 '<div><span ng-repeat="item in items | filter:myFilter">{{item}}</span
></div>'); |
| 113 scope.context['items'] = ['foo', 'bar', 'baz']; |
| 114 scope.context['myFilter'] = (String item) => item.startsWith('b'); |
| 115 scope.apply(); |
| 116 expect(element.querySelectorAll('span').length).toEqual(2); |
| 117 }); |
| 118 |
| 119 it('should support function as a formatter', () { |
| 120 scope.context['isEven'] = (num) => num % 2 == 0; |
| 121 var element = compile( |
| 122 '<div ng-show="true">' |
| 123 '<span ng-repeat="r in [1, 2] | filter:isEven">{{r}}</span>' |
| 124 '</div>'); |
| 125 scope.apply(); |
| 126 expect(element.text).toEqual('2'); |
79 }); | 127 }); |
80 | 128 |
81 | 129 |
82 describe('track by', () { | 130 describe('track by', () { |
83 it(r'should track using expression function', () { | 131 it(r'should track using expression function', () { |
84 element = $compile( | 132 element = compile( |
85 '<ul>' + | 133 '<ul>' |
86 '<li ng-repeat="item in items track by item.id">{{item.name}};</
li>' + | 134 '<li ng-repeat="item in items track by item.id">{{item.name}};</
li>' |
87 '</ul>'); | 135 '</ul>'); |
88 scope.context['items'] = [{"id": 'misko'}, {"id": 'igor'}]; | 136 scope.context['items'] = [{"id": 'misko'}, {"id": 'igor'}]; |
89 scope.apply(); | 137 scope.apply(); |
90 var li0 = element.find('li')[0]; | 138 var li0 = element.querySelectorAll('li')[0]; |
91 var li1 = element.find('li')[1]; | 139 var li1 = element.querySelectorAll('li')[1]; |
92 | 140 |
93 scope.context['items'].add(scope.context['items'].removeAt(0)); | 141 scope.context['items'].add(scope.context['items'].removeAt(0)); |
94 scope.apply(); | 142 scope.apply(); |
95 expect(element.find('li')[0]).toBe(li1); | 143 expect(element.querySelectorAll('li')[0]).toBe(li1); |
96 expect(element.find('li')[1]).toBe(li0); | 144 expect(element.querySelectorAll('li')[1]).toBe(li0); |
97 }); | 145 }); |
98 | 146 |
99 | 147 |
100 it(r'should track using build in $id function', () { | 148 it(r'should track using build in $id function', () { |
101 element = $compile( | 149 element = compile( |
102 '<ul>' + | 150 '<ul>' |
103 r'<li ng-repeat="item in items track by $id(item)">{{item.name}}
;</li>' + | 151 r'<li ng-repeat="item in items track by $id(item)">{{item.name}}
;</li>' |
104 '</ul>'); | 152 '</ul>'); |
105 scope.context['items'] = [{"name": 'misko'}, {"name": 'igor'}]; | 153 scope.context['items'] = [{"name": 'misko'}, {"name": 'igor'}]; |
106 scope.apply(); | 154 scope.apply(); |
107 var li0 = element.find('li')[0]; | 155 var li0 = element.querySelectorAll('li')[0]; |
108 var li1 = element.find('li')[1]; | 156 var li1 = element.querySelectorAll('li')[1]; |
109 | 157 |
110 scope.context['items'].add(scope.context['items'].removeAt(0)); | 158 scope.context['items'].add(scope.context['items'].removeAt(0)); |
111 scope.apply(); | 159 scope.apply(); |
112 expect(element.find('li')[0]).toBe(li1); | 160 expect(element.querySelectorAll('li')[0]).toBe(li1); |
113 expect(element.find('li')[1]).toBe(li0); | 161 expect(element.querySelectorAll('li')[1]).toBe(li0); |
114 }); | 162 }); |
115 | 163 |
116 | 164 |
117 it(r'should iterate over an array of primitives', () { | 165 it(r'should iterate over an array of primitives', () { |
118 element = $compile( | 166 element = compile( |
119 r'<ul>' + | 167 r'<ul>' |
120 r'<li ng-repeat="item in items track by $index">{{item}};</li>'
+ | 168 r'<li ng-repeat="item in items track by $index">{{item}};</li>' |
121 r'</ul>'); | 169 r'</ul>'); |
122 | 170 |
123 // INIT | 171 // INIT |
124 scope.context['items'] = [true, true, true]; | 172 scope.context['items'] = [true, true, true]; |
125 scope.apply(); | 173 scope.apply(); |
126 expect(element.find('li').length).toEqual(3); | 174 expect(element.querySelectorAll('li').length).toEqual(3); |
127 expect(element.text()).toEqual('true;true;true;'); | 175 expect(element.text).toEqual('true;true;true;'); |
128 | 176 |
129 scope.context['items'] = [false, true, true]; | 177 scope.context['items'] = [false, true, true]; |
130 scope.apply(); | 178 scope.apply(); |
131 expect(element.find('li').length).toEqual(3); | 179 expect(element.querySelectorAll('li').length).toEqual(3); |
132 expect(element.text()).toEqual('false;true;true;'); | 180 expect(element.text).toEqual('false;true;true;'); |
133 | 181 |
134 scope.context['items'] = [false, true, false]; | 182 scope.context['items'] = [false, true, false]; |
135 scope.apply(); | 183 scope.apply(); |
136 expect(element.find('li').length).toEqual(3); | 184 expect(element.querySelectorAll('li').length).toEqual(3); |
137 expect(element.text()).toEqual('false;true;false;'); | 185 expect(element.text).toEqual('false;true;false;'); |
138 | 186 |
139 scope.context['items'] = [true]; | 187 scope.context['items'] = [true]; |
140 scope.apply(); | 188 scope.apply(); |
141 expect(element.find('li').length).toEqual(1); | 189 expect(element.querySelectorAll('li').length).toEqual(1); |
142 expect(element.text()).toEqual('true;'); | 190 expect(element.text).toEqual('true;'); |
143 | 191 |
144 scope.context['items'] = [true, true, false]; | 192 scope.context['items'] = [true, true, false]; |
145 scope.apply(); | 193 scope.apply(); |
146 expect(element.find('li').length).toEqual(3); | 194 expect(element.querySelectorAll('li').length).toEqual(3); |
147 expect(element.text()).toEqual('true;true;false;'); | 195 expect(element.text).toEqual('true;true;false;'); |
148 | 196 |
149 scope.context['items'] = [true, false, false]; | 197 scope.context['items'] = [true, false, false]; |
150 scope.apply(); | 198 scope.apply(); |
151 expect(element.find('li').length).toEqual(3); | 199 expect(element.querySelectorAll('li').length).toEqual(3); |
152 expect(element.text()).toEqual('true;false;false;'); | 200 expect(element.text).toEqual('true;false;false;'); |
153 | 201 |
154 // string | 202 // string |
155 scope.context['items'] = ['a', 'a', 'a']; | 203 scope.context['items'] = ['a', 'a', 'a']; |
156 scope.apply(); | 204 scope.apply(); |
157 expect(element.find('li').length).toEqual(3); | 205 expect(element.querySelectorAll('li').length).toEqual(3); |
158 expect(element.text()).toEqual('a;a;a;'); | 206 expect(element.text).toEqual('a;a;a;'); |
159 | 207 |
160 scope.context['items'] = ['ab', 'a', 'a']; | 208 scope.context['items'] = ['ab', 'a', 'a']; |
161 scope.apply(); | 209 scope.apply(); |
162 expect(element.find('li').length).toEqual(3); | 210 expect(element.querySelectorAll('li').length).toEqual(3); |
163 expect(element.text()).toEqual('ab;a;a;'); | 211 expect(element.text).toEqual('ab;a;a;'); |
164 | 212 |
165 scope.context['items'] = ['test']; | 213 scope.context['items'] = ['test']; |
166 scope.apply(); | 214 scope.apply(); |
167 expect(element.find('li').length).toEqual(1); | 215 expect(element.querySelectorAll('li').length).toEqual(1); |
168 expect(element.text()).toEqual('test;'); | 216 expect(element.text).toEqual('test;'); |
169 | 217 |
170 scope.context['items'] = ['same', 'value']; | 218 scope.context['items'] = ['same', 'value']; |
171 scope.apply(); | 219 scope.apply(); |
172 expect(element.find('li').length).toEqual(2); | 220 expect(element.querySelectorAll('li').length).toEqual(2); |
173 expect(element.text()).toEqual('same;value;'); | 221 expect(element.text).toEqual('same;value;'); |
174 | 222 |
175 // number | 223 // number |
176 scope.context['items'] = [12, 12, 12]; | 224 scope.context['items'] = [12, 12, 12]; |
177 scope.apply(); | 225 scope.apply(); |
178 expect(element.find('li').length).toEqual(3); | 226 expect(element.querySelectorAll('li').length).toEqual(3); |
179 expect(element.text()).toEqual('12;12;12;'); | 227 expect(element.text).toEqual('12;12;12;'); |
180 | 228 |
181 scope.context['items'] = [53, 12, 27]; | 229 scope.context['items'] = [53, 12, 27]; |
182 scope.apply(); | 230 scope.apply(); |
183 expect(element.find('li').length).toEqual(3); | 231 expect(element.querySelectorAll('li').length).toEqual(3); |
184 expect(element.text()).toEqual('53;12;27;'); | 232 expect(element.text).toEqual('53;12;27;'); |
185 | 233 |
186 scope.context['items'] = [89]; | 234 scope.context['items'] = [89]; |
187 scope.apply(); | 235 scope.apply(); |
188 expect(element.find('li').length).toEqual(1); | 236 expect(element.querySelectorAll('li').length).toEqual(1); |
189 expect(element.text()).toEqual('89;'); | 237 expect(element.text).toEqual('89;'); |
190 | 238 |
191 scope.context['items'] = [89, 23]; | 239 scope.context['items'] = [89, 23]; |
192 scope.apply(); | 240 scope.apply(); |
193 expect(element.find('li').length).toEqual(2); | 241 expect(element.querySelectorAll('li').length).toEqual(2); |
194 expect(element.text()).toEqual('89;23;'); | 242 expect(element.text).toEqual('89;23;'); |
195 }); | 243 }); |
196 | 244 |
197 }); | 245 }); |
198 | 246 |
199 | 247 |
200 it(r'should error on wrong parsing of ngRepeat', () { | 248 it(r'should error on wrong parsing of ngRepeat', () { |
201 element = $('<ul><li ng-repeat="i dont parse"></li></ul>'); | |
202 expect(() { | 249 expect(() { |
203 $compile(element); | 250 compile('<ul><li ng-repeat="i dont parse"></li></ul>')(); |
204 }).toThrow("[NgErr7] ngRepeat error! Expected expression in form of '_item
_ in _collection_[ track by _id_]' but got 'i dont parse'."); | 251 }).toThrow("[NgErr7] ngRepeat error! Expected expression in form of " |
| 252 "'_item_ in _collection_[ track by _id_]' but got " |
| 253 "'i dont parse'."); |
205 }); | 254 }); |
206 | 255 |
207 | 256 |
208 it("should throw error when left-hand-side of ngRepeat can't be parsed", ()
{ | 257 it("should throw error when left-hand-side of ngRepeat can't be parsed", ()
{ |
209 element = $('<ul><li ng-repeat="i dont parse in foo"></li></ul>'); | |
210 expect(() { | 258 expect(() { |
211 $compile(element); | 259 compile('<ul><li ng-repeat="i dont parse in foo"></li></ul>')(); |
212 }).toThrow("[NgErr8] ngRepeat error! '_item_' in '_item_ in _collection_
' should be an identifier or '(_key_, _value_)' expression, but got 'i dont pars
e'."); | 260 }).toThrow("[NgErr8] ngRepeat error! '_item_' in '_item_ in " |
| 261 "_collection_' should be an identifier or '(_key_, _value_)' " |
| 262 "expression, but got 'i dont parse'."); |
213 }); | 263 }); |
214 | 264 |
215 | 265 |
216 it(r'should expose iterator offset as $index when iterating over arrays', | 266 it(r'should expose iterator offset as $index when iterating over arrays', |
217 () { | 267 () { |
218 element = $compile( | 268 element = compile( |
219 '<ul>' + | 269 '<ul>' + |
220 '<li ng-repeat="item in items">{{item}}:{{\$index}}|</li>' + | 270 '<li ng-repeat="item in items">{{item}}:{{\$index}}|</li>' + |
221 '</ul>'); | 271 '</ul>'); |
222 scope.context['items'] = ['misko', 'shyam', 'frodo']; | 272 scope.context['items'] = ['misko', 'shyam', 'frodo']; |
223 scope.apply(); | 273 scope.apply(); |
224 expect(element.text()).toEqual('misko:0|shyam:1|frodo:2|'); | 274 expect(element.text).toEqual('misko:0|shyam:1|frodo:2|'); |
225 }); | 275 }); |
226 | 276 |
227 | 277 |
228 it(r'should expose iterator position as $first, $middle and $last when itera
ting over arrays', | 278 it(r'should expose iterator position as $first, $middle and $last when itera
ting over arrays', |
229 () { | 279 () { |
230 element = $compile( | 280 element = compile( |
231 '<ul>' + | 281 '<ul>' |
232 '<li ng-repeat="item in items">{{item}}:{{\$first}}-{{\$middle}}-{{\$l
ast}}|</li>' + | 282 '<li ng-repeat="item in items">{{item}}:{{\$first}}-{{\$middle}}-{{\$l
ast}}|</li>' |
233 '</ul>'); | 283 '</ul>'); |
234 scope.context['items'] = ['misko', 'shyam', 'doug']; | 284 scope.context['items'] = ['misko', 'shyam', 'doug']; |
235 scope.apply(); | 285 scope.apply(); |
236 expect(element.text()). | 286 expect(element.text) |
237 toEqual('misko:true-false-false|shyam:false-true-false|doug:false-fals
e-true|'); | 287 .toEqual('misko:true-false-false|' |
| 288 'shyam:false-true-false|' |
| 289 'doug:false-false-true|'); |
238 | 290 |
239 scope.context['items'].add('frodo'); | 291 scope.context['items'].add('frodo'); |
240 scope.apply(); | 292 scope.apply(); |
241 expect(element.text()). | 293 expect(element.text) |
242 toEqual('misko:true-false-false|' + | 294 .toEqual('misko:true-false-false|' |
243 'shyam:false-true-false|' + | 295 'shyam:false-true-false|' |
244 'doug:false-true-false|' + | 296 'doug:false-true-false|' |
245 'frodo:false-false-true|'); | 297 'frodo:false-false-true|'); |
246 | 298 |
247 scope.context['items'].removeLast(); | 299 scope.context['items'].removeLast(); |
248 scope.context['items'].removeLast(); | 300 scope.context['items'].removeLast(); |
249 scope.apply(); | 301 scope.apply(); |
250 expect(element.text()).toEqual('misko:true-false-false|shyam:false-false-t
rue|'); | 302 |
251 | 303 expect(element.text).toEqual('misko:true-false-false|' |
252 scope.context['items'].removeLast(); | 304 'shyam:false-false-true|'); |
253 scope.apply(); | 305 scope.context['items'].removeLast(); |
254 expect(element.text()).toEqual('misko:true-false-true|'); | 306 scope.apply(); |
| 307 expect(element.text).toEqual('misko:true-false-true|'); |
255 }); | 308 }); |
256 | 309 |
257 it(r'should report odd', () { | 310 it(r'should report odd', () { |
258 element = $compile( | 311 element = compile( |
259 '<ul>' + | 312 '<ul>' |
260 '<li ng-repeat="item in items">{{item}}:{{\$odd}}-{{\$even}}|</li>' + | 313 '<li ng-repeat="item in items">{{item}}:{{\$odd}}-{{\$even}}|</li>' |
261 '</ul>'); | 314 '</ul>'); |
262 scope.context['items'] = ['misko', 'shyam', 'doug']; | 315 scope.context['items'] = ['misko', 'shyam', 'doug']; |
263 scope.apply(); | 316 scope.apply(); |
264 expect(element.text()).toEqual('misko:false-true|shyam:true-false|doug:fal
se-true|'); | 317 expect(element.text).toEqual('misko:false-true|' |
| 318 'shyam:true-false|' |
| 319 'doug:false-true|'); |
265 | 320 |
266 scope.context['items'].add('frodo'); | 321 scope.context['items'].add('frodo'); |
267 scope.apply(); | 322 scope.apply(); |
268 expect(element.text()).toEqual('misko:false-true|shyam:true-false|doug:fal
se-true|frodo:true-false|'); | 323 expect(element.text).toEqual('misko:false-true|' |
269 | 324 'shyam:true-false|' |
270 scope.context['items'].removeLast(); | 325 'doug:false-true|' |
271 scope.context['items'].removeLast(); | 326 'frodo:true-false|'); |
272 scope.apply(); | 327 |
273 expect(element.text()).toEqual('misko:false-true|shyam:true-false|'); | 328 scope.context['items'].removeLast(); |
274 | 329 scope.context['items'].removeLast(); |
275 scope.context['items'].removeLast(); | 330 scope.apply(); |
276 scope.apply(); | 331 expect(element.text).toEqual('misko:false-true|shyam:true-false|'); |
277 expect(element.text()).toEqual('misko:false-true|'); | 332 |
| 333 scope.context['items'].removeLast(); |
| 334 scope.apply(); |
| 335 expect(element.text).toEqual('misko:false-true|'); |
278 }); | 336 }); |
279 | 337 |
280 it(r'should repeat over nested arrays', () { | 338 it(r'should repeat over nested arrays', () { |
281 element = $compile( | 339 element = compile( |
282 '<ul>' + | 340 '<ul>' + |
283 '<li ng-repeat="subgroup in groups">' + | 341 '<li ng-repeat="subgroup in groups">' + |
284 '<div ng-repeat="group in subgroup">{{group}}|</div>X' + | 342 '<div ng-repeat="group in subgroup">{{group}}|</div>X' + |
285 '</li>' + | 343 '</li>' + |
286 '</ul>'); | 344 '</ul>'); |
287 scope.context['groups'] = [['a', 'b'], ['c','d']]; | 345 scope.context['groups'] = [['a', 'b'], ['c','d']]; |
288 scope.apply(); | 346 scope.apply(); |
289 | 347 |
290 expect(element.text()).toEqual('a|b|Xc|d|X'); | 348 expect(element.text).toEqual('a|b|Xc|d|X'); |
291 }); | 349 }); |
292 | 350 |
293 | 351 |
294 describe('stability', () { | 352 describe('stability', () { |
295 var a, b, c, d, lis; | 353 var a, b, c, d, lis; |
296 | 354 |
297 beforeEach(() { | 355 beforeEach(() { |
298 element = $compile( | 356 element = compile( |
299 '<ul>' + | 357 '<ul>' |
300 '<li ng-repeat="item in items">{{key}}:{{val}}|></li>' + | 358 '<li ng-repeat="item in items">{{key}}:{{val}}|></li>' |
301 '</ul>'); | 359 '</ul>'); |
302 a = {}; | 360 a = {}; |
303 b = {}; | 361 b = {}; |
304 c = {}; | 362 c = {}; |
305 d = {}; | 363 d = {}; |
306 | 364 |
307 scope.context['items'] = [a, b, c]; | 365 scope.context['items'] = [a, b, c]; |
308 scope.apply(); | 366 scope.apply(); |
309 lis = element.find('li'); | 367 lis = element.querySelectorAll('li'); |
310 }); | 368 }); |
311 | 369 |
312 | 370 |
313 it(r'should preserve the order of elements', () { | 371 it(r'should preserve the order of elements', () { |
314 scope.context['items'] = [a, c, d]; | 372 scope.context['items'] = [a, c, d]; |
315 scope.apply(); | 373 scope.apply(); |
316 var newElements = element.find('li'); | 374 var newElements = element.querySelectorAll('li'); |
317 expect(newElements[0]).toEqual(lis[0]); | 375 expect(newElements[0]).toEqual(lis[0]); |
318 expect(newElements[1]).toEqual(lis[2]); | 376 expect(newElements[1]).toEqual(lis[2]); |
319 expect(newElements[2] == lis[1]).toEqual(false); | 377 expect(newElements[2]).not.toEqual(lis[1]); |
320 }); | 378 }); |
321 | 379 |
322 | 380 it(r'should not throw an error on duplicates', () { |
323 it(r'should throw error on adding existing duplicates and recover', () { | |
324 scope.context['items'] = [a, a, a]; | 381 scope.context['items'] = [a, a, a]; |
325 expect(() { | 382 expect(() => scope.apply()).not.toThrow(); |
326 scope.apply(); | 383 scope.context['items'].add(a); |
327 }).toThrow("[NgErr50] ngRepeat error! Duplicates in a repeater are not a
llowed. Use 'track by' expression to specify unique keys. Repeater: item in item
s, Duplicate key: {}"); | 384 expect(() => scope.apply()).not.toThrow(); |
328 | |
329 // recover | |
330 scope.context['items'] = [a]; | |
331 scope.apply(); | |
332 var newElements = element.find('li'); | |
333 expect(newElements.length).toEqual(1); | |
334 expect(newElements[0]).toEqual(lis[0]); | |
335 | |
336 scope.context['items'] = []; | |
337 scope.apply(); | |
338 newElements = element.find('li'); | |
339 expect(newElements.length).toEqual(0); | |
340 }); | 385 }); |
341 | 386 |
342 | |
343 it(r'should throw error on new duplicates and recover', () { | |
344 scope.context['items'] = [d, d, d]; | |
345 expect(() { | |
346 scope.apply(); | |
347 }).toThrow("[NgErr50] ngRepeat error! Duplicates in a repeater are not a
llowed. Use 'track by' expression to specify unique keys. Repeater: item in item
s, Duplicate key: {}"); | |
348 | |
349 // recover | |
350 scope.context['items'] = [a]; | |
351 scope.apply(); | |
352 var newElements = element.find('li'); | |
353 expect(newElements.length).toEqual(1); | |
354 expect(newElements[0]).toEqual(lis[0]); | |
355 | |
356 scope.context['items'] = []; | |
357 scope.apply(); | |
358 newElements = element.find('li'); | |
359 expect(newElements.length).toEqual(0); | |
360 }); | |
361 | |
362 | |
363 it(r'should reverse items when the collection is reversed', () { | 387 it(r'should reverse items when the collection is reversed', () { |
364 scope.context['items'] = [a, b, c]; | 388 scope.context['items'] = [a, b, c]; |
365 scope.apply(); | 389 scope.apply(); |
366 lis = element.find('li'); | 390 lis = element.querySelectorAll('li'); |
367 | 391 |
368 scope.context['items'] = [c, b, a]; | 392 scope.context['items'] = [c, b, a]; |
369 scope.apply(); | 393 scope.apply(); |
370 var newElements = element.find('li'); | 394 var newElements = element.querySelectorAll('li'); |
371 expect(newElements.length).toEqual(3); | 395 expect(newElements.length).toEqual(3); |
372 expect(newElements[0]).toEqual(lis[2]); | 396 expect(newElements[0]).toEqual(lis[2]); |
373 expect(newElements[1]).toEqual(lis[1]); | 397 expect(newElements[1]).toEqual(lis[1]); |
374 expect(newElements[2]).toEqual(lis[0]); | 398 expect(newElements[2]).toEqual(lis[0]); |
375 }); | 399 }); |
376 | 400 |
377 | 401 |
378 it(r'should reuse elements even when model is composed of primitives', ()
{ | 402 it(r'should reuse elements even when model is composed of primitives', ()
{ |
379 // rebuilding repeater from scratch can be expensive, we should try to a
void it even for | 403 // rebuilding repeater from scratch can be expensive, we should try to |
380 // model that is composed of primitives. | 404 // avoid it even for model that is composed of primitives. |
381 | 405 |
382 scope.context['items'] = ['hello', 'cau', 'ahoj']; | 406 scope.context['items'] = ['hello', 'cau', 'ahoj']; |
383 scope.apply(); | 407 scope.apply(); |
384 lis = element.find('li'); | 408 lis = element.querySelectorAll('li'); |
385 lis[2].id = 'yes'; | 409 lis[2].id = 'yes'; |
386 | 410 |
387 scope.context['items'] = ['ahoj', 'hello', 'cau']; | 411 scope.context['items'] = ['ahoj', 'hello', 'cau']; |
388 scope.apply(); | 412 scope.apply(); |
389 var newLis = element.find('li'); | 413 var newLis = element.querySelectorAll('li'); |
390 expect(newLis.length).toEqual(3); | 414 expect(newLis.length).toEqual(3); |
391 expect(newLis[0]).toEqual(lis[2]); | 415 expect(newLis[0]).toEqual(lis[2]); |
392 expect(newLis[1]).toEqual(lis[0]); | 416 expect(newLis[1]).toEqual(lis[0]); |
393 expect(newLis[2]).toEqual(lis[1]); | 417 expect(newLis[2]).toEqual(lis[1]); |
394 }); | 418 }); |
395 }); | 419 }); |
396 | 420 |
| 421 |
| 422 it('should correctly handle detached state', () { |
| 423 scope.context['items'] = [1]; |
| 424 |
| 425 var parentScope = scope.createChild(new PrototypeMap(scope.context)); |
| 426 element = compile( |
| 427 '<ul>' |
| 428 '<li ng-repeat="item in items">{{item}}</li>' |
| 429 '</ul>', parentScope); |
| 430 |
| 431 parentScope.destroy(); |
| 432 expect(scope.apply).not.toThrow(); |
| 433 }); |
| 434 |
| 435 it(r'should not move blocks when elements only added or removed', |
| 436 inject((Injector injector) { |
| 437 var throwOnMove = new MockAnimate(); |
| 438 var child = injector.createChild( |
| 439 [new Module()..value(Animate, throwOnMove)]); |
| 440 |
| 441 child.invoke((Injector injector, Scope rootScope, Compiler compiler, |
| 442 DirectiveMap _directives) { |
| 443 exceptionHandler = injector.get(ExceptionHandler); |
| 444 scope = rootScope; |
| 445 compile = (html) { |
| 446 element = e(html); |
| 447 var viewFactory = compiler([element], _directives); |
| 448 viewFactory(injector, [element]); |
| 449 return element; |
| 450 }; |
| 451 directives = _directives; |
| 452 }); |
| 453 |
| 454 element = compile( |
| 455 '<ul>' |
| 456 '<li ng-repeat="item in items">{{item}}</li>' |
| 457 '</ul>'); |
| 458 |
| 459 scope..context['items'] = ['a', 'b', 'c'] |
| 460 ..apply() |
| 461 // grow |
| 462 ..context['items'].add('d') |
| 463 ..apply() |
| 464 // shrink |
| 465 ..context['items'].removeLast() |
| 466 ..apply() |
| 467 ..context['items'].removeAt(0) |
| 468 ..apply(); |
| 469 |
| 470 expect(element).toHaveText('bc'); |
| 471 })); |
397 }); | 472 }); |
398 } | 473 } |
OLD | NEW |