| 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 part of observe; | 5 part of observe; |
| 6 | 6 |
| 7 /** The callback used in the [CompoundBinding.combinator] field. */ | 7 /** The callback used in the [CompoundBinding.combinator] field. */ |
| 8 typedef Object CompoundBindingCombinator(Map objects); | 8 typedef Object CompoundBindingCombinator(Map objects); |
| 9 | 9 |
| 10 /** | 10 /** |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 * binding.bind('name2', obj2, path2); | 25 * binding.bind('name2', obj2, path2); |
| 26 * //... | 26 * //... |
| 27 * binding.bind('nameN', objN, pathN); | 27 * binding.bind('nameN', objN, pathN); |
| 28 */ | 28 */ |
| 29 // TODO(jmesserly): rename to something that indicates it's a computed value? | 29 // TODO(jmesserly): rename to something that indicates it's a computed value? |
| 30 class CompoundBinding extends ChangeNotifierBase { | 30 class CompoundBinding extends ChangeNotifierBase { |
| 31 CompoundBindingCombinator _combinator; | 31 CompoundBindingCombinator _combinator; |
| 32 | 32 |
| 33 // TODO(jmesserly): ideally these would be String keys, but sometimes we | 33 // TODO(jmesserly): ideally these would be String keys, but sometimes we |
| 34 // use integers. | 34 // use integers. |
| 35 Map<dynamic, StreamSubscription> _bindings = new Map(); | 35 Map<dynamic, StreamSubscription> _observers = new Map(); |
| 36 Map _values = new Map(); | 36 Map _values = new Map(); |
| 37 bool _scheduled = false; | 37 bool _scheduled = false; |
| 38 bool _disposed = false; | |
| 39 Object _value; | 38 Object _value; |
| 40 | 39 |
| 41 CompoundBinding([CompoundBindingCombinator combinator]) { | 40 CompoundBinding([CompoundBindingCombinator combinator]) { |
| 42 // TODO(jmesserly): this is a tweak to the original code, it seemed to me | 41 // TODO(jmesserly): this is a tweak to the original code, it seemed to me |
| 43 // that passing the combinator to the constructor should be equivalent to | 42 // that passing the combinator to the constructor should be equivalent to |
| 44 // setting it via the property. | 43 // setting it via the property. |
| 45 // I also added a null check to the combinator setter. | 44 // I also added a null check to the combinator setter. |
| 46 this.combinator = combinator; | 45 this.combinator = combinator; |
| 47 } | 46 } |
| 48 | 47 |
| 49 CompoundBindingCombinator get combinator => _combinator; | 48 CompoundBindingCombinator get combinator => _combinator; |
| 50 | 49 |
| 51 set combinator(CompoundBindingCombinator combinator) { | 50 set combinator(CompoundBindingCombinator combinator) { |
| 52 _combinator = combinator; | 51 _combinator = combinator; |
| 53 if (combinator != null) _scheduleResolve(); | 52 if (combinator != null) _scheduleResolve(); |
| 54 } | 53 } |
| 55 | 54 |
| 56 static const _VALUE = const Symbol('value'); | 55 static const _VALUE = const Symbol('value'); |
| 57 | 56 |
| 58 get value => _value; | 57 get value => _value; |
| 59 | 58 |
| 60 int get length => _bindings.length; | 59 int get length => _observers.length; |
| 61 | 60 |
| 62 void set value(newValue) { | 61 void set value(newValue) { |
| 63 _value = notifyPropertyChange(_VALUE, _value, newValue); | 62 _value = notifyPropertyChange(_VALUE, _value, newValue); |
| 64 } | 63 } |
| 65 | 64 |
| 66 void bind(name, model, String path) { | 65 void bind(name, model, String path) { |
| 67 unbind(name); | 66 unbind(name); |
| 68 | 67 |
| 69 // TODO(jmesserly): should we avoid observing until we are observed, | 68 // TODO(jmesserly): should we delay observing until we are observed, |
| 70 // similar to PathObserver? Similar for unobserving? | 69 // similar to PathObserver? |
| 71 _bindings[name] = new PathObserver(model, path).bindSync((value) { | 70 _observers[name] = new PathObserver(model, path).bindSync((value) { |
| 72 _values[name] = value; | 71 _values[name] = value; |
| 73 _scheduleResolve(); | 72 _scheduleResolve(); |
| 74 }); | 73 }); |
| 75 } | 74 } |
| 76 | 75 |
| 77 void unbind(name, {bool suppressResolve: false}) { | 76 void unbind(name, {bool suppressResolve: false}) { |
| 78 var binding = _bindings.remove(name); | 77 var binding = _observers.remove(name); |
| 79 if (binding == null) return; | 78 if (binding == null) return; |
| 80 | 79 |
| 81 binding.cancel(); | 80 binding.cancel(); |
| 82 _values.remove(name); | 81 _values.remove(name); |
| 83 if (!suppressResolve) _scheduleResolve(); | 82 if (!suppressResolve) _scheduleResolve(); |
| 84 } | 83 } |
| 85 | 84 |
| 86 // TODO(rafaelw): Is this the right processing model? | 85 // TODO(rafaelw): Is this the right processing model? |
| 87 // TODO(rafaelw): Consider having a seperate ChangeSummary for | 86 // TODO(rafaelw): Consider having a seperate ChangeSummary for |
| 88 // CompoundBindings so to excess dirtyChecks. | 87 // CompoundBindings so to excess dirtyChecks. |
| 89 void _scheduleResolve() { | 88 void _scheduleResolve() { |
| 90 if (_scheduled) return; | 89 if (_scheduled) return; |
| 91 _scheduled = true; | 90 _scheduled = true; |
| 92 runAsync(resolve); | 91 runAsync(resolve); |
| 93 } | 92 } |
| 94 | 93 |
| 95 void resolve() { | 94 void resolve() { |
| 96 if (_disposed) return; | 95 if (_observers.isEmpty) return; |
| 97 _scheduled = false; | 96 _scheduled = false; |
| 98 | 97 |
| 99 if (_combinator == null) { | 98 if (_combinator == null) { |
| 100 throw new StateError( | 99 throw new StateError( |
| 101 'CompoundBinding attempted to resolve without a combinator'); | 100 'CompoundBinding attempted to resolve without a combinator'); |
| 102 } | 101 } |
| 103 | 102 |
| 104 value = _combinator(_values); | 103 value = _combinator(_values); |
| 105 } | 104 } |
| 106 | 105 |
| 107 void dispose() { | 106 /** |
| 108 for (var binding in _bindings.values) { | 107 * Closes the observer. |
| 108 * |
| 109 * This happens automatically if the [value] property is no longer observed, |
| 110 * but this can also be called explicitly. |
| 111 */ |
| 112 void close() { |
| 113 for (var binding in _observers.values) { |
| 109 binding.cancel(); | 114 binding.cancel(); |
| 110 } | 115 } |
| 111 _bindings.clear(); | 116 _observers.clear(); |
| 112 _values.clear(); | 117 _values.clear(); |
| 113 | |
| 114 _disposed = true; | |
| 115 value = null; | 118 value = null; |
| 116 } | 119 } |
| 120 |
| 121 _unobserved() => close(); |
| 117 } | 122 } |
| OLD | NEW |