|
5 | 5 | library observable.src.observable;
|
6 | 6 |
|
7 | 7 | import 'dart:async';
|
8 |
| -import 'dart:collection' show UnmodifiableListView; |
9 | 8 |
|
10 | 9 | import 'package:meta/meta.dart';
|
11 | 10 |
|
12 |
| -import 'property_change_record.dart' show PropertyChangeRecord; |
13 |
| -import 'records.dart' show ChangeRecord; |
| 11 | +import 'change_notifier.dart'; |
| 12 | +import 'records.dart'; |
14 | 13 |
|
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). |
| 14 | +/// Represents an object with observable state or properties. |
18 | 15 | ///
|
19 | 16 | /// 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; |
| 17 | +/// observability. You may implement it in the following ways: |
| 18 | +/// - Extend or mixin [ChangeNotifier] |
| 19 | +/// - Implement the interface yourself and provide your own implementation |
| 20 | +abstract class Observable<C extends ChangeRecord> { |
| 21 | + // To be removed when https://github.com/dart-lang/observable/issues/10 |
| 22 | + final ChangeNotifier<C> _delegate = new ChangeNotifier<C>(); |
28 | 23 |
|
29 |
| - List<ChangeRecord> _records; |
| 24 | + // Whether Observable was not given a type. |
| 25 | + final bool _isNotGeneric = C == dynamic; |
30 | 26 |
|
31 |
| - /// The stream of property change records to this object, delivered |
32 |
| - /// asynchronously. |
| 27 | + /// Emits a list of changes when the state of the object changes. |
33 | 28 | ///
|
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 |
| - } |
| 29 | + /// Changes should produced in order, if significant. |
| 30 | + Stream<List<C>> get changes => _delegate.changes; |
42 | 31 |
|
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) |
| 32 | + /// May override to be notified when [changes] is first observed. |
46 | 33 | @protected
|
47 |
| - void observed() {} |
| 34 | + @mustCallSuper |
| 35 | + @Deprecated('Use ChangeNotifier instead to have this method available') |
| 36 | + // REMOVE IGNORE when https://github.com/dart-lang/observable/issues/10 |
| 37 | + // ignore: invalid_use_of_protected_member |
| 38 | + void observed() => _delegate.observed(); |
48 | 39 |
|
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) |
| 40 | + /// May override to be notified when [changes] is no longer observed. |
52 | 41 | @protected
|
53 |
| - void unobserved() { |
54 |
| - // Free some memory |
55 |
| - _changes = null; |
56 |
| - } |
| 42 | + @mustCallSuper |
| 43 | + @Deprecated('Use ChangeNotifier instead to have this method available') |
| 44 | + // REMOVE IGNORE when https://github.com/dart-lang/observable/issues/10 |
| 45 | + // ignore: invalid_use_of_protected_member |
| 46 | + void unobserved() => _delegate.unobserved(); |
57 | 47 |
|
58 | 48 | /// True if this object has any observers.
|
59 |
| - bool get hasObservers => _changes != null && _changes.hasListener; |
| 49 | + @Deprecated('Use ChangeNotifier instead to have this method available') |
| 50 | + bool get hasObservers => _delegate.hasObservers; |
60 | 51 |
|
61 |
| - /// Synchronously deliver pending [changes]. |
| 52 | + /// If [hasObservers], synchronously emits [changes] that have been queued. |
62 | 53 | ///
|
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 |
| - } |
| 54 | + /// Returns `true` if changes were emitted. |
| 55 | + @Deprecated('Use ChangeNotifier instead to have this method available') |
| 56 | + // REMOVE IGNORE when https://github.com/dart-lang/observable/issues/10 |
| 57 | + // ignore: invalid_use_of_protected_member |
| 58 | + bool deliverChanges() => _delegate.deliverChanges(); |
76 | 59 |
|
77 | 60 | /// Notify that the [field] name of this object has been changed.
|
78 | 61 | ///
|
79 | 62 | /// The [oldValue] and [newValue] are also recorded. If the two values are
|
80 | 63 | /// equal, no change will be recorded.
|
81 | 64 | ///
|
82 | 65 | /// For convenience this returns [newValue].
|
| 66 | + /// |
| 67 | + /// ## Deprecated |
| 68 | + /// |
| 69 | + /// All [Observable] objects will no longer be required to emit change records |
| 70 | + /// when any property changes. For example, `ObservableList` will only emit |
| 71 | + /// on `ObservableList.changes`, instead of on `ObservableList.listChanges`. |
| 72 | + /// |
| 73 | + /// If you are using a typed `implements/extends Observable<C>`, it is illegal |
| 74 | + /// to call this method - will throw an [UnsupportedError] when called. |
| 75 | + @Deprecated('Use PropertyChangeNotifier') |
83 | 76 | /*=T*/ notifyPropertyChange/*<T>*/(
|
84 |
| - Symbol field, /*=T*/ oldValue, /*=T*/ newValue) { |
| 77 | + Symbol field, |
| 78 | + /*=T*/ |
| 79 | + oldValue, |
| 80 | + /*=T*/ |
| 81 | + newValue, |
| 82 | + ) { |
85 | 83 | if (hasObservers && oldValue != newValue) {
|
86 |
| - notifyChange(new PropertyChangeRecord(this, field, oldValue, newValue)); |
| 84 | + if (_isNotGeneric) { |
| 85 | + notifyChange( |
| 86 | + new PropertyChangeRecord( |
| 87 | + this, |
| 88 | + field, |
| 89 | + oldValue, |
| 90 | + newValue, |
| 91 | + ) as C, |
| 92 | + ); |
| 93 | + } else { |
| 94 | + throw new UnsupportedError('Generic typed Observable does not support'); |
| 95 | + } |
87 | 96 | }
|
88 | 97 | return newValue;
|
89 | 98 | }
|
90 | 99 |
|
91 |
| - /// Notify observers of a change. |
| 100 | + /// Schedules [change] to be delivered. |
92 | 101 | ///
|
93 |
| - /// This will automatically schedule [deliverChanges]. |
| 102 | + /// If [change] is omitted then [ChangeRecord.ANY] will be sent. |
94 | 103 | ///
|
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 |
| - } |
| 104 | + /// If there are no listeners to [changes], this method does nothing. |
| 105 | + @Deprecated('Use ChangeNotifier instead to have this method available') |
| 106 | + void notifyChange([C change]) => _delegate.notifyChange(change); |
106 | 107 | }
|
0 commit comments