OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016, 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 library observable.src.observable; |
| 6 |
| 7 import 'dart:async'; |
| 8 import 'dart:collection' show UnmodifiableListView; |
| 9 |
| 10 import 'package:meta/meta.dart'; |
| 11 |
| 12 import 'change_record.dart' show ChangeRecord; |
| 13 import 'property_change_record.dart' show PropertyChangeRecord; |
| 14 |
| 15 /// Represents an object with observable properties. This is used by data in |
| 16 /// model-view architectures to notify interested parties of [changes] to the |
| 17 /// object's properties (fields or getter/setter pairs). |
| 18 /// |
| 19 /// The interface does not require any specific technique to implement |
| 20 /// observability. You can implement it in the following ways: |
| 21 /// |
| 22 /// - Deriving from this class via a mixin or base class. When a field, |
| 23 /// property, or indexable item is changed, the derived class should call |
| 24 /// [notifyPropertyChange]. See that method for an example. |
| 25 /// - Implementing this interface and providing your own implementation. |
| 26 abstract class Observable { |
| 27 StreamController<List<ChangeRecord>> _changes; |
| 28 |
| 29 List<ChangeRecord> _records; |
| 30 |
| 31 /// The stream of property change records to this object, delivered |
| 32 /// asynchronously. |
| 33 /// |
| 34 /// [deliverChanges] can be called to force synchronous delivery. |
| 35 Stream<List<ChangeRecord>> get changes { |
| 36 if (_changes == null) { |
| 37 _changes = new StreamController.broadcast( |
| 38 sync: true, onListen: observed, onCancel: unobserved); |
| 39 } |
| 40 return _changes.stream; |
| 41 } |
| 42 |
| 43 /// Derived classes may override this method to be called when the [changes] |
| 44 /// are first observed. |
| 45 // TODO(tvolkert): @mustCallSuper (github.com/dart-lang/sdk/issues/27275) |
| 46 @protected |
| 47 void observed() {} |
| 48 |
| 49 /// Derived classes may override this method to be called when the [changes] |
| 50 /// are no longer being observed. |
| 51 // TODO(tvolkert): @mustCallSuper (github.com/dart-lang/sdk/issues/27275) |
| 52 @protected |
| 53 void unobserved() { |
| 54 // Free some memory |
| 55 _changes = null; |
| 56 } |
| 57 |
| 58 /// True if this object has any observers. |
| 59 bool get hasObservers => _changes != null && _changes.hasListener; |
| 60 |
| 61 /// Synchronously deliver pending [changes]. |
| 62 /// |
| 63 /// Returns `true` if any records were delivered, otherwise `false`. |
| 64 /// Pending records will be cleared regardless, to keep newly added |
| 65 /// observers from being notified of changes that occurred before |
| 66 /// they started observing. |
| 67 bool deliverChanges() { |
| 68 List<ChangeRecord> records = _records; |
| 69 _records = null; |
| 70 if (hasObservers && records != null) { |
| 71 _changes.add(new UnmodifiableListView<ChangeRecord>(records)); |
| 72 return true; |
| 73 } |
| 74 return false; |
| 75 } |
| 76 |
| 77 /// Notify that the [field] name of this object has been changed. |
| 78 /// |
| 79 /// The [oldValue] and [newValue] are also recorded. If the two values are |
| 80 /// equal, no change will be recorded. |
| 81 /// |
| 82 /// For convenience this returns [newValue]. |
| 83 /*=T*/ notifyPropertyChange/*<T>*/( |
| 84 Symbol field, /*=T*/ oldValue, /*=T*/ newValue) { |
| 85 if (hasObservers && oldValue != newValue) { |
| 86 notifyChange(new PropertyChangeRecord(this, field, oldValue, newValue)); |
| 87 } |
| 88 return newValue; |
| 89 } |
| 90 |
| 91 /// Notify observers of a change. |
| 92 /// |
| 93 /// This will automatically schedule [deliverChanges]. |
| 94 /// |
| 95 /// For most objects [Observable.notifyPropertyChange] is more convenient, but |
| 96 /// collections sometimes deliver other types of changes such as a |
| 97 /// [MapChangeRecord]. |
| 98 void notifyChange(ChangeRecord record) { |
| 99 if (!hasObservers) return; |
| 100 if (_records == null) { |
| 101 _records = []; |
| 102 scheduleMicrotask(deliverChanges); |
| 103 } |
| 104 _records.add(record); |
| 105 } |
| 106 } |
OLD | NEW |