| 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 custom_element_bindings_test; | 5 library custom_element_bindings_test; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:html'; | 8 import 'dart:html'; |
| 9 import 'package:mdv/mdv.dart' as mdv; | 9 import 'package:mdv/mdv.dart' as mdv; |
| 10 import 'package:observe/observe.dart'; | 10 import 'package:observe/observe.dart'; |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 119 | 119 |
| 120 observeTest('template bind uses overridden custom element bind', () { | 120 observeTest('template bind uses overridden custom element bind', () { |
| 121 | 121 |
| 122 var model = toSymbolMap({'a': new Point(123, 444), 'b': new Monster(100)}); | 122 var model = toSymbolMap({'a': new Point(123, 444), 'b': new Monster(100)}); |
| 123 | 123 |
| 124 var div = createTestHtml('<template bind>' | 124 var div = createTestHtml('<template bind>' |
| 125 '<my-custom-element my-point="{{a}}" scary-monster="{{b}}">' | 125 '<my-custom-element my-point="{{a}}" scary-monster="{{b}}">' |
| 126 '</my-custom-element>' | 126 '</my-custom-element>' |
| 127 '</template>'); | 127 '</template>'); |
| 128 | 128 |
| 129 mdv.instanceCreated.listen((fragment) { | 129 callback(fragment) { |
| 130 for (var e in fragment.queryAll('my-custom-element')) { | 130 for (var e in fragment.queryAll('my-custom-element')) { |
| 131 new MyCustomElement.attach(e); | 131 new MyCustomElement.attach(e); |
| 132 } | 132 } |
| 133 }); | 133 } |
| 134 mdv.instanceCreated.add(callback); |
| 134 | 135 |
| 135 div.query('template').model = model; | 136 div.query('template').model = model; |
| 136 performMicrotaskCheckpoint(); | 137 performMicrotaskCheckpoint(); |
| 137 | 138 |
| 138 var element = div.nodes[1]; | 139 var element = div.nodes[1]; |
| 139 | 140 |
| 140 expect(element.xtag is MyCustomElement, true, | 141 expect(element.xtag is MyCustomElement, true, |
| 141 reason: '${element.xtag} should be a MyCustomElement'); | 142 reason: '${element.xtag} should be a MyCustomElement'); |
| 142 | 143 |
| 143 expect(element.xtag.myPoint, model[sym('a')]); | 144 expect(element.xtag.myPoint, model[sym('a')]); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 154 performMicrotaskCheckpoint(); | 155 performMicrotaskCheckpoint(); |
| 155 | 156 |
| 156 expect(element.parentNode, null, reason: 'element was detached'); | 157 expect(element.parentNode, null, reason: 'element was detached'); |
| 157 | 158 |
| 158 model[sym('a')] = new Point(1, 2); | 159 model[sym('a')] = new Point(1, 2); |
| 159 model[sym('b')] = new Monster(200); | 160 model[sym('b')] = new Monster(200); |
| 160 performMicrotaskCheckpoint(); | 161 performMicrotaskCheckpoint(); |
| 161 | 162 |
| 162 expect(element.xtag.myPoint, null, reason: 'model was unbound'); | 163 expect(element.xtag.myPoint, null, reason: 'model was unbound'); |
| 163 expect(element.xtag.scaryMonster.health, 100, reason: 'model was unbound'); | 164 expect(element.xtag.scaryMonster.health, 100, reason: 'model was unbound'); |
| 165 |
| 166 mdv.instanceCreated.remove(callback); |
| 164 }); | 167 }); |
| 165 | 168 |
| 166 } | 169 } |
| 167 | 170 |
| 168 class Monster { | 171 class Monster { |
| 169 int health; | 172 int health; |
| 170 Monster(this.health); | 173 Monster(this.health); |
| 171 } | 174 } |
| 172 | 175 |
| 173 /** Demonstrates a custom element overriding bind/unbind/unbindAll. */ | 176 /** Demonstrates a custom element overriding bind/unbind/unbindAll. */ |
| 174 class MyCustomElement implements Element { | 177 class MyCustomElement implements Element { |
| 175 final Element real; | 178 final Element real; |
| 176 | 179 |
| 177 Point myPoint; | 180 Point myPoint; |
| 178 Monster scaryMonster; | 181 Monster scaryMonster; |
| 179 | 182 |
| 180 StreamSubscription _sub1, _sub2; | |
| 181 | |
| 182 MyCustomElement() : this.attach(new Element.tag('my-custom-element')); | 183 MyCustomElement() : this.attach(new Element.tag('my-custom-element')); |
| 183 | 184 |
| 184 MyCustomElement.attach(this.real) { | 185 MyCustomElement.attach(this.real) { |
| 185 real.xtag = this; | 186 real.xtag = this; |
| 186 } | 187 } |
| 187 | 188 |
| 189 get attributes => real.attributes; |
| 190 get bindings => real.bindings; |
| 188 | 191 |
| 189 get attributes => real.attributes; | 192 NodeBinding createBinding(String name, model, String path) { |
| 190 | |
| 191 void bind(String name, model, String path) { | |
| 192 switch (name) { | 193 switch (name) { |
| 193 case 'my-point': | 194 case 'my-point': |
| 194 unbind('my-point'); | |
| 195 attributes.remove('my-point'); | |
| 196 | |
| 197 _sub1 = new PathObserver(model, path).bindSync((v) { | |
| 198 myPoint = v; | |
| 199 }); | |
| 200 return; | |
| 201 case 'scary-monster': | 195 case 'scary-monster': |
| 202 unbind('scary-monster'); | 196 return new _MyCustomBinding(this, name, model, path); |
| 203 attributes.remove('scary-monster'); | |
| 204 | |
| 205 _sub2 = new PathObserver(model, path).bindSync((v) { | |
| 206 scaryMonster = v; | |
| 207 }); | |
| 208 return; | |
| 209 } | 197 } |
| 210 real.bind(name, model, path); | 198 return real.createBinding(name, model, path); |
| 211 } | 199 } |
| 212 | 200 |
| 213 void unbind(String name) { | 201 bind(String name, model, String path) => real.bind(name, model, path); |
| 214 switch (name) { | 202 void unbind(String name) => real.unbind(name); |
| 215 case 'my-point': | 203 void unbindAll() => real.unbindAll(); |
| 216 if (_sub1 != null) { | 204 } |
| 217 _sub1.cancel(); | 205 |
| 218 _sub1 = null; | 206 class _MyCustomBinding extends mdv.NodeBinding { |
| 219 } | 207 _MyCustomBinding(MyCustomElement node, property, model, path) |
| 220 return; | 208 : super(node, property, model, path) { |
| 221 case 'scary-monster': | 209 |
| 222 if (_sub2 != null) { | 210 node.attributes.remove(property); |
| 223 _sub2.cancel(); | |
| 224 _sub2 = null; | |
| 225 } | |
| 226 return; | |
| 227 } | |
| 228 real.unbind(name); | |
| 229 } | 211 } |
| 230 | 212 |
| 231 void unbindAll() { | 213 MyCustomElement get node => super.node; |
| 232 unbind('my-point'); | 214 |
| 233 unbind('scary-monster'); | 215 void boundValueChanged(newValue) { |
| 234 real.unbindAll(); | 216 if (property == 'my-point') node.myPoint = newValue; |
| 217 if (property == 'scary-monster') node.scaryMonster = newValue; |
| 235 } | 218 } |
| 236 } | 219 } |
| 237 | 220 |
| 221 |
| 238 /** | 222 /** |
| 239 * Demonstrates a custom element can override attributes []= and remove. | 223 * Demonstrates a custom element can override attributes []= and remove. |
| 240 * and see changes that the data binding system is making to the attributes. | 224 * and see changes that the data binding system is making to the attributes. |
| 241 */ | 225 */ |
| 242 class WithAttrsCustomElement implements Element { | 226 class WithAttrsCustomElement implements Element { |
| 243 final Element real; | 227 final Element real; |
| 244 final AttributeMapWrapper<String, String> attributes; | 228 final AttributeMapWrapper<String, String> attributes; |
| 245 | 229 |
| 246 factory WithAttrsCustomElement() { | 230 factory WithAttrsCustomElement() { |
| 247 var real = new Element.tag('with-attrs-custom-element'); | 231 var real = new Element.tag('with-attrs-custom-element'); |
| 248 var attributes = new AttributeMapWrapper(real.attributes); | 232 var attributes = new AttributeMapWrapper(real.attributes); |
| 249 return new WithAttrsCustomElement._(real, attributes); | 233 return new WithAttrsCustomElement._(real, attributes); |
| 250 } | 234 } |
| 251 | 235 |
| 252 WithAttrsCustomElement._(this.real, this.attributes) { | 236 WithAttrsCustomElement._(this.real, this.attributes) { |
| 253 real.xtag = this; | 237 real.xtag = this; |
| 254 } | 238 } |
| 255 | 239 |
| 256 void bind(String name, model, String path) => real.bind(name, model, path); | 240 createBinding(String name, model, String path) => |
| 241 real.createBinding(name, model, path); |
| 242 bind(String name, model, String path) => real.bind(name, model, path); |
| 257 void unbind(String name) => real.unbind(name); | 243 void unbind(String name) => real.unbind(name); |
| 258 void unbindAll() => real.unbindAll(); | 244 void unbindAll() => real.unbindAll(); |
| 259 } | 245 } |
| 260 | 246 |
| 261 // TODO(jmesserly): would be nice to use mocks when mirrors work on dart2js. | 247 // TODO(jmesserly): would be nice to use mocks when mirrors work on dart2js. |
| 262 class AttributeMapWrapper<K, V> implements Map<K, V> { | 248 class AttributeMapWrapper<K, V> implements Map<K, V> { |
| 263 final List log = []; | 249 final List log = []; |
| 264 Map<K, V> _map; | 250 Map<K, V> _map; |
| 265 | 251 |
| 266 AttributeMapWrapper(this._map); | 252 AttributeMapWrapper(this._map); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 282 } | 268 } |
| 283 | 269 |
| 284 void clear() => _map.clear(); | 270 void clear() => _map.clear(); |
| 285 void forEach(void f(K key, V value)) => _map.forEach(f); | 271 void forEach(void f(K key, V value)) => _map.forEach(f); |
| 286 Iterable<K> get keys => _map.keys; | 272 Iterable<K> get keys => _map.keys; |
| 287 Iterable<V> get values => _map.values; | 273 Iterable<V> get values => _map.values; |
| 288 int get length => _map.length; | 274 int get length => _map.length; |
| 289 bool get isEmpty => _map.isEmpty; | 275 bool get isEmpty => _map.isEmpty; |
| 290 bool get isNotEmpty => _map.isNotEmpty; | 276 bool get isNotEmpty => _map.isNotEmpty; |
| 291 } | 277 } |
| OLD | NEW |