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

Side by Side Diff: pkg/mdv/lib/src/template_iterator.dart

Issue 19492018: [mdv] Avoid observing placeholder arrays in TemplateIterator (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 5 months 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 mdv; 5 part of mdv;
6 6
7 // This code is a port of Model-Driven-Views: 7 // This code is a port of Model-Driven-Views:
8 // https://github.com/polymer-project/mdv 8 // https://github.com/polymer-project/mdv
9 // The code mostly comes from src/template_element.js 9 // The code mostly comes from src/template_element.js
10 10
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after
172 final bool isBinding; 172 final bool isBinding;
173 173
174 _BindingToken(this.value, {this.isBinding: false}); 174 _BindingToken(this.value, {this.isBinding: false});
175 175
176 bool get isText => !isBinding; 176 bool get isText => !isBinding;
177 } 177 }
178 178
179 class _TemplateIterator { 179 class _TemplateIterator {
180 final Element _templateElement; 180 final Element _templateElement;
181 final List<Node> terminators = []; 181 final List<Node> terminators = [];
182 final CompoundBinding inputs; 182 CompoundBinding inputs;
183 List iteratedValue; 183 List iteratedValue;
184 Object _lastValue; 184 bool closed = false;
185 185
186 StreamSubscription _sub; 186 StreamSubscription _sub;
187 StreamSubscription _valueBinding;
188 187
189 _TemplateIterator(this._templateElement) 188 _TemplateIterator(this._templateElement) {
190 : inputs = new CompoundBinding(resolveInputs) { 189 inputs = new CompoundBinding(resolveInputs);
justinfagnani 2013/07/24 00:42:55 why remove this from the initializer list, or make
Jennifer Messerly 2013/07/24 00:52:58 it's sneaky: resolveInputs is no longer static, so
191
192 _valueBinding = new PathObserver(inputs, 'value').bindSync(valueChanged);
193 } 190 }
194 191
195 static Object resolveInputs(Map values) { 192 resolveInputs(Map values) {
justinfagnani 2013/07/24 00:42:55 void?
Jennifer Messerly 2013/07/24 00:52:58 Done.
Jennifer Messerly 2013/07/24 01:06:55 update: it actually can't be void... CompoundBindi
193 if (closed) return;
194
196 if (values.containsKey('if') && !_toBoolean(values['if'])) { 195 if (values.containsKey('if') && !_toBoolean(values['if'])) {
197 return null; 196 valueChanged(null);
197 } else if (values.containsKey('repeat')) {
198 valueChanged(values['repeat']);
199 } else if (values.containsKey('bind')) {
200 valueChanged([values['bind']]);
201 } else {
202 valueChanged(null);
198 } 203 }
199
200 if (values.containsKey('repeat')) {
201 return values['repeat'];
202 }
203
204 if (values.containsKey('bind')) {
205 return [values['bind']];
206 }
207
208 return null;
209 } 204 }
210 205
211 void valueChanged(value) { 206 void valueChanged(value) {
212 // TODO(jmesserly): should PathObserver do this for us? 207 if (value is! List) value = null;
justinfagnani 2013/07/24 00:42:55 when do we want to use Iterable over List?
Jennifer Messerly 2013/07/24 00:52:58 good question. I think we should follow up with MD
213 var oldValue = _lastValue;
214 _lastValue = value;
215 208
216 if (value is! List) { 209 var oldValue = iteratedValue;
217 value = [];
218 }
219
220 unobserve(); 210 unobserve();
221 iteratedValue = value; 211 iteratedValue = value;
222 212
223 if (value is Observable) { 213 if (iteratedValue is Observable) {
224 _sub = value.changes.listen(_handleChanges); 214 _sub = iteratedValue.changes.listen(_handleChanges);
225 } 215 }
226 216
227 int addedCount = iteratedValue.length; 217 var splices = calculateSplices(
228 var removedCount = oldValue is List ? (oldValue as List).length : 0; 218 iteratedValue != null ? iteratedValue : [],
229 if (addedCount == 0 && removedCount == 0) return; // nothing to do. 219 oldValue != null ? oldValue : []);
230 220
231 _handleChanges([new ListChangeRecord(0, addedCount: addedCount, 221 if (splices.length > 0) _handleChanges(splices);
232 removedCount: removedCount)]); 222
223 if (inputs.length == 0) {
224 close();
225 _mdv(_templateElement)._templateIterator = null;
226 }
233 } 227 }
234 228
235 Node getTerminatorAt(int index) { 229 Node getTerminatorAt(int index) {
236 if (index == -1) return _templateElement; 230 if (index == -1) return _templateElement;
237 var terminator = terminators[index]; 231 var terminator = terminators[index];
238 if (terminator is Element && (terminator as Element).isTemplate && 232 if (terminator is Element && (terminator as Element).isTemplate &&
239 !identical(terminator, _templateElement)) { 233 !identical(terminator, _templateElement)) {
240 var subIterator = _mdv(terminator)._templateIterator; 234 var subIterator = _mdv(terminator)._templateIterator;
241 if (subIterator != null) { 235 if (subIterator != null) {
242 return subIterator.getTerminatorAt(subIterator.terminators.length - 1); 236 return subIterator.getTerminatorAt(subIterator.terminators.length - 1);
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
288 var instanceNodes = instanceCache.remove(model); 282 var instanceNodes = instanceCache.remove(model);
289 if (instanceNodes != null) return instanceNodes; 283 if (instanceNodes != null) return instanceNodes;
290 284
291 var fragment = _templateElement.createInstance(model, syntax); 285 var fragment = _templateElement.createInstance(model, syntax);
292 instanceNodes = fragment.nodes.toList(); 286 instanceNodes = fragment.nodes.toList();
293 fragment.nodes.clear(); 287 fragment.nodes.clear();
294 return instanceNodes; 288 return instanceNodes;
295 } 289 }
296 290
297 void _handleChanges(Iterable<ChangeRecord> splices) { 291 void _handleChanges(Iterable<ChangeRecord> splices) {
292 if (closed) return;
293
298 splices = splices.where((s) => s is ListChangeRecord); 294 splices = splices.where((s) => s is ListChangeRecord);
299 295
300 var template = _templateElement; 296 var template = _templateElement;
301 var syntax = template.attributes['syntax']; 297 var syntax = template.attributes['syntax'];
302 298
303 if (template.parentNode == null || template.document.window == null) { 299 if (template.parentNode == null || template.document.window == null) {
304 abandon(); 300 close();
305 // TODO(jmesserly): MDV calls templateIteratorTable.delete(this) here, 301 // TODO(jmesserly): MDV calls templateIteratorTable.delete(this) here,
306 // but I think that's a no-op because only nodes are used as keys. 302 // but I think that's a no-op because only nodes are used as keys.
307 // See https://github.com/Polymer/mdv/pull/114. 303 // See https://github.com/Polymer/mdv/pull/114.
308 return; 304 return;
309 } 305 }
310 306
311 // TODO(jmesserly): IdentityMap matches JS semantics, but it's O(N) right 307 // TODO(jmesserly): IdentityMap matches JS semantics, but it's O(N) right
312 // now. See http://dartbug.com/4161. 308 // now. See http://dartbug.com/4161.
313 var instanceCache = new IdentityMap(); 309 var instanceCache = new IdentityMap();
314 var removeDelta = 0; 310 var removeDelta = 0;
(...skipping 24 matching lines...) Expand all
339 instanceNodes.forEach(_unbindAllRecursively); 335 instanceNodes.forEach(_unbindAllRecursively);
340 } 336 }
341 } 337 }
342 338
343 void unobserve() { 339 void unobserve() {
344 if (_sub == null) return; 340 if (_sub == null) return;
345 _sub.cancel(); 341 _sub.cancel();
346 _sub = null; 342 _sub = null;
347 } 343 }
348 344
349 void abandon() { 345 void close() {
346 if (closed) return;
347
350 unobserve(); 348 unobserve();
351 _valueBinding.cancel();
352 terminators.clear(); 349 terminators.clear();
353 inputs.dispose(); 350 inputs.dispose();
351 closed = true;
354 } 352 }
355 353
356 static void _unbindAllRecursively(Node node) { 354 static void _unbindAllRecursively(Node node) {
357 var nodeExt = _mdv(node); 355 var nodeExt = _mdv(node);
358 nodeExt._templateInstance = null; 356 nodeExt._templateInstance = null;
359 if (node is Element && (node as Element).isTemplate) { 357 if (node is Element && (node as Element).isTemplate) {
360 // Make sure we stop observing when we remove an element. 358 // Make sure we stop observing when we remove an element.
361 var templateIterator = nodeExt._templateIterator; 359 var templateIterator = nodeExt._templateIterator;
362 if (templateIterator != null) { 360 if (templateIterator != null) {
363 templateIterator.abandon(); 361 templateIterator.close();
364 nodeExt._templateIterator = null; 362 nodeExt._templateIterator = null;
365 } 363 }
366 } 364 }
367 365
368 _nodeOrCustom(node).unbindAll(); 366 _nodeOrCustom(node).unbindAll();
369 for (var c = node.firstChild; c != null; c = c.nextNode) { 367 for (var c = node.firstChild; c != null; c = c.nextNode) {
370 _unbindAllRecursively(c); 368 _unbindAllRecursively(c);
371 } 369 }
372 } 370 }
373 } 371 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698