Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit 3827f42

Browse files
vicbrkirov
authored andcommitted
feat(dccd): Observable support
1 parent 7a10390 commit 3827f42

File tree

2 files changed

+67
-13
lines changed

2 files changed

+67
-13
lines changed

lib/change_detection/dirty_checking_change_detector.dart

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -429,10 +429,11 @@ class DirtyCheckingRecord<H> implements WatchRecord<H> {
429429
static const int _MODE_GETTER_OR_METHOD_CLOSURE_ = 5;
430430
static const int _MODE_GETTER_OBS_OR_METHOD_CLOSURE_ = 6;
431431
static const int _MODE_MAP_FIELD_ = 7;
432-
static const int _MODE_ITERABLE_ = 8;
433-
static const int _MODE_LIST_NOTIFIED_ = 9;
434-
static const int _MODE_MAP_ = 10;
435-
static const int _MODE_MAP_NOTIFIED_ = 11;
432+
static const int _MODE_MAP_FIELD_NOTIFIED_ = 8;
433+
static const int _MODE_ITERABLE_ = 9;
434+
static const int _MODE_LIST_NOTIFIED_ = 10;
435+
static const int _MODE_MAP_ = 11;
436+
static const int _MODE_MAP_NOTIFIED_ = 12;
436437

437438
final DirtyCheckingChangeDetectorGroup _group;
438439
final FieldGetterFactory _fieldGetterFactory;
@@ -476,7 +477,7 @@ class DirtyCheckingRecord<H> implements WatchRecord<H> {
476477
while (_object is ContextLocals) {
477478
var ctx = _object as ContextLocals;
478479
if (ctx.hasProperty(field)) {
479-
_mode = _MODE_MAP_FIELD_;
480+
_mode = _MODE_MAP_FIELD_;
480481
_getter = null;
481482
return;
482483
}
@@ -501,9 +502,9 @@ class DirtyCheckingRecord<H> implements WatchRecord<H> {
501502
// mapping with the one from the new reference.
502503
currentValue._revertToPreviousState();
503504
}
504-
if (_object is obs.ChangeNotifier) {
505+
if (_object is obs.Observable) {
505506
_mode = _MODE_MAP_NOTIFIED_; // Run the dccd after the map is added
506-
var subscription = (_object as obs.ChangeNotifier).changes.listen((_) {
507+
var subscription = (_object as obs.Observable).changes.listen((_) {
507508
_mode = _MODE_MAP_NOTIFIED_; // Run the dccd after the map is updated
508509
});
509510
_group._registerObservable(this, subscription);
@@ -536,10 +537,19 @@ class DirtyCheckingRecord<H> implements WatchRecord<H> {
536537
}
537538

538539
if (_object is Map) {
539-
_mode = _MODE_MAP_FIELD_;
540-
_getter = null;
540+
if (_object is obs.Observable) {
541+
_mode = _MODE_MAP_FIELD_NOTIFIED_;
542+
var subscription = (_object as obs.Observable).changes.listen((_) {
543+
_mode = _MODE_MAP_FIELD_NOTIFIED_; // Run the dccd after the map is updated
544+
});
545+
_group._registerObservable(this, subscription);
546+
_getter = null;
547+
} else {
548+
_mode = _MODE_MAP_FIELD_;
549+
_getter = null;
550+
}
541551
} else {
542-
_mode = _object is obs.ChangeNotifier ?
552+
_mode = _object is obs.Observable ?
543553
_MODE_GETTER_OBS_OR_METHOD_CLOSURE_ :
544554
_MODE_GETTER_OR_METHOD_CLOSURE_;
545555
_getter = _fieldGetterFactory.getter(_object, field);
@@ -579,9 +589,6 @@ class DirtyCheckingRecord<H> implements WatchRecord<H> {
579589
_mode = _MODE_NOOP_;
580590
if (current is! Function || identical(current, _getter(object))) {
581591
var subscription = (object as obs.Observable).changes.listen((records) {
582-
// todo(vicb) we should only go to the _MODE_GETTER_NOTIFIED_ mode when a record
583-
// is applicable to the current `field`. With the current implementation, any field
584-
// on an observable object will trigger this listener.
585592
_mode = _MODE_GETTER_NOTIFIED_;
586593
});
587594
_group._registerObservable(this, subscription);
@@ -590,6 +597,10 @@ class DirtyCheckingRecord<H> implements WatchRecord<H> {
590597
case _MODE_MAP_FIELD_:
591598
current = object[field];
592599
break;
600+
case _MODE_MAP_FIELD_NOTIFIED_:
601+
_mode = _MODE_NOOP_; // no-op until next notification
602+
current = object[field];
603+
break;
593604
case _MODE_IDENTITY_:
594605
current = object;
595606
_mode = _MODE_NOOP_;

test/change_detection/dirty_checking_change_detector_spec.dart

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,29 @@ void testWithGetterFactory(FieldGetterFactory getterFactory) {
594594
describe('map watching', () {
595595
addMapSpec(useObservable: false);
596596
addMapSpec(useObservable: true);
597+
598+
it('should only check ObservableMap items after notification', async(() {
599+
var map = new ObservableMap.from({'foo': 'bar'});
600+
detector.watch(map, 'foo', null);
601+
602+
// The value is always dirty-checked the first time
603+
var iterator = detector.collectChanges();
604+
expect(iterator.moveNext()).toEqual(true);
605+
expect(iterator.current.currentValue).toEqual('bar');
606+
expect(iterator.moveNext()).toEqual(false);
607+
608+
// The value should not be dirty-checked again before a change notification is received
609+
map['foo'] = 'baz';
610+
iterator = detector.collectChanges();
611+
expect(iterator.moveNext()).toEqual(false);
612+
613+
// Trigger the ObservableMap notification, changes should be detected
614+
microLeap();
615+
iterator = detector.collectChanges();
616+
expect(iterator.moveNext()).toEqual(true);
617+
expect(iterator.current.currentValue).toEqual('baz');
618+
expect(iterator.moveNext()).toEqual(false);
619+
}));
597620
});
598621

599622
describe('function watching', () {
@@ -1008,6 +1031,26 @@ void addMapSpec({bool useObservable}) {
10081031

10091032
describe('use observable: $useObservable', () {
10101033
describe('previous state', () {
1034+
1035+
it('should detect map value changes', wrap(() {
1036+
var map = mapFactory({'foo': 'bar'});
1037+
detector.watch(map, 'foo', null);
1038+
1039+
var iterator = getChangeIterator();
1040+
expect(iterator.moveNext()).toEqual(true);
1041+
expect(iterator.current.currentValue).toEqual('bar');
1042+
expect(iterator.moveNext()).toEqual(false);
1043+
1044+
iterator = getChangeIterator();
1045+
expect(iterator.moveNext()).toEqual(false);
1046+
1047+
map['foo'] = 'baz';
1048+
iterator = getChangeIterator();
1049+
expect(iterator.moveNext()).toEqual(true);
1050+
expect(iterator.current.currentValue).toEqual('baz');
1051+
expect(iterator.moveNext()).toEqual(false);
1052+
}));
1053+
10111054
it('should store on insertion', wrap(() {
10121055
var map = mapFactory({});
10131056
var record = detector.watch(map, null, null);

0 commit comments

Comments
 (0)