Chromium Code Reviews| Index: pkg/observe/lib/src/change_notifier.dart |
| diff --git a/pkg/observe/lib/src/change_notifier.dart b/pkg/observe/lib/src/change_notifier.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..9e54f60811c5c6e2e956a27f038cdbfacfabde26 |
| --- /dev/null |
| +++ b/pkg/observe/lib/src/change_notifier.dart |
| @@ -0,0 +1,112 @@ |
| +// Copyright (c) 2013, 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. |
| + |
| +part of observe; |
| + |
| +/** |
| + * Interface representing an [Observable] object that performs its own change |
| + * notifications, and does not need to be considered by [Observable.dirtyCheck]. |
| + */ |
| +abstract class ChangeNotifier extends Observable { |
|
Jennifer Messerly
2013/07/19 01:32:58
splitting Observable and ChangeNotifier into disti
|
| + /** |
| + * Notify observers of a change. |
| + * |
| + * For most objects [ChangeNotifierMixin.notifyPropertyChange] is more |
| + * convenient, but collections sometimes deliver other types of changes such |
| + * as a [ListChangeRecord]. |
| + */ |
| + void notifyChange(ChangeRecord record); |
| +} |
| + |
| +/** |
| + * Base class implementing [ChangeNotifier]. |
| + * |
| + * When a field, property, or indexable item is changed, a derived class should |
| + * call [notifyPropertyChange]. See that method for an example. |
| + */ |
| +typedef ChangeNotifierBase = Object with ChangeNotifierMixin; |
|
Jennifer Messerly
2013/07/19 01:32:58
I'm not sure about the split here into ChangeNotif
|
| + |
| +/** |
| + * Mixin for implementing [ChangeNotifier] objects. |
| + * |
| + * When a field, property, or indexable item is changed, a derived class should |
| + * call [notifyPropertyChange]. See that method for an example. |
| + */ |
| +abstract class ChangeNotifierMixin implements ChangeNotifier { |
|
Jennifer Messerly
2013/07/19 01:32:58
this is essentially the old ObservableMixin impl
|
| + StreamController _changes; |
| + List<ChangeRecord> _records; |
| + |
| + Stream<List<ChangeRecord>> get changes { |
| + if (_changes == null) { |
| + _changes = new StreamController.broadcast(sync: true, |
| + onListen: _observed, onCancel: _unobserved); |
| + } |
| + return _changes.stream; |
| + } |
| + |
| + // TODO(jmesserly): should these be public? They're useful lifecycle methods |
| + // for subclasses. Ideally they'd be protected. |
| + /** |
| + * Override this method to be called when the [changes] are first observed. |
| + */ |
| + void _observed() {} |
| + |
| + /** |
| + * Override this method to be called when the [changes] are no longer being |
| + * observed. |
| + */ |
| + void _unobserved() {} |
| + |
| + bool deliverChanges() { |
| + var records = _records; |
| + _records = null; |
| + if (hasObservers && records != null) { |
| + // TODO(jmesserly): make "records" immutable |
| + _changes.add(records); |
| + return true; |
| + } |
| + return false; |
| + } |
| + |
| + /** |
| + * True if this object has any observers, and should call |
| + * [notifyPropertyChange] for changes. |
| + */ |
| + bool get hasObservers => _changes != null && _changes.hasListener; |
| + |
| + /** |
| + * Notify that the field [name] of this object has been changed. |
| + * |
| + * The [oldValue] and [newValue] are also recorded. If the two values are |
| + * identical, no change will be recorded. |
| + * |
| + * For convenience this returns [newValue]. This makes it easy to use in a |
| + * setter: |
| + * |
| + * var _myField; |
| + * get myField => _myField; |
| + * set myField(value) { |
| + * _myField = notifyPropertyChange( |
| + * const Symbol('myField'), _myField, value); |
| + * } |
| + */ |
| + // TODO(jmesserly): should this be == instead of identical, to prevent |
| + // spurious loops? |
| + notifyPropertyChange(Symbol field, Object oldValue, Object newValue) { |
| + if (hasObservers && !identical(oldValue, newValue)) { |
| + notifyChange(new PropertyChangeRecord(field)); |
| + } |
| + return newValue; |
| + } |
| + |
| + void notifyChange(ChangeRecord record) { |
| + if (!hasObservers) return; |
| + |
| + if (_records == null) { |
| + _records = []; |
| + runAsync(deliverChanges); |
| + } |
| + _records.add(record); |
| + } |
| +} |