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 /** |
11 * Model-Driven Views contains a helper object which is useful for the | 11 * CompoundBinding is an object which knows how to listen to multiple path |
12 * implementation of a Custom Syntax. | 12 * values (registered via [bind]) and invoke its [combinator] when one or more |
| 13 * of the values have changed and set its [value] property to the return value |
| 14 * of the function. When any value has changed, all current values are provided |
| 15 * to the [combinator] in the single `values` argument. |
| 16 * |
| 17 * For example: |
13 * | 18 * |
14 * var binding = new CompoundBinding((values) { | 19 * var binding = new CompoundBinding((values) { |
15 * var combinedValue; | 20 * var combinedValue; |
16 * // compute combinedValue based on the current values which are provided | 21 * // compute combinedValue based on the current values which are provided |
17 * return combinedValue; | 22 * return combinedValue; |
18 * }); | 23 * }); |
19 * binding.bind('name1', obj1, path1); | 24 * binding.bind('name1', obj1, path1); |
20 * binding.bind('name2', obj2, path2); | 25 * binding.bind('name2', obj2, path2); |
21 * //... | 26 * //... |
22 * binding.bind('nameN', objN, pathN); | 27 * 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 */ | 28 */ |
30 // 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? |
31 class CompoundBinding extends ObservableBase { | 30 class CompoundBinding extends ChangeNotifierBase { |
32 CompoundBindingCombinator _combinator; | 31 CompoundBindingCombinator _combinator; |
33 | 32 |
34 // TODO(jmesserly): ideally these would be String keys, but sometimes we | 33 // TODO(jmesserly): ideally these would be String keys, but sometimes we |
35 // use integers. | 34 // use integers. |
36 Map<dynamic, StreamSubscription> _bindings = new Map(); | 35 Map<dynamic, StreamSubscription> _bindings = new Map(); |
37 Map _values = new Map(); | 36 Map _values = new Map(); |
38 bool _scheduled = false; | 37 bool _scheduled = false; |
39 bool _disposed = false; | 38 bool _disposed = false; |
40 Object _value; | 39 Object _value; |
41 | 40 |
(...skipping 16 matching lines...) Expand all Loading... |
58 | 57 |
59 get value => _value; | 58 get value => _value; |
60 | 59 |
61 void set value(newValue) { | 60 void set value(newValue) { |
62 _value = notifyPropertyChange(_VALUE, _value, newValue); | 61 _value = notifyPropertyChange(_VALUE, _value, newValue); |
63 } | 62 } |
64 | 63 |
65 void bind(name, model, String path) { | 64 void bind(name, model, String path) { |
66 unbind(name); | 65 unbind(name); |
67 | 66 |
| 67 // TODO(jmesserly): should we avoid observing until we are observed, |
| 68 // similar to PathObserver? Similar for unobserving? |
68 _bindings[name] = new PathObserver(model, path).bindSync((value) { | 69 _bindings[name] = new PathObserver(model, path).bindSync((value) { |
69 _values[name] = value; | 70 _values[name] = value; |
70 _scheduleResolve(); | 71 _scheduleResolve(); |
71 }); | 72 }); |
72 } | 73 } |
73 | 74 |
74 void unbind(name, {bool suppressResolve: false}) { | 75 void unbind(name, {bool suppressResolve: false}) { |
75 var binding = _bindings.remove(name); | 76 var binding = _bindings.remove(name); |
76 if (binding == null) return; | 77 if (binding == null) return; |
77 | 78 |
78 binding.cancel(); | 79 binding.cancel(); |
79 _values.remove(name); | 80 _values.remove(name); |
80 if (!suppressResolve) _scheduleResolve(); | 81 if (!suppressResolve) _scheduleResolve(); |
81 } | 82 } |
82 | 83 |
83 // TODO(rafaelw): Is this the right processing model? | 84 // TODO(rafaelw): Is this the right processing model? |
84 // TODO(rafaelw): Consider having a seperate ChangeSummary for | 85 // TODO(rafaelw): Consider having a seperate ChangeSummary for |
85 // CompoundBindings so to excess dirtyChecks. | 86 // CompoundBindings so to excess dirtyChecks. |
86 void _scheduleResolve() { | 87 void _scheduleResolve() { |
87 if (_scheduled) return; | 88 if (_scheduled) return; |
88 _scheduled = true; | 89 _scheduled = true; |
89 queueChangeRecords(resolve); | 90 runAsync(resolve); |
90 } | 91 } |
91 | 92 |
92 void resolve() { | 93 void resolve() { |
93 if (_disposed) return; | 94 if (_disposed) return; |
94 _scheduled = false; | 95 _scheduled = false; |
95 | 96 |
96 if (_combinator == null) { | 97 if (_combinator == null) { |
97 throw new StateError( | 98 throw new StateError( |
98 'CompoundBinding attempted to resolve without a combinator'); | 99 'CompoundBinding attempted to resolve without a combinator'); |
99 } | 100 } |
100 | 101 |
101 value = _combinator(_values); | 102 value = _combinator(_values); |
102 } | 103 } |
103 | 104 |
104 void dispose() { | 105 void dispose() { |
105 for (var binding in _bindings.values) { | 106 for (var binding in _bindings.values) { |
106 binding.cancel(); | 107 binding.cancel(); |
107 } | 108 } |
108 _bindings.clear(); | 109 _bindings.clear(); |
109 _values.clear(); | 110 _values.clear(); |
110 | 111 |
111 _disposed = true; | 112 _disposed = true; |
112 value = null; | 113 value = null; |
113 } | 114 } |
114 } | 115 } |
OLD | NEW |