| Index: packages/observable/lib/src/observable.dart
|
| diff --git a/packages/observable/lib/src/observable.dart b/packages/observable/lib/src/observable.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..7d1ec6f4c4f3dd5af22d1f590a3239c7755f3790
|
| --- /dev/null
|
| +++ b/packages/observable/lib/src/observable.dart
|
| @@ -0,0 +1,106 @@
|
| +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +library observable.src.observable;
|
| +
|
| +import 'dart:async';
|
| +import 'dart:collection' show UnmodifiableListView;
|
| +
|
| +import 'package:meta/meta.dart';
|
| +
|
| +import 'change_record.dart' show ChangeRecord;
|
| +import 'property_change_record.dart' show PropertyChangeRecord;
|
| +
|
| +/// Represents an object with observable properties. This is used by data in
|
| +/// model-view architectures to notify interested parties of [changes] to the
|
| +/// object's properties (fields or getter/setter pairs).
|
| +///
|
| +/// The interface does not require any specific technique to implement
|
| +/// observability. You can implement it in the following ways:
|
| +///
|
| +/// - Deriving from this class via a mixin or base class. When a field,
|
| +/// property, or indexable item is changed, the derived class should call
|
| +/// [notifyPropertyChange]. See that method for an example.
|
| +/// - Implementing this interface and providing your own implementation.
|
| +abstract class Observable {
|
| + StreamController<List<ChangeRecord>> _changes;
|
| +
|
| + List<ChangeRecord> _records;
|
| +
|
| + /// The stream of property change records to this object, delivered
|
| + /// asynchronously.
|
| + ///
|
| + /// [deliverChanges] can be called to force synchronous delivery.
|
| + Stream<List<ChangeRecord>> get changes {
|
| + if (_changes == null) {
|
| + _changes = new StreamController.broadcast(
|
| + sync: true, onListen: observed, onCancel: unobserved);
|
| + }
|
| + return _changes.stream;
|
| + }
|
| +
|
| + /// Derived classes may override this method to be called when the [changes]
|
| + /// are first observed.
|
| + // TODO(tvolkert): @mustCallSuper (github.com/dart-lang/sdk/issues/27275)
|
| + @protected
|
| + void observed() {}
|
| +
|
| + /// Derived classes may override this method to be called when the [changes]
|
| + /// are no longer being observed.
|
| + // TODO(tvolkert): @mustCallSuper (github.com/dart-lang/sdk/issues/27275)
|
| + @protected
|
| + void unobserved() {
|
| + // Free some memory
|
| + _changes = null;
|
| + }
|
| +
|
| + /// True if this object has any observers.
|
| + bool get hasObservers => _changes != null && _changes.hasListener;
|
| +
|
| + /// Synchronously deliver pending [changes].
|
| + ///
|
| + /// Returns `true` if any records were delivered, otherwise `false`.
|
| + /// Pending records will be cleared regardless, to keep newly added
|
| + /// observers from being notified of changes that occurred before
|
| + /// they started observing.
|
| + bool deliverChanges() {
|
| + List<ChangeRecord> records = _records;
|
| + _records = null;
|
| + if (hasObservers && records != null) {
|
| + _changes.add(new UnmodifiableListView<ChangeRecord>(records));
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + /// Notify that the [field] name of this object has been changed.
|
| + ///
|
| + /// The [oldValue] and [newValue] are also recorded. If the two values are
|
| + /// equal, no change will be recorded.
|
| + ///
|
| + /// For convenience this returns [newValue].
|
| + /*=T*/ notifyPropertyChange/*<T>*/(
|
| + Symbol field, /*=T*/ oldValue, /*=T*/ newValue) {
|
| + if (hasObservers && oldValue != newValue) {
|
| + notifyChange(new PropertyChangeRecord(this, field, oldValue, newValue));
|
| + }
|
| + return newValue;
|
| + }
|
| +
|
| + /// Notify observers of a change.
|
| + ///
|
| + /// This will automatically schedule [deliverChanges].
|
| + ///
|
| + /// For most objects [Observable.notifyPropertyChange] is more convenient, but
|
| + /// collections sometimes deliver other types of changes such as a
|
| + /// [MapChangeRecord].
|
| + void notifyChange(ChangeRecord record) {
|
| + if (!hasObservers) return;
|
| + if (_records == null) {
|
| + _records = [];
|
| + scheduleMicrotask(deliverChanges);
|
| + }
|
| + _records.add(record);
|
| + }
|
| +}
|
|
|