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

Commit 0828f50

Browse files
tboschcaitp
authored andcommitted
feat(notifier): add proof of concept for custom notification
The templating engine wants to use a consistent mechanism for databinding throughout its internal implementation. To enable this we need a way to register for changes in certain HTML elements, rather than using dirty checking to observe them. This is a proof of concept for one way that might be plugged in. Closes #35
1 parent 86a4908 commit 0828f50

File tree

2 files changed

+74
-3
lines changed

2 files changed

+74
-3
lines changed

src/dirty_checking.js

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var _MODE_GETTER_ = 3;
2222
var _MODE_MAP_FIELD_ = 4;
2323
var _MODE_ITERABLE_ = 5;
2424
var _MODE_MAP_ = 6;
25+
var _MODE_MAP_FIELD_NOTIFY_ONLY_= 10;
2526
export class GetterCache {
2627
constructor(map) {
2728
this._map = map;
@@ -186,9 +187,10 @@ export class DirtyCheckingChangeDetectorGroup extends ChangeDetector {
186187
}
187188
}
188189
export class DirtyCheckingChangeDetector extends DirtyCheckingChangeDetectorGroup {
189-
constructor(cache) {
190+
constructor(cache, notifier = new ChangeNotifier()) {
190191
super(null, cache);
191192
this._fakeHead = DirtyCheckingRecord.marker();
193+
this._notifier = notifier;
192194
}
193195

194196
_assertRecordsOk() {
@@ -276,6 +278,47 @@ class ChangeIterator {
276278
}
277279
}
278280

281+
// TODO: Add unit tests for this!
282+
export class ChangeNotifier {
283+
constructor() {
284+
this.objectRecords = new WeakMap();
285+
}
286+
notify(object) {
287+
var records = this.objectRecords.get(object) || [];
288+
records.forEach((record) => {
289+
// TODO: Is this the correct way of manually dirty checking
290+
// some records??
291+
var watch = record._handler._watchHead;
292+
if (watch && record.check(true)) {
293+
watch._dirty = true;
294+
watch.invoke();
295+
}
296+
});
297+
}
298+
isNotifyOnly(object, fieldName) {
299+
return false;
300+
}
301+
addWatch(object, fieldName, record) {
302+
var records = this.objectRecords.get(object);
303+
if (!records) {
304+
records = [];
305+
this.objectRecords.set(object, records);
306+
}
307+
records.push(record);
308+
return records.length;
309+
}
310+
removeWatch(object, fieldName, record) {
311+
var records = this.objectRecords.get(object);
312+
if (records) {
313+
var index = records.indexOf(record);
314+
if (index !== -1) {
315+
records.splice(index, 1);
316+
}
317+
}
318+
return records ? records.length : 0;
319+
}
320+
}
321+
279322
class DirtyCheckingRecord extends ChangeRecord {
280323
constructor(group, object, fieldName, getter, handler) {
281324
this._group = group;
@@ -307,7 +350,17 @@ class DirtyCheckingRecord extends ChangeRecord {
307350
get object() {
308351
return this._object;
309352
}
353+
_clearObject() {
354+
var notifier = this._group && this._group._root._notifier;
355+
if (notifier && this._object && (
356+
this._mode === _MODE_MAP_FIELD_NOTIFY_ONLY_ || this._mode === _MODE_MAP_FIELD_)
357+
) {
358+
notifier.removeWatch(this._object, this._field, this);
359+
}
360+
this._object = null;
361+
}
310362
set object(obj) {
363+
this._clearObject(obj);
311364
this._object = obj;
312365
if (obj === null) {
313366
this._mode = _MODE_IDENTITY_;
@@ -333,7 +386,13 @@ class DirtyCheckingRecord extends ChangeRecord {
333386
return;
334387
}
335388
if (typeof obj === "object") {
336-
this._mode = _MODE_MAP_FIELD_;
389+
var notifier = this._group && this._group._root._notifier;
390+
if (notifier && notifier.isNotifyOnly(obj, this._field)) {
391+
this._mode = _MODE_MAP_FIELD_NOTIFY_ONLY_;
392+
} else {
393+
this._mode = _MODE_MAP_FIELD_;
394+
}
395+
notifier.addWatch(obj, this._field, this);
337396
// _instanceMirror = null; --- Reflection needed?
338397
} else if (this._getter !== null) {
339398
this._mode = _MODE_GETTER_;
@@ -343,7 +402,7 @@ class DirtyCheckingRecord extends ChangeRecord {
343402
// _instanceMirror = reflect(obj); --- I'm really not sure about this!
344403
}
345404
}
346-
check() {
405+
check(inNotify) {
347406
// assert(_mode != null); --- Traceur v0.0.24 missing assert()
348407
var current;
349408
switch (this._mode) {
@@ -359,6 +418,13 @@ class DirtyCheckingRecord extends ChangeRecord {
359418
case _MODE_GETTER_:
360419
current = this._getter(this.object);
361420
break;
421+
case _MODE_MAP_FIELD_NOTIFY_ONLY_:
422+
if (inNotify) {
423+
current = this.object[this.field];
424+
} else {
425+
return false;
426+
}
427+
break;
362428
case _MODE_MAP_FIELD_:
363429
if (!this.object) return undefined;
364430
current = this.object[this.field];
@@ -395,6 +461,9 @@ class DirtyCheckingRecord extends ChangeRecord {
395461
return false;
396462
}
397463
remove() {
464+
// TODO: This is not called when a WatchGroup is destroyed.
465+
// TODO: Should also be called when a parent WatchGroup is destroyed!
466+
this._clearObject();
398467
this._group._recordRemove(this);
399468
}
400469
toString() {

src/watch_group.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ export class WatchGroup {
265265
var astWR = putIfAbsent(this._cache, ast.expression, function() {
266266
return ast.setupWatch(that);
267267
});
268+
// TODO: Add tests for this line!
269+
astWR.handler.addForwardHandler(collectionHandler);
268270

269271
// propagate the value from the LHS to here
270272
collectionHandler.acceptValue(astWR.currentValue);

0 commit comments

Comments
 (0)