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