OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library template_binding.test.custom_element_bindings_test; | |
6 | |
7 import 'dart:async'; | |
8 import 'dart:html'; | |
9 import 'dart:collection' show MapView; | |
10 import 'package:template_binding/template_binding.dart'; | |
11 import 'package:observe/observe.dart'; | |
12 import 'package:unittest/html_config.dart'; | |
13 import 'package:unittest/unittest.dart'; | |
14 import 'package:web_components/polyfill.dart'; | |
15 import 'utils.dart'; | |
16 import 'package:observe/mirrors_used.dart'; // make test smaller | |
17 import 'package:smoke/mirrors.dart' as smoke; | |
18 | |
19 Future _registered; | |
20 | |
21 main() => dirtyCheckZone().run(() { | |
22 smoke.useMirrors(); | |
23 useHtmlConfiguration(); | |
24 | |
25 _registered = customElementsReady.then((_) { | |
26 document.registerElement('my-custom-element', MyCustomElement); | |
27 }); | |
28 | |
29 group('Custom Element Bindings', customElementBindingsTest); | |
30 }); | |
31 | |
32 customElementBindingsTest() { | |
33 setUp(() { | |
34 document.body.append(testDiv = new DivElement()); | |
35 return _registered; | |
36 }); | |
37 | |
38 tearDown(() { | |
39 testDiv.remove(); | |
40 testDiv = null; | |
41 }); | |
42 | |
43 test('override bind/bindFinished', () { | |
44 var element = new MyCustomElement(); | |
45 var model = toObservable({'a': new Point(123, 444), 'b': new Monster(100)}); | |
46 | |
47 var pointBinding = nodeBind(element) | |
48 .bind('my-point', new PathObserver(model, 'a')); | |
49 | |
50 var scaryBinding = nodeBind(element) | |
51 .bind('scary-monster', new PathObserver(model, 'b')); | |
52 | |
53 expect(element.attributes, isNot(contains('my-point'))); | |
54 expect(element.attributes, isNot(contains('scary-monster'))); | |
55 | |
56 expect(element.myPoint, model['a']); | |
57 expect(element.scaryMonster, model['b']); | |
58 | |
59 model['a'] = null; | |
60 return new Future(() { | |
61 expect(element.myPoint, null); | |
62 expect(element.bindFinishedCalled, 0); | |
63 pointBinding.close(); | |
64 | |
65 model['a'] = new Point(1, 2); | |
66 model['b'] = new Monster(200); | |
67 }).then(endOfMicrotask).then((_) { | |
68 expect(element.scaryMonster, model['b']); | |
69 expect(element.myPoint, null, reason: 'a was unbound'); | |
70 | |
71 scaryBinding.close(); | |
72 model['b'] = null; | |
73 }).then(endOfMicrotask).then((_) { | |
74 expect(element.scaryMonster.health, 200); | |
75 expect(element.bindFinishedCalled, 0); | |
76 }); | |
77 }); | |
78 | |
79 test('template bind uses overridden custom element bind', () { | |
80 | |
81 var model = toObservable({'a': new Point(123, 444), 'b': new Monster(100)}); | |
82 var div = createTestHtml('<template bind>' | |
83 '<my-custom-element my-point="{{a}}" scary-monster="{{b}}">' | |
84 '</my-custom-element>' | |
85 '</template>'); | |
86 | |
87 templateBind(div.query('template')).model = model; | |
88 var element; | |
89 return new Future(() { | |
90 element = div.nodes[1]; | |
91 | |
92 expect(element is MyCustomElement, true, | |
93 reason: '$element should be a MyCustomElement'); | |
94 | |
95 expect(element.myPoint, model['a']); | |
96 expect(element.scaryMonster, model['b']); | |
97 | |
98 expect(element.attributes, isNot(contains('my-point'))); | |
99 expect(element.attributes, isNot(contains('scary-monster'))); | |
100 | |
101 expect(element.bindFinishedCalled, 1); | |
102 | |
103 model['a'] = null; | |
104 }).then(endOfMicrotask).then((_) { | |
105 expect(element.myPoint, null); | |
106 expect(element.bindFinishedCalled, 1); | |
107 | |
108 | |
109 templateBind(div.query('template')).model = null; | |
110 }).then(endOfMicrotask).then((_) { | |
111 // Note: the detached element | |
112 expect(element.parentNode is DocumentFragment, true, | |
113 reason: 'removed element is added back to its document fragment'); | |
114 expect(element.parentNode.parentNode, null, | |
115 reason: 'document fragment is detached'); | |
116 expect(element.bindFinishedCalled, 1); | |
117 | |
118 model['a'] = new Point(1, 2); | |
119 model['b'] = new Monster(200); | |
120 }).then(endOfMicrotask).then((_) { | |
121 expect(element.myPoint, null, reason: 'model was unbound'); | |
122 expect(element.scaryMonster.health, 100, reason: 'model was unbound'); | |
123 expect(element.bindFinishedCalled, 1); | |
124 }); | |
125 }); | |
126 | |
127 } | |
128 | |
129 class Monster { | |
130 int health; | |
131 Monster(this.health); | |
132 } | |
133 | |
134 /** Demonstrates a custom element overriding bind/bindFinished. */ | |
135 class MyCustomElement extends HtmlElement implements NodeBindExtension { | |
136 Point myPoint; | |
137 Monster scaryMonster; | |
138 int bindFinishedCalled = 0; | |
139 | |
140 factory MyCustomElement() => new Element.tag('my-custom-element'); | |
141 | |
142 MyCustomElement.created() : super.created(); | |
143 | |
144 Bindable bind(String name, value, {oneTime: false}) { | |
145 switch (name) { | |
146 case 'my-point': | |
147 case 'scary-monster': | |
148 attributes.remove(name); | |
149 if (oneTime) { | |
150 _setProperty(name, value); | |
151 return null; | |
152 } | |
153 _setProperty(name, value.open((x) => _setProperty(name, x))); | |
154 | |
155 if (!enableBindingsReflection) return value; | |
156 if (bindings == null) bindings = {}; | |
157 var old = bindings[name]; | |
158 if (old != null) old.close(); | |
159 return bindings[name] = value; | |
160 } | |
161 return nodeBindFallback(this).bind(name, value, oneTime: oneTime); | |
162 } | |
163 | |
164 void bindFinished() { | |
165 bindFinishedCalled++; | |
166 } | |
167 | |
168 get bindings => nodeBindFallback(this).bindings; | |
169 set bindings(x) => nodeBindFallback(this).bindings = x; | |
170 get templateInstance => nodeBindFallback(this).templateInstance; | |
171 | |
172 void _setProperty(String property, newValue) { | |
173 if (property == 'my-point') myPoint = newValue; | |
174 if (property == 'scary-monster') scaryMonster = newValue; | |
175 } | |
176 } | |
177 | |
OLD | NEW |