ObservableMap<K, V> class
Represents an observable map of model values. If any items are added, removed, or replaced, then observers that are listening to changes will be notified.
class ObservableMap<K, V> extends ObservableBase implements Map<K, V> { static const _LENGTH = const Symbol('length'); final Map<K, V> _map; /** Creates an observable map. */ ObservableMap() : _map = new HashMap<K, V>(); /** Creates a new observable map using a [LinkedHashMap]. */ ObservableMap.linked() : _map = new LinkedHashMap<K, V>(); /** Creates a new observable map using a [SplayTreeMap]. */ ObservableMap.sorted() : _map = new SplayTreeMap<K, V>(); /** * Creates an observable map that contains all key value pairs of [other]. * It will attempt to use the same backing map type if the other map is a * [LinkedHashMap], [SplayTreeMap], or [HashMap]. Otherwise it defaults to * [HashMap]. * * Note this will perform a shallow conversion. If you want a deep conversion * you should use [toObservable]. */ factory ObservableMap.from(Map<K, V> other) { var result = new ObservableMap<K, V>._createFromType(other); other.forEach((key, value) { result[key] = value; }); return result; } factory ObservableMap._createFromType(Map<K, V> other) { ObservableMap result; if (other is SplayTreeMap) { result = new ObservableMap<K, V>.sorted(); } else if (other is LinkedHashMap) { result = new ObservableMap<K, V>.linked(); } else { result = new ObservableMap<K, V>(); } return result; } Iterable<K> get keys => _map.keys; Iterable<V> get values => _map.values; int get length =>_map.length; bool get isEmpty => length == 0; bool get isNotEmpty => !isEmpty; bool containsValue(Object value) => _map.containsValue(value); bool containsKey(Object key) => _map.containsKey(key); V operator [](Object key) => _map[key]; void operator []=(K key, V value) { int len = _map.length; V oldValue = _map[key]; _map[key] = value; if (hasObservers) { if (len != _map.length) { notifyPropertyChange(_LENGTH, len, _map.length); notifyChange(new MapChangeRecord(key, isInsert: true)); } else if (!identical(oldValue, value)) { notifyChange(new MapChangeRecord(key)); } } } V putIfAbsent(K key, V ifAbsent()) { int len = _map.length; V result = _map.putIfAbsent(key, ifAbsent); if (hasObservers && len != _map.length) { notifyPropertyChange(_LENGTH, len, _map.length); notifyChange(new MapChangeRecord(key, isInsert: true)); } return result; } V remove(Object key) { int len = _map.length; V result = _map.remove(key); if (hasObservers && len != _map.length) { notifyChange(new MapChangeRecord(key, isRemove: true)); notifyPropertyChange(_LENGTH, len, _map.length); } return result; } void clear() { int len = _map.length; if (hasObservers && len > 0) { _map.forEach((key, value) { notifyChange(new MapChangeRecord(key, isRemove: true)); }); notifyPropertyChange(_LENGTH, len, 0); } _map.clear(); } void forEach(void f(K key, V value)) => _map.forEach(f); String toString() => Maps.mapToString(this); }
Extends
ObservableBase > ObservableMap<K, V>
Implements
Constructors
new ObservableMap() #
Creates an observable map.
ObservableMap() : _map = new HashMap<K, V>();
factory ObservableMap.from(Map<K, V> other) #
Creates an observable map that contains all key value pairs of other. It will attempt to use the same backing map type if the other map is a LinkedHashMap, SplayTreeMap, or HashMap. Otherwise it defaults to HashMap.
Note this will perform a shallow conversion. If you want a deep conversion you should use toObservable.
factory ObservableMap.from(Map<K, V> other) { var result = new ObservableMap<K, V>._createFromType(other); other.forEach((key, value) { result[key] = value; }); return result; }
new ObservableMap.linked() #
Creates a new observable map using a LinkedHashMap.
ObservableMap.linked() : _map = new LinkedHashMap<K, V>();
new ObservableMap.sorted() #
Creates a new observable map using a SplayTreeMap.
ObservableMap.sorted() : _map = new SplayTreeMap<K, V>();
Properties
final Stream<List<ChangeRecord>> changes #
The stream of change records to this object.
Changes should be delivered in asynchronous batches by calling queueChangeRecords.
deliverChangeRecords can be called to force delivery.
Stream<List<ChangeRecord>> get changes { if (_broadcastController == null) { _broadcastController = new StreamController<List<ChangeRecord>>.broadcast(sync: true); } return _broadcastController.stream; }
final bool hasObservers #
True if this object has any observers, and should call notifyPropertyChange for changes.
bool get hasObservers => _broadcastController != null && _broadcastController.hasListener;
final bool isEmpty #
final bool isNotEmpty #
final int length #
Operators
V operator [](Object key) #
Returns the value for the given key or null if key is not in the map. Because null values are supported, one should either use containsKey to distinguish between an absent key and a null value, or use the putIfAbsent method.
V operator [](Object key) => _map[key];
void operator []=(K key, V value) #
void operator []=(K key, V value) { int len = _map.length; V oldValue = _map[key]; _map[key] = value; if (hasObservers) { if (len != _map.length) { notifyPropertyChange(_LENGTH, len, _map.length); notifyChange(new MapChangeRecord(key, isInsert: true)); } else if (!identical(oldValue, value)) { notifyChange(new MapChangeRecord(key)); } } }
Methods
abstract void addAll(Map<K, V> other) #
Adds all key-value pairs of other to this map.
If a key of other is already in this map, its value is overwritten.
The operation is equivalent to doing this[key] = value
for each key
and associated value in other. It iterates over
other, which must
therefore not change during the iteration.
void clear() #
bool containsKey(Object key) #
bool containsValue(Object value) #
void notifyChange(ChangeRecord record) #
Notify observers of a change. For most objects notifyPropertyChange is more convenient, but collections sometimes deliver other types of changes such as a ListChangeRecord.
void notifyChange(ChangeRecord record) { if (!hasObservers) return; if (_changes == null) { _changes = []; queueChangeRecords(_deliverChanges); } _changes.add(record); }
dynamic notifyPropertyChange(Symbol field, Object oldValue, Object newValue) #
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);
}
notifyPropertyChange(Symbol field, Object oldValue, Object newValue) { if (hasObservers && !identical(oldValue, newValue)) { notifyChange(new PropertyChangeRecord(field)); } return newValue; }
V putIfAbsent(K key, V ifAbsent()) #
If key is not associated to a value, calls ifAbsent and updates the map by mapping key to the value returned by ifAbsent. Returns the value in the map.
It is an error to add or remove keys from map during the call to ifAbsent.
V putIfAbsent(K key, V ifAbsent()) { int len = _map.length; V result = _map.putIfAbsent(key, ifAbsent); if (hasObservers && len != _map.length) { notifyPropertyChange(_LENGTH, len, _map.length); notifyChange(new MapChangeRecord(key, isInsert: true)); } return result; }
V remove(Object key) #
Removes the association for the given key. Returns the value for key in the map or null if key is not in the map. Note that values can be null and a returned null value does not always imply that the key is absent.
V remove(Object key) { int len = _map.length; V result = _map.remove(key); if (hasObservers && len != _map.length) { notifyChange(new MapChangeRecord(key, isRemove: true)); notifyPropertyChange(_LENGTH, len, _map.length); } return result; }
String toString() #
Returns a string representation of this object.
String toString() => Maps.mapToString(this);