| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library template_binding.test.custom_element_bindings_test; | 5 library template_binding.test.custom_element_bindings_test; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:html'; | 8 import 'dart:html'; |
| 9 import 'dart:collection' show MapView; | 9 import 'dart:collection' show MapView; |
| 10 import 'package:template_binding/template_binding.dart'; | 10 import 'package:template_binding/template_binding.dart'; |
| 11 import 'package:observe/observe.dart'; | 11 import 'package:observe/observe.dart'; |
| 12 import 'package:unittest/html_config.dart'; | 12 import 'package:unittest/html_config.dart'; |
| 13 import 'package:unittest/unittest.dart'; | 13 import 'package:unittest/unittest.dart'; |
| 14 import 'package:web_components/polyfill.dart'; | 14 import 'package:web_components/polyfill.dart'; |
| 15 import 'utils.dart'; | 15 import 'utils.dart'; |
| 16 | 16 |
| 17 Future _registered; | 17 Future _registered; |
| 18 | 18 |
| 19 main() => dirtyCheckZone().run(() { | 19 main() => dirtyCheckZone().run(() { |
| 20 useHtmlConfiguration(); | 20 useHtmlConfiguration(); |
| 21 | 21 |
| 22 _registered = customElementsReady.then((_) { | 22 _registered = customElementsReady.then((_) { |
| 23 document.register('my-custom-element', MyCustomElement); | 23 document.registerElement('my-custom-element', MyCustomElement); |
| 24 document.register('with-attrs-custom-element', WithAttrsCustomElement); | 24 document.registerElement('with-attrs-custom-element', |
| 25 WithAttrsCustomElement); |
| 25 }); | 26 }); |
| 26 | 27 |
| 27 group('Custom Element Bindings', customElementBindingsTest); | 28 group('Custom Element Bindings', customElementBindingsTest); |
| 28 }); | 29 }); |
| 29 | 30 |
| 30 customElementBindingsTest() { | 31 customElementBindingsTest() { |
| 31 setUp(() { | 32 setUp(() { |
| 32 document.body.append(testDiv = new DivElement()); | 33 document.body.append(testDiv = new DivElement()); |
| 33 return _registered; | 34 return _registered; |
| 34 }); | 35 }); |
| 35 | 36 |
| 36 tearDown(() { | 37 tearDown(() { |
| 37 testDiv.remove(); | 38 testDiv.remove(); |
| 38 testDiv = null; | 39 testDiv = null; |
| 39 }); | 40 }); |
| 40 | 41 |
| 41 test('override bind/unbind/unbindAll', () { | 42 test('override bind/bindFinished', () { |
| 42 var element = new MyCustomElement(); | 43 var element = new MyCustomElement(); |
| 43 var model = toObservable({'a': new Point(123, 444), 'b': new Monster(100)}); | 44 var model = toObservable({'a': new Point(123, 444), 'b': new Monster(100)}); |
| 44 | 45 |
| 45 nodeBind(element) | 46 var pointBinding = nodeBind(element) |
| 46 ..bind('my-point', new PathObserver(model, 'a')) | 47 .bind('my-point', new PathObserver(model, 'a')); |
| 47 ..bind('scary-monster', new PathObserver(model, 'b')); | 48 |
| 49 var scaryBinding = nodeBind(element) |
| 50 .bind('scary-monster', new PathObserver(model, 'b')); |
| 48 | 51 |
| 49 expect(element.attributes, isNot(contains('my-point'))); | 52 expect(element.attributes, isNot(contains('my-point'))); |
| 50 expect(element.attributes, isNot(contains('scary-monster'))); | 53 expect(element.attributes, isNot(contains('scary-monster'))); |
| 51 | 54 |
| 52 expect(element.myPoint, model['a']); | 55 expect(element.myPoint, model['a']); |
| 53 expect(element.scaryMonster, model['b']); | 56 expect(element.scaryMonster, model['b']); |
| 54 | 57 |
| 55 model['a'] = null; | 58 model['a'] = null; |
| 56 return new Future(() { | 59 return new Future(() { |
| 57 expect(element.myPoint, null); | 60 expect(element.myPoint, null); |
| 58 nodeBind(element).unbind('my-point'); | 61 expect(element.bindFinishedCalled, 0); |
| 62 pointBinding.close(); |
| 59 | 63 |
| 60 model['a'] = new Point(1, 2); | 64 model['a'] = new Point(1, 2); |
| 61 model['b'] = new Monster(200); | 65 model['b'] = new Monster(200); |
| 62 }).then(endOfMicrotask).then((_) { | 66 }).then(endOfMicrotask).then((_) { |
| 63 expect(element.scaryMonster, model['b']); | 67 expect(element.scaryMonster, model['b']); |
| 64 expect(element.myPoint, null, reason: 'a was unbound'); | 68 expect(element.myPoint, null, reason: 'a was unbound'); |
| 65 | 69 |
| 66 nodeBind(element).unbindAll(); | 70 scaryBinding.close(); |
| 67 model['b'] = null; | 71 model['b'] = null; |
| 68 }).then(endOfMicrotask).then((_) { | 72 }).then(endOfMicrotask).then((_) { |
| 69 expect(element.scaryMonster.health, 200); | 73 expect(element.scaryMonster.health, 200); |
| 74 expect(element.bindFinishedCalled, 0); |
| 70 }); | 75 }); |
| 71 }); | 76 }); |
| 72 | 77 |
| 73 test('override attribute setter', () { | 78 test('override attribute setter', () { |
| 74 var element = new WithAttrsCustomElement(); | 79 var element = new WithAttrsCustomElement(); |
| 75 var model = toObservable({'a': 1, 'b': 2}); | 80 var model = toObservable({'a': 1, 'b': 2}); |
| 76 nodeBind(element).bind('hidden?', new PathObserver(model, 'a')); | 81 nodeBind(element).bind('hidden?', new PathObserver(model, 'a')); |
| 77 nodeBind(element).bind('id', new PathObserver(model, 'b')); | 82 nodeBind(element).bind('id', new PathObserver(model, 'b')); |
| 78 | 83 |
| 79 expect(element.attributes, contains('hidden')); | 84 expect(element.attributes, contains('hidden')); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 117 | 122 |
| 118 var model = toObservable({'a': new Point(123, 444), 'b': new Monster(100)}); | 123 var model = toObservable({'a': new Point(123, 444), 'b': new Monster(100)}); |
| 119 var div = createTestHtml('<template bind>' | 124 var div = createTestHtml('<template bind>' |
| 120 '<my-custom-element my-point="{{a}}" scary-monster="{{b}}">' | 125 '<my-custom-element my-point="{{a}}" scary-monster="{{b}}">' |
| 121 '</my-custom-element>' | 126 '</my-custom-element>' |
| 122 '</template>'); | 127 '</template>'); |
| 123 | 128 |
| 124 templateBind(div.query('template')).model = model; | 129 templateBind(div.query('template')).model = model; |
| 125 var element; | 130 var element; |
| 126 return new Future(() { | 131 return new Future(() { |
| 127 print('!!! running future'); | |
| 128 element = div.nodes[1]; | 132 element = div.nodes[1]; |
| 129 | 133 |
| 130 expect(element is MyCustomElement, true, | 134 expect(element is MyCustomElement, true, |
| 131 reason: '$element should be a MyCustomElement'); | 135 reason: '$element should be a MyCustomElement'); |
| 132 | 136 |
| 133 expect(element.myPoint, model['a']); | 137 expect(element.myPoint, model['a']); |
| 134 expect(element.scaryMonster, model['b']); | 138 expect(element.scaryMonster, model['b']); |
| 135 | 139 |
| 136 expect(element.attributes, isNot(contains('my-point'))); | 140 expect(element.attributes, isNot(contains('my-point'))); |
| 137 expect(element.attributes, isNot(contains('scary-monster'))); | 141 expect(element.attributes, isNot(contains('scary-monster'))); |
| 138 | 142 |
| 143 expect(element.bindFinishedCalled, 1); |
| 144 |
| 139 model['a'] = null; | 145 model['a'] = null; |
| 140 }).then(endOfMicrotask).then((_) { | 146 }).then(endOfMicrotask).then((_) { |
| 141 expect(element.myPoint, null); | 147 expect(element.myPoint, null); |
| 148 expect(element.bindFinishedCalled, 1); |
| 149 |
| 142 | 150 |
| 143 templateBind(div.query('template')).model = null; | 151 templateBind(div.query('template')).model = null; |
| 144 }).then(endOfMicrotask).then((_) { | 152 }).then(endOfMicrotask).then((_) { |
| 145 | 153 // Note: the detached element |
| 146 expect(element.parentNode, null, reason: 'element was detached'); | 154 expect(element.parentNode is DocumentFragment, true, |
| 155 reason: 'removed element is added back to its document fragment'); |
| 156 expect(element.parentNode.parentNode, null, |
| 157 reason: 'document fragment is detached'); |
| 158 expect(element.bindFinishedCalled, 1); |
| 147 | 159 |
| 148 model['a'] = new Point(1, 2); | 160 model['a'] = new Point(1, 2); |
| 149 model['b'] = new Monster(200); | 161 model['b'] = new Monster(200); |
| 150 }).then(endOfMicrotask).then((_) { | 162 }).then(endOfMicrotask).then((_) { |
| 151 | |
| 152 expect(element.myPoint, null, reason: 'model was unbound'); | 163 expect(element.myPoint, null, reason: 'model was unbound'); |
| 153 expect(element.scaryMonster.health, 100, reason: 'model was unbound'); | 164 expect(element.scaryMonster.health, 100, reason: 'model was unbound'); |
| 165 expect(element.bindFinishedCalled, 1); |
| 154 }); | 166 }); |
| 155 }); | 167 }); |
| 156 | 168 |
| 157 } | 169 } |
| 158 | 170 |
| 159 class Monster { | 171 class Monster { |
| 160 int health; | 172 int health; |
| 161 Monster(this.health); | 173 Monster(this.health); |
| 162 } | 174 } |
| 163 | 175 |
| 164 /** Demonstrates a custom element overriding bind/unbind/unbindAll. */ | 176 /** Demonstrates a custom element overriding bind/bindFinished. */ |
| 165 class MyCustomElement extends HtmlElement implements NodeBindExtension { | 177 class MyCustomElement extends HtmlElement implements NodeBindExtension { |
| 166 Point myPoint; | 178 Point myPoint; |
| 167 Monster scaryMonster; | 179 Monster scaryMonster; |
| 180 int bindFinishedCalled = 0; |
| 168 | 181 |
| 169 factory MyCustomElement() => new Element.tag('my-custom-element'); | 182 factory MyCustomElement() => new Element.tag('my-custom-element'); |
| 170 | 183 |
| 171 MyCustomElement.created() : super.created(); | 184 MyCustomElement.created() : super.created(); |
| 172 | 185 |
| 173 Bindable bind(String name, value, {oneTime: false}) { | 186 Bindable bind(String name, value, {oneTime: false}) { |
| 174 switch (name) { | 187 switch (name) { |
| 175 case 'my-point': | 188 case 'my-point': |
| 176 case 'scary-monster': | 189 case 'scary-monster': |
| 177 attributes.remove(name); | 190 attributes.remove(name); |
| 178 if (oneTime) { | 191 if (oneTime) { |
| 179 _setProperty(name, value); | 192 _setProperty(name, value); |
| 180 return null; | 193 return null; |
| 181 } | 194 } |
| 182 unbind(name); | |
| 183 _setProperty(name, value.open((x) => _setProperty(name, x))); | 195 _setProperty(name, value.open((x) => _setProperty(name, x))); |
| 196 |
| 197 if (!enableBindingsReflection) return value; |
| 198 if (bindings == null) bindings = {}; |
| 199 var old = bindings[name]; |
| 200 if (old != null) old.close(); |
| 184 return bindings[name] = value; | 201 return bindings[name] = value; |
| 185 } | 202 } |
| 186 return nodeBindFallback(this).bind(name, value, oneTime: oneTime); | 203 return nodeBindFallback(this).bind(name, value, oneTime: oneTime); |
| 187 } | 204 } |
| 188 | 205 |
| 189 unbind(name) => nodeBindFallback(this).unbind(name); | 206 void bindFinished() { |
| 190 unbindAll() => nodeBindFallback(this).unbindAll(); | 207 bindFinishedCalled++; |
| 208 } |
| 209 |
| 191 get bindings => nodeBindFallback(this).bindings; | 210 get bindings => nodeBindFallback(this).bindings; |
| 211 set bindings(x) => nodeBindFallback(this).bindings = x; |
| 192 get templateInstance => nodeBindFallback(this).templateInstance; | 212 get templateInstance => nodeBindFallback(this).templateInstance; |
| 193 | 213 |
| 194 void _setProperty(String property, newValue) { | 214 void _setProperty(String property, newValue) { |
| 195 if (property == 'my-point') myPoint = newValue; | 215 if (property == 'my-point') myPoint = newValue; |
| 196 if (property == 'scary-monster') scaryMonster = newValue; | 216 if (property == 'scary-monster') scaryMonster = newValue; |
| 197 } | 217 } |
| 198 } | 218 } |
| 199 | 219 |
| 200 | 220 |
| 201 /** | 221 /** |
| (...skipping 19 matching lines...) Expand all Loading... |
| 221 | 241 |
| 222 AttributeMapWrapper(Map map) : super(map); | 242 AttributeMapWrapper(Map map) : super(map); |
| 223 | 243 |
| 224 void operator []=(K key, V value) { | 244 void operator []=(K key, V value) { |
| 225 log.add(['[]=', key, value]); | 245 log.add(['[]=', key, value]); |
| 226 super[key] = value; | 246 super[key] = value; |
| 227 } | 247 } |
| 228 | 248 |
| 229 V remove(Object key) { | 249 V remove(Object key) { |
| 230 log.add(['remove', key]); | 250 log.add(['remove', key]); |
| 231 super.remove(key); | 251 return super.remove(key); |
| 232 } | 252 } |
| 233 } | 253 } |
| OLD | NEW |