| 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 part of observe; |
| 6 |
| 7 /** The callback used in the [CompoundBinding.combinator] field. */ |
| 8 typedef Object CompoundBindingCombinator(Map objects); |
| 9 |
| 10 /** |
| 11 * Model-Driven Views contains a helper object which is useful for the |
| 12 * implementation of a Custom Syntax. |
| 13 * |
| 14 * var binding = new CompoundBinding((values) { |
| 15 * var combinedValue; |
| 16 * // compute combinedValue based on the current values which are provided |
| 17 * return combinedValue; |
| 18 * }); |
| 19 * binding.bind('name1', obj1, path1); |
| 20 * binding.bind('name2', obj2, path2); |
| 21 * //... |
| 22 * binding.bind('nameN', objN, pathN); |
| 23 * |
| 24 * CompoundBinding is an object which knows how to listen to multiple path |
| 25 * values (registered via [bind]) and invoke its [combinator] when one or more |
| 26 * of the values have changed and set its [value] property to the return value |
| 27 * of the function. When any value has changed, all current values are provided |
| 28 * to the [combinator] in the single `values` argument. |
| 29 */ |
| 30 // TODO(jmesserly): rename to something that indicates it's a computed value? |
| 31 class CompoundBinding extends ObservableBase { |
| 32 CompoundBindingCombinator _combinator; |
| 33 |
| 34 // TODO(jmesserly): ideally these would be String keys, but sometimes we |
| 35 // use integers. |
| 36 Map<dynamic, StreamSubscription> _bindings = new Map(); |
| 37 Map _values = new Map(); |
| 38 bool _scheduled = false; |
| 39 bool _disposed = false; |
| 40 Object _value; |
| 41 |
| 42 CompoundBinding([CompoundBindingCombinator combinator]) { |
| 43 // TODO(jmesserly): this is a tweak to the original code, it seemed to me |
| 44 // that passing the combinator to the constructor should be equivalent to |
| 45 // setting it via the property. |
| 46 // I also added a null check to the combinator setter. |
| 47 this.combinator = combinator; |
| 48 } |
| 49 |
| 50 CompoundBindingCombinator get combinator => _combinator; |
| 51 |
| 52 set combinator(CompoundBindingCombinator combinator) { |
| 53 _combinator = combinator; |
| 54 if (combinator != null) _scheduleResolve(); |
| 55 } |
| 56 |
| 57 static const _VALUE = const Symbol('value'); |
| 58 |
| 59 get value => _value; |
| 60 |
| 61 void set value(newValue) { |
| 62 _value = notifyPropertyChange(_VALUE, _value, newValue); |
| 63 } |
| 64 |
| 65 void bind(name, model, String path) { |
| 66 unbind(name); |
| 67 |
| 68 _bindings[name] = new PathObserver(model, path).bindSync((value) { |
| 69 _values[name] = value; |
| 70 _scheduleResolve(); |
| 71 }); |
| 72 } |
| 73 |
| 74 void unbind(name, {bool suppressResolve: false}) { |
| 75 var binding = _bindings.remove(name); |
| 76 if (binding == null) return; |
| 77 |
| 78 binding.cancel(); |
| 79 _values.remove(name); |
| 80 if (!suppressResolve) _scheduleResolve(); |
| 81 } |
| 82 |
| 83 // TODO(rafaelw): Is this the right processing model? |
| 84 // TODO(rafaelw): Consider having a seperate ChangeSummary for |
| 85 // CompoundBindings so to excess dirtyChecks. |
| 86 void _scheduleResolve() { |
| 87 if (_scheduled) return; |
| 88 _scheduled = true; |
| 89 queueChangeRecords(resolve); |
| 90 } |
| 91 |
| 92 void resolve() { |
| 93 if (_disposed) return; |
| 94 _scheduled = false; |
| 95 |
| 96 if (_combinator == null) { |
| 97 throw new StateError( |
| 98 'CompoundBinding attempted to resolve without a combinator'); |
| 99 } |
| 100 |
| 101 value = _combinator(_values); |
| 102 } |
| 103 |
| 104 void dispose() { |
| 105 for (var binding in _bindings.values) { |
| 106 binding.cancel(); |
| 107 } |
| 108 _bindings.clear(); |
| 109 _values.clear(); |
| 110 |
| 111 _disposed = true; |
| 112 value = null; |
| 113 } |
| 114 } |
| OLD | NEW |