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

Side by Side Diff: lib/watcher.dart

Issue 12225039: Support for observable models, fixes #259 (Closed) Base URL: https://github.com/dart-lang/web-ui.git@master
Patch Set: Created 7 years, 10 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
« no previous file with comments | « lib/templating.dart ('k') | lib/web_ui.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 /** 5 /**
6 * A library to observe changes on Dart objects. 6 * A library to observe changes on Dart objects.
7 * 7 *
8 * Similar to the principle of watchers in AngularJS, this library provides the 8 * Similar to the principle of watchers in AngularJS, this library provides the
9 * mechanisms to observe and react to changes that happen in an application's 9 * mechanisms to observe and react to changes that happen in an application's
10 * data model. 10 * data model.
(...skipping 30 matching lines...) Expand all
41 * more details. 41 * more details.
42 * 42 *
43 * A common design pattern for MVC applications is to call [dispatch] at the end 43 * A common design pattern for MVC applications is to call [dispatch] at the end
44 * of each event loop (e.g. after each UI event is fired). Our view library does 44 * of each event loop (e.g. after each UI event is fired). Our view library does
45 * this automatically. 45 * this automatically.
46 */ 46 */
47 library watcher; 47 library watcher;
48 48
49 import 'dart:async'; 49 import 'dart:async';
50 import 'dart:collection'; 50 import 'dart:collection';
51 import 'observe.dart';
51 import 'src/linked_list.dart'; 52 import 'src/linked_list.dart';
52 53
53 /** 54 /**
55 * True to use the [observe] library instead of watchers.
56 *
57 * Observers require the [observable] annotation on objects and for collection
58 * types to be observable, such as [ObservableList]. But in return they offer
59 * better performance and more precise change tracking. [dispatch] is not
60 * required with observers, and changes to observable objects are always
61 * detected.
62 *
63 * Currently this flag is experimental, but it may be the default in the future.
64 */
65 bool useObservers = false;
66
67 /**
54 * Watch for changes in [target]. The [callback] function will be called when 68 * Watch for changes in [target]. The [callback] function will be called when
55 * [dispatch] is called and the value represented by [target] had changed. The 69 * [dispatch] is called and the value represented by [target] had changed. The
56 * returned function can be used to unregister this watcher. 70 * returned function can be used to unregister this watcher.
57 * 71 *
58 * There are several values you can use for [target]: 72 * There are several values you can use for [target]:
59 * 73 *
60 * * A [Getter] function. 74 * * A [Getter] function.
61 * Use this to watch expressions as they change. For instance, to watch 75 * Use this to watch expressions as they change. For instance, to watch
62 * whether `a.b.c` changes, wrap it in a getter and call [watch] as follows: 76 * whether `a.b.c` changes, wrap it in a getter and call [watch] as follows:
63 * watch(() => a.b.c, ...) 77 * watch(() => a.b.c, ...)
64 * These targets are tracked to check for equality. If calling `target()` 78 * These targets are tracked to check for equality. If calling `target()`
65 * returns the same result, then the [callback] will not be invoked. In the 79 * returns the same result, then the [callback] will not be invoked. In the
66 * special case whe the getter returns a [List], we will treat the value in a 80 * special case whe the getter returns a [List], we will treat the value in a
67 * special way, similar to passing [List] directly as [target]. 81 * special way, similar to passing [List] directly as [target].
68 * **Important**: this library assumes that [Getter] is a read-only function 82 * **Important**: this library assumes that [Getter] is a read-only function
69 * and that it will consistently return the same value if called multiple 83 * and that it will consistently return the same value if called multiple
70 * times in a row. 84 * times in a row.
71 * 85 *
72 * * A [List]. 86 * * A [List].
73 * Use this to watch whether a list object changes. For instance, to detect 87 * Use this to watch whether a list object changes. For instance, to detect
74 * if an element is added or changed in a list can call [watch] as follows: 88 * if an element is added or changed in a list can call [watch] as follows:
75 * watch(list, ...) 89 * watch(list, ...)
76 * 90 *
77 * * A [Handle]. 91 * * A [Handle].
78 * This is syntactic sugar for using the getter portion of a [Handle]. 92 * This is syntactic sugar for using the getter portion of a [Handle].
79 * watch(handle, ...) // equivalent to `watch(handle._getter, ...)` 93 * watch(handle, ...) // equivalent to `watch(handle._getter, ...)`
80 */ 94 */
81 WatcherDisposer watch(var target, ValueWatcher callback, [String debugName]) { 95 ChangeUnobserver watch(target, ChangeObserver callback, [String debugName]) {
96 if (useObservers) return observe(target, callback);
97
82 if (callback == null) return () {}; // no use in passing null as a callback. 98 if (callback == null) return () {}; // no use in passing null as a callback.
83 if (_watchers == null) _watchers = new LinkedList<_Watcher>(); 99 if (_watchers == null) _watchers = new LinkedList<_Watcher>();
84 Function exp; 100 Function exp;
85 bool isList = false; 101 bool isList = false;
86 if (target is Handle) { 102 if (target is Handle) {
87 exp = (target as Handle)._getter; 103 exp = (target as Handle)._getter;
88 } else if (target is Function) { 104 } else if (target is Function) {
89 exp = target; 105 exp = target;
90 try { 106 try {
91 var val = target(); 107 var val = target();
(...skipping 21 matching lines...) Expand all
113 : new _Watcher(exp, callback, debugName); 129 : new _Watcher(exp, callback, debugName);
114 var node = _watchers.add(watcher); 130 var node = _watchers.add(watcher);
115 return node.remove; 131 return node.remove;
116 } 132 }
117 133
118 /** 134 /**
119 * Add a watcher for [exp] and immediatly invoke [callback]. The watch event 135 * Add a watcher for [exp] and immediatly invoke [callback]. The watch event
120 * passed to [callback] will have `null` as the old value, and the current 136 * passed to [callback] will have `null` as the old value, and the current
121 * evaluation of [exp] as the new value. 137 * evaluation of [exp] as the new value.
122 */ 138 */
123 WatcherDisposer watchAndInvoke(exp, callback, [debugName]) { 139 ChangeUnobserver watchAndInvoke(exp, callback, [debugName]) {
124 var res = watch(exp, callback, debugName); 140 var res = watch(exp, callback, debugName);
125 // TODO(jmesserly): this should be "is Getter" once dart2js bug is fixed. 141 // TODO(jmesserly): this should be "is Getter" once dart2js bug is fixed.
126 if (exp is Function) { 142 if (exp is Function) {
127 callback(new WatchEvent(null, exp())); 143 callback(new ChangeNotification(null, exp()));
128 } else { 144 } else {
129 callback(new WatchEvent(null, exp)); 145 callback(new ChangeNotification(null, exp));
130 } 146 }
131 return res; 147 return res;
132 } 148 }
133 149
134 /** Callback fired when an expression changes. */
135 typedef void ValueWatcher(WatchEvent e);
136
137 /** A function that unregisters a watcher. */
138 typedef void WatcherDisposer();
139
140 /** Event passed to [ValueMatcher] showing what changed. */
141 class WatchEvent {
142
143 /** Previous value seen on the watched expression. */
144 final oldValue;
145
146 /** New value seen on the watched expression. */
147 final newValue;
148
149 WatchEvent(this.oldValue, this.newValue);
150 }
151
152 /** Internal set of active watchers. */ 150 /** Internal set of active watchers. */
153 LinkedList<_Watcher> _watchers; 151 LinkedList<_Watcher> _watchers;
154 152
155 /** 153 /**
156 * An internal representation of a watcher. Contains the expression it watches, 154 * An internal representation of a watcher. Contains the expression it watches,
157 * the last value seen for it, and a callback to invoke when a change is 155 * the last value seen for it, and a callback to invoke when a change is
158 * detected. 156 * detected.
159 */ 157 */
160 class _Watcher { 158 class _Watcher {
161 /** Name used to debug. */ 159 /** Name used to debug. */
162 final String debugName; 160 final String debugName;
163 161
164 /** Function that retrieves the value being watched. */ 162 /** Function that retrieves the value being watched. */
165 final Getter _getter; 163 final Getter _getter;
166 164
167 /** Callback to invoke when the value changes. */ 165 /** Callback to invoke when the value changes. */
168 final ValueWatcher _callback; 166 final ChangeObserver _callback;
169 167
170 /** Last value observed on the matched expression. */ 168 /** Last value observed on the matched expression. */
171 var _lastValue; 169 var _lastValue;
172 170
173 _Watcher(this._getter, this._callback, this.debugName) { 171 _Watcher(this._getter, this._callback, this.debugName) {
174 _lastValue = _getter(); 172 _lastValue = _getter();
175 } 173 }
176 174
177 String toString() => debugName == null ? '<unnamed>' : debugName; 175 String toString() => debugName == null ? '<unnamed>' : debugName;
178 176
179 /** Detect if any changes occurred and if so invoke [_callback]. */ 177 /** Detect if any changes occurred and if so invoke [_callback]. */
180 bool compareAndNotify() { 178 bool compareAndNotify() {
181 var currentValue = _safeRead(); 179 var currentValue = _safeRead();
182 if (_compare(currentValue)) { 180 if (_compare(currentValue)) {
183 var oldValue = _lastValue; 181 var oldValue = _lastValue;
184 _update(currentValue); 182 _update(currentValue);
185 _callback(new WatchEvent(oldValue, currentValue)); 183 _callback(new ChangeNotification(oldValue, currentValue));
186 return true; 184 return true;
187 } 185 }
188 return false; 186 return false;
189 } 187 }
190 188
191 bool _compare(currentValue) => _lastValue != currentValue; 189 bool _compare(currentValue) => _lastValue != currentValue;
192 190
193 void _update(currentValue) { 191 void _update(currentValue) {
194 _lastValue = currentValue; 192 _lastValue = currentValue;
195 } 193 }
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
277 275
278 void set value(T value) { 276 void set value(T value) {
279 if (_setter != null) { 277 if (_setter != null) {
280 _setter(value); 278 _setter(value);
281 } else { 279 } else {
282 throw new Exception('Sorry - this handle has no setter.'); 280 throw new Exception('Sorry - this handle has no setter.');
283 } 281 }
284 } 282 }
285 } 283 }
286 284
287 /** 285 /**
288 * A watcher for list objects. It stores as the last value a shallow copy of the 286 * A watcher for list objects. It stores as the last value a shallow copy of the
289 * list as it was when we last detected any changes. 287 * list as it was when we last detected any changes.
290 */ 288 */
291 class _ListWatcher<T> extends _Watcher { 289 class _ListWatcher<T> extends _Watcher {
292 290
293 _ListWatcher(getter, ValueWatcher callback, String debugName) 291 _ListWatcher(getter, ChangeObserver callback, String debugName)
294 : super(getter, callback, debugName) { 292 : super(getter, callback, debugName) {
295 _update(_safeRead()); 293 _update(_safeRead());
296 } 294 }
297 295
298 bool _compare(List<T> currentValue) { 296 bool _compare(List<T> currentValue) {
299 if (_lastValue.length != currentValue.length) return true; 297 if (_lastValue.length != currentValue.length) return true;
300 298
301 for (int i = 0 ; i < _lastValue.length; i++) { 299 for (int i = 0 ; i < _lastValue.length; i++) {
302 if (_lastValue[i] != currentValue[i]) return true; 300 if (_lastValue[i] != currentValue[i]) return true;
303 } 301 }
304 return false; 302 return false;
305 } 303 }
306 304
307 void _update(currentValue) { 305 void _update(currentValue) {
308 _lastValue = new List<T>.from(currentValue); 306 _lastValue = new List<T>.from(currentValue);
309 } 307 }
310 } 308 }
OLDNEW
« no previous file with comments | « lib/templating.dart ('k') | lib/web_ui.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698