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

Side by Side Diff: lib/watcher.dart

Issue 12096106: work in progress: observable implementation using detailed change records (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 29 matching lines...) Expand all
40 * You can watch several kinds of expressions, including lists. See [watch] for 40 * You can watch several kinds of expressions, including lists. See [watch] for
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 'observe.dart';
51
52 /**
53 * True to use the [observe] library instead of watchers.
54 *
55 * Observers require the [observable] annotation on objects and for collection
56 * types to be observable, such as [ObservableList]. But in return they offer
57 * better performance and more precise change tracking. [dispatch] is not
58 * required with observers, and changes to observable objects are always
59 * detected.
60 *
61 * Currently this flag is experimental, but it may be the default in the future.
62 */
63 bool useObservers = false;
50 64
51 /** 65 /**
52 * Watch for changes in [target]. The [callback] function will be called when 66 * Watch for changes in [target]. The [callback] function will be called when
53 * [dispatch] is called and the value represented by [target] had changed. The 67 * [dispatch] is called and the value represented by [target] had changed. The
54 * returned function can be used to unregister this watcher. 68 * returned function can be used to unregister this watcher.
55 * 69 *
56 * There are several values you can use for [target]: 70 * There are several values you can use for [target]:
57 * 71 *
58 * * A [Getter] function. 72 * * A [Getter] function.
59 * Use this to watch expressions as they change. For instance, to watch 73 * Use this to watch expressions as they change. For instance, to watch
60 * whether `a.b.c` changes, wrap it in a getter and call [watch] as follows: 74 * whether `a.b.c` changes, wrap it in a getter and call [watch] as follows:
61 * watch(() => a.b.c, ...) 75 * watch(() => a.b.c, ...)
62 * These targets are tracked to check for equality. If calling `target()` 76 * These targets are tracked to check for equality. If calling `target()`
63 * returns the same result, then the [callback] will not be invoked. In the 77 * returns the same result, then the [callback] will not be invoked. In the
64 * special case whe the getter returns a [List], we will treat the value in a 78 * special case whe the getter returns a [List], we will treat the value in a
65 * special way, similar to passing [List] directly as [target]. 79 * special way, similar to passing [List] directly as [target].
66 * **Important**: this library assumes that [Getter] is a read-only function 80 * **Important**: this library assumes that [Getter] is a read-only function
67 * and that it will consistently return the same value if called multiple 81 * and that it will consistently return the same value if called multiple
68 * times in a row. 82 * times in a row.
69 * 83 *
70 * * A [List]. 84 * * A [List].
71 * Use this to watch whether a list object changes. For instance, to detect 85 * Use this to watch whether a list object changes. For instance, to detect
72 * if an element is added or changed in a list can call [watch] as follows: 86 * if an element is added or changed in a list can call [watch] as follows:
73 * watch(list, ...) 87 * watch(list, ...)
74 * 88 *
75 * * A [Handle]. 89 * * A [Handle].
76 * This is syntactic sugar for using the getter portion of a [Handle]. 90 * This is syntactic sugar for using the getter portion of a [Handle].
77 * watch(handle, ...) // equivalent to `watch(handle._getter, ...)` 91 * watch(handle, ...) // equivalent to `watch(handle._getter, ...)`
78 */ 92 */
79 WatcherDisposer watch(var target, ValueWatcher callback, [String debugName]) { 93 ChangeUnobserver watch(target, ExpressionObserver callback,
94 [String debugName]) {
95 if (useObservers) return observe(target, callback);
96
80 if (callback == null) return () {}; // no use in passing null as a callback. 97 if (callback == null) return () {}; // no use in passing null as a callback.
81 if (_watchers == null) _watchers = []; 98 if (_watchers == null) _watchers = [];
82 Function exp; 99 Function exp;
83 bool isList = false; 100 bool isList = false;
84 if (target is Handle) { 101 if (target is Handle) {
85 exp = (target as Handle)._getter; 102 exp = (target as Handle)._getter;
86 } else if (target is Function) { 103 } else if (target is Function) {
87 exp = target; 104 exp = target;
88 try { 105 try {
89 isList = (target() is List); 106 isList = (target() is List);
(...skipping 12 matching lines...) Expand all
102 : new _Watcher(exp, callback, debugName); 119 : new _Watcher(exp, callback, debugName);
103 _watchers.add(watcher); 120 _watchers.add(watcher);
104 return () => _unregister(watcher); 121 return () => _unregister(watcher);
105 } 122 }
106 123
107 /** 124 /**
108 * Add a watcher for [exp] and immediatly invoke [callback]. The watch event 125 * Add a watcher for [exp] and immediatly invoke [callback]. The watch event
109 * passed to [callback] will have `null` as the old value, and the current 126 * passed to [callback] will have `null` as the old value, and the current
110 * evaluation of [exp] as the new value. 127 * evaluation of [exp] as the new value.
111 */ 128 */
112 WatcherDisposer watchAndInvoke(exp, callback, [debugName]) { 129 ChangeUnobserver watchAndInvoke(exp, callback, [debugName]) {
113 var res = watch(exp, callback, debugName); 130 var res = watch(exp, callback, debugName);
114 // TODO(jmesserly): this should be "is Getter" once dart2js bug is fixed. 131 // TODO(jmesserly): this should be "is Getter" once dart2js bug is fixed.
115 if (exp is Function) { 132 if (exp is Function) {
116 callback(new WatchEvent(null, exp())); 133 callback(new ExpressionChange(null, exp()));
117 } else { 134 } else {
118 callback(new WatchEvent(null, exp)); 135 callback(new ExpressionChange(null, exp));
119 } 136 }
120 return res; 137 return res;
121 } 138 }
122 139
123 /** Callback fired when an expression changes. */
124 typedef void ValueWatcher(WatchEvent e);
125
126 /** A function that unregisters a watcher. */
127 typedef void WatcherDisposer();
128
129 /** Event passed to [ValueMatcher] showing what changed. */
130 class WatchEvent {
131
132 /** Previous value seen on the watched expression. */
133 final oldValue;
134
135 /** New value seen on the watched expression. */
136 final newValue;
137
138 WatchEvent(this.oldValue, this.newValue);
139 }
140
141 /** Internal set of active watchers. */ 140 /** Internal set of active watchers. */
142 List<_Watcher> _watchers; 141 List<_Watcher> _watchers;
143 142
144 /** 143 /**
145 * An internal representation of a watcher. Contains the expression it watches, 144 * An internal representation of a watcher. Contains the expression it watches,
146 * the last value seen for it, and a callback to invoke when a change is 145 * the last value seen for it, and a callback to invoke when a change is
147 * detected. 146 * detected.
148 */ 147 */
149 class _Watcher { 148 class _Watcher {
150 /** Name used to debug. */ 149 /** Name used to debug. */
151 final String debugName; 150 final String debugName;
152 151
153 /** Function that retrieves the value being watched. */ 152 /** Function that retrieves the value being watched. */
154 final Getter _getter; 153 final Getter _getter;
155 154
156 /** Callback to invoke when the value changes. */ 155 /** Callback to invoke when the value changes. */
157 final ValueWatcher _callback; 156 final ExpressionObserver _callback;
158 157
159 /** Last value observed on the matched expression. */ 158 /** Last value observed on the matched expression. */
160 var _lastValue; 159 var _lastValue;
161 160
162 _Watcher(this._getter, this._callback, this.debugName) { 161 _Watcher(this._getter, this._callback, this.debugName) {
163 _lastValue = _getter(); 162 _lastValue = _getter();
164 } 163 }
165 164
166 String toString() => debugName == null ? '<unnamed>' : debugName; 165 String toString() => debugName == null ? '<unnamed>' : debugName;
167 166
168 /** Detect if any changes occurred and if so invoke [_callback]. */ 167 /** Detect if any changes occurred and if so invoke [_callback]. */
169 bool compareAndNotify() { 168 bool compareAndNotify() {
170 var currentValue = _safeRead(); 169 var currentValue = _safeRead();
171 if (_compare(currentValue)) { 170 if (_compare(currentValue)) {
172 var oldValue = _lastValue; 171 var oldValue = _lastValue;
173 _update(currentValue); 172 _update(currentValue);
174 _callback(new WatchEvent(oldValue, currentValue)); 173 _callback(new ExpressionChange(oldValue, currentValue));
175 return true; 174 return true;
176 } 175 }
177 return false; 176 return false;
178 } 177 }
179 178
180 bool get _hasChanged => _compare(_safeRead()); 179 bool get _hasChanged => _compare(_safeRead());
181 180
182 void _updateAndNotify() { 181 void _updateAndNotify() {
183 var currentValue = _safeRead(); 182 var currentValue = _safeRead();
184 var oldValue = _lastValue; 183 var oldValue = _lastValue;
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
280 279
281 void set value(T value) { 280 void set value(T value) {
282 if (_setter != null) { 281 if (_setter != null) {
283 _setter(value); 282 _setter(value);
284 } else { 283 } else {
285 throw new Exception('Sorry - this handle has no setter.'); 284 throw new Exception('Sorry - this handle has no setter.');
286 } 285 }
287 } 286 }
288 } 287 }
289 288
290 /** 289 /**
291 * A watcher for list objects. It stores as the last value a shallow copy of the 290 * A watcher for list objects. It stores as the last value a shallow copy of the
292 * list as it was when we last detected any changes. 291 * list as it was when we last detected any changes.
293 */ 292 */
294 class _ListWatcher<T> extends _Watcher { 293 class _ListWatcher<T> extends _Watcher {
295 294
296 _ListWatcher(getter, ValueWatcher callback, String debugName) 295 _ListWatcher(getter, ExpressionObserver callback, String debugName)
297 : super(getter, callback, debugName) { 296 : super(getter, callback, debugName) {
298 _update(_safeRead()); 297 _update(_safeRead());
299 } 298 }
300 299
301 bool _compare(List<T> currentValue) { 300 bool _compare(List<T> currentValue) {
302 if (_lastValue.length != currentValue.length) return true; 301 if (_lastValue.length != currentValue.length) return true;
303 302
304 for (int i = 0 ; i < _lastValue.length; i++) { 303 for (int i = 0 ; i < _lastValue.length; i++) {
305 if (_lastValue[i] != currentValue[i]) return true; 304 if (_lastValue[i] != currentValue[i]) return true;
306 } 305 }
307 return false; 306 return false;
308 } 307 }
309 308
310 void _update(currentValue) { 309 void _update(currentValue) {
311 _lastValue = new List<T>.from(currentValue); 310 _lastValue = new List<T>.from(currentValue);
312 } 311 }
313 } 312 }
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