Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(95)

Side by Side Diff: pkg/template_binding/lib/src/input_bindings.dart

Issue 52553007: fix selected index binding to not count microtasks (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 part of template_binding; 5 part of template_binding;
6 6
7 abstract class _InputBinding extends NodeBinding { 7 abstract class _InputBinding extends NodeBinding {
8 StreamSubscription _eventSub; 8 StreamSubscription _eventSub;
9 9
10 _InputBinding(node, name, model, path): super(node, name, model, path) { 10 _InputBinding(node, name, model, path): super(node, name, model, path) {
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
121 if (treeScope == null) return const []; 121 if (treeScope == null) return const [];
122 122
123 var radios = treeScope.querySelectorAll( 123 var radios = treeScope.querySelectorAll(
124 'input[type="radio"][name="${element.name}"]'); 124 'input[type="radio"][name="${element.name}"]');
125 return radios.where((el) => el != element && el.form == null); 125 return radios.where((el) => el != element && el.form == null);
126 } 126 }
127 } 127 }
128 } 128 }
129 129
130 class _SelectBinding extends _InputBinding { 130 class _SelectBinding extends _InputBinding {
131 MutationObserver _onMutation;
132
131 _SelectBinding(node, property, model, path) 133 _SelectBinding(node, property, model, path)
132 : super(node, property, model, path); 134 : super(node, property, model, path);
133 135
134 SelectElement get node => super.node; 136 SelectElement get node => super.node;
135 137
136 void valueChanged(newValue) { 138 void valueChanged(newValue) {
139 _cancelMutationObserver();
140
137 if (_tryUpdateValue(newValue)) return; 141 if (_tryUpdateValue(newValue)) return;
138 142
139 // The binding may wish to bind to an <option> which has not yet been 143 // It could be that a template will expand an <option> child (or grandchild,
140 // produced by a child <template>. Furthermore, we may need to wait for 144 // if we have an <optgroup> in between). Since selected index cannot be set
141 // <optgroup> iterating and then for <option>. 145 // if the children aren't created yet, we need to wait for them to be
142 // 146 // created do this with a MutationObserver.
143 // Unlike the JavaScript implemenation, we don't have a special 147 // Dart note: unlike JS we use mutation observers to avoid:
144 // "Object.observe" event loop to schedule on. 148 // https://github.com/Polymer/NodeBind/issues/5
145 // (See the the "ensureScheduled" function:
146 // https://github.com/Polymer/mdv/commit/9a51ad7ed74a292bf71662cea28acbd151f f65c8)
147 //
148 // Instead we use scheduleMicrotask. Each <template repeat> needs a delay of
149 // 2:
150 // * once to happen after the child _TemplateIterator is created
151 // * once to be after _TemplateIterator's CompoundPathObserver resolve
152 // And then we need to do this delay sequence twice:
153 // * once for OPTGROUP
154 // * once for OPTION.
155 // The resulting 2 * 2 is our maxRetries.
156 //
157 // TODO(jmesserly): a much better approach would be to find the nested
158 // <template> and wait on some future that completes when it has expanded.
159 var maxRetries = 4;
Siggi Cherem (dart-lang) 2013/11/04 20:49:23 yay!
160 delaySetSelectedIndex() {
161 if (!_tryUpdateValue(newValue) && maxRetries-- > 0) {
162 scheduleMicrotask(delaySetSelectedIndex);
163 }
164 }
165 149
166 scheduleMicrotask(delaySetSelectedIndex); 150 // Note: it doesn't matter when the children get added; even if they get
151 // added much later, presumably we want the selected index data binding to
152 // still take effect.
153 _onMutation = new MutationObserver((x, y) {
154 if (_tryUpdateValue(value)) _cancelMutationObserver();
155 })..observe(node, childList: true, subtree: true);
167 } 156 }
168 157
169 bool _tryUpdateValue(newValue) { 158 bool _tryUpdateValue(newValue) {
170 if (property == 'selectedIndex') { 159 if (property == 'selectedIndex') {
171 var intValue = _toInt(newValue); 160 var intValue = _toInt(newValue);
172 node.selectedIndex = intValue; 161 node.selectedIndex = intValue;
173 return node.selectedIndex == intValue; 162 return node.selectedIndex == intValue;
174 } else if (property == 'value') { 163 } else if (property == 'value') {
175 node.value = sanitizeBoundValue(newValue); 164 node.value = sanitizeBoundValue(newValue);
176 return node.value == newValue; 165 return node.value == newValue;
177 } 166 }
178 } 167 }
179 168
169 void _cancelMutationObserver() {
170 if (_onMutation != null) {
171 _onMutation.disconnect();
172 _onMutation = null;
173 }
174 }
175
180 void nodeValueChanged(e) { 176 void nodeValueChanged(e) {
177 _cancelMutationObserver();
178
181 if (property == 'selectedIndex') { 179 if (property == 'selectedIndex') {
182 value = node.selectedIndex; 180 value = node.selectedIndex;
183 } else if (property == 'value') { 181 } else if (property == 'value') {
184 value = node.value; 182 value = node.value;
185 } 183 }
186 } 184 }
187 185
188 // TODO(jmesserly,sigmund): I wonder how many bindings typically convert from 186 // TODO(jmesserly,sigmund): I wonder how many bindings typically convert from
189 // one type to another (e.g. value-as-number) and whether it is useful to 187 // one type to another (e.g. value-as-number) and whether it is useful to
190 // have something like a int/num binding converter (either as a base class or 188 // have something like a int/num binding converter (either as a base class or
191 // a wrapper). 189 // a wrapper).
192 static int _toInt(value) { 190 static int _toInt(value) {
193 if (value is String) return int.parse(value, onError: (_) => 0); 191 if (value is String) return int.parse(value, onError: (_) => 0);
194 return value is int ? value : 0; 192 return value is int ? value : 0;
195 } 193 }
196 } 194 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698