parent
38340ce8d9
commit
5527a1b1a4
|
@ -1,24 +0,0 @@
|
||||||
import {ProtoWatchGrou, WatchGroup} from './watch_group';
|
|
||||||
import {ProtoRecord, Record} from './record';
|
|
||||||
import {FIELD} from 'facade/lang';
|
|
||||||
export * from './record';
|
|
||||||
export * from './watch_group'
|
|
||||||
|
|
||||||
export class ChangeDetection {
|
|
||||||
|
|
||||||
@FIELD('final _rootWatchGroup:WatchGroup')
|
|
||||||
constructor(watchGroup:WatchGroup) {
|
|
||||||
this._rootWatchGroup = watchGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
detectChanges():int {
|
|
||||||
var current:Record = _rootWatchGroup.headRecord;
|
|
||||||
var count:number = 0;
|
|
||||||
while (current != null) {
|
|
||||||
if (current.check()) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import {ProtoWatchGroup, WatchGroup} from './watch_group';
|
||||||
|
import {ProtoRecord, Record} from './record';
|
||||||
|
import {FIELD, int} from 'facade/lang';
|
||||||
|
export * from './record';
|
||||||
|
export * from './watch_group'
|
||||||
|
|
||||||
|
export class ChangeDetector {
|
||||||
|
|
||||||
|
@FIELD('final _rootWatchGroup:WatchGroup')
|
||||||
|
constructor(watchGroup:WatchGroup) {
|
||||||
|
this._rootWatchGroup = watchGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
detectChanges():int {
|
||||||
|
var record:Record = this._rootWatchGroup.headRecord;
|
||||||
|
var count:int = 0;
|
||||||
|
for (record = this._rootWatchGroup.headRecord;
|
||||||
|
record != null;
|
||||||
|
record = record.checkNext) {
|
||||||
|
if (record.check()) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,14 @@
|
||||||
library change_detection.facade;
|
library change_detection.facade;
|
||||||
|
|
||||||
|
@MirrorsUsed(targets: const [FieldGetterFactory], metaTargets: const [] )
|
||||||
|
import 'dart:mirrors';
|
||||||
|
|
||||||
typedef SetterFn(Object obj, value);
|
typedef SetterFn(Object obj, value);
|
||||||
|
|
||||||
|
class FieldGetterFactory {
|
||||||
|
getter(Object object, String name) {
|
||||||
|
Symbol symbol = new Symbol(name);
|
||||||
|
InstanceMirror instanceMirror = reflect(object);
|
||||||
|
return (Object object) => instanceMirror.getField(symbol).reflectee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,7 @@
|
||||||
export var SetterFn = Function;
|
export var SetterFn = Function;
|
||||||
|
|
||||||
|
export class FieldGetterFactory {
|
||||||
|
getter(object, name:string) {
|
||||||
|
return new Function('o', 'return o["' + name + '"]');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,48 +1,28 @@
|
||||||
//import * as wg from './watch_group';
|
import {ProtoWatchGroup, WatchGroup} from './watch_group';
|
||||||
import {FIELD} from 'facade/lang';
|
import {FIELD} from 'facade/lang';
|
||||||
|
import {FieldGetterFactory} from './facade';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For now we are dropping expression coelescence. We can always add it later, but
|
* For now we are dropping expression coalescence. We can always add it later, but
|
||||||
* real world numbers should that it does not provide significant benefits.
|
* real world numbers show that it does not provide significant benefits.
|
||||||
*/
|
*/
|
||||||
export class ProtoRecord {
|
export class ProtoRecord {
|
||||||
|
|
||||||
@FIELD('final watchGroup:wg.ProtoWatchGroup')
|
@FIELD('final watchGroup:wg.ProtoWatchGroup')
|
||||||
@FIELD('final fieldName:String')
|
@FIELD('final fieldName:String')
|
||||||
/// order list of all records. Including head/tail markers
|
/// order list of all records. Including head/tail markers
|
||||||
@FIELD('next:ProtoRecord')
|
@FIELD('next:ProtoRecord')
|
||||||
@FIELD('prev:ProtoRecord')
|
@FIELD('prev:ProtoRecord')
|
||||||
// Opeque data which will be the target of notification.
|
// Opaque data which will be the target of notification.
|
||||||
// If the object is instance of Record, than it it is directly procssed
|
// If the object is instance of Record, than it it is directly processed
|
||||||
// Otherwise it is the context used by WatchGroupDispatcher.
|
// Otherwise it is the context used by WatchGroupDispatcher.
|
||||||
@FIELD('memento')
|
@FIELD('memento')
|
||||||
@FIELD('_clone')
|
constructor(watchGroup:ProtoWatchGroup, fieldName:string, dispatchMemento) {
|
||||||
constructor(watchGroup/*:wg.ProtoWatchGroup*/, fieldName:String, memento) {
|
|
||||||
this.watchGroup = watchGroup;
|
this.watchGroup = watchGroup;
|
||||||
this.fieldName = fieldName;
|
this.fieldName = fieldName;
|
||||||
this.memento = memento;
|
this.dispatchMemento = dispatchMemento;
|
||||||
this.next = null;
|
this.next = null;
|
||||||
this.prev = null;
|
this.prev = null;
|
||||||
this.changeNotifier = null;
|
|
||||||
this._clone = null;
|
|
||||||
this.changeContext = null;
|
|
||||||
this.dispatcherContext = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
instantiate(watchGroup/*:wg.WatchGroup*/):Record {
|
|
||||||
var record = this._clone = new Record(watchGroup, this);
|
|
||||||
record.prev = this.prev._clone;
|
|
||||||
record._checkPrev = this.prev._clone;
|
|
||||||
return _clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
instantiateComplete():Record {
|
|
||||||
var record = this._clone;
|
|
||||||
record.next = this.next._clone;
|
|
||||||
record._checkNext = this.next._clone;
|
|
||||||
this._clone = null;
|
|
||||||
return this.next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,11 +39,8 @@ export class ProtoRecord {
|
||||||
* - Atomic watch operations
|
* - Atomic watch operations
|
||||||
* - Defaults to dirty checking
|
* - Defaults to dirty checking
|
||||||
* - Keep this object as lean as possible. (Lean in number of fields)
|
* - Keep this object as lean as possible. (Lean in number of fields)
|
||||||
*
|
|
||||||
* MEMORY COST: 13 Words;
|
|
||||||
*/
|
*/
|
||||||
export class Record {
|
export class Record {
|
||||||
|
|
||||||
@FIELD('final watchGroup:WatchGroup')
|
@FIELD('final watchGroup:WatchGroup')
|
||||||
@FIELD('final protoRecord:ProtoRecord')
|
@FIELD('final protoRecord:ProtoRecord')
|
||||||
/// order list of all records. Including head/tail markers
|
/// order list of all records. Including head/tail markers
|
||||||
|
@ -79,64 +56,93 @@ export class Record {
|
||||||
@FIELD('_context')
|
@FIELD('_context')
|
||||||
@FIELD('_getter')
|
@FIELD('_getter')
|
||||||
@FIELD('_arguments')
|
@FIELD('_arguments')
|
||||||
@FIELD('currentValue')
|
|
||||||
@FIELD('previousValue')
|
@FIELD('previousValue')
|
||||||
constructor(watchGroup/*:wg.WatchGroup*/, protoRecord:ProtoRecord) {
|
constructor(watchGroup/*:wg.WatchGroup*/, protoRecord:ProtoRecord) {
|
||||||
this.protoRecord = protoRecord;
|
this.protoRecord = protoRecord;
|
||||||
this.watchGroup = watchGroup;
|
this.watchGroup = watchGroup;
|
||||||
this.next = null;
|
this.next = null;
|
||||||
this.prev = null;
|
this.prev = null;
|
||||||
this._checkNext = null;
|
this.checkNext = null;
|
||||||
this._checkPrev = null;
|
this.checkPrev = null;
|
||||||
this._notifierNext = null;
|
this.notifierNext = null;
|
||||||
|
|
||||||
this._mode = MODE_STATE_MARKER;
|
this.mode = MODE_STATE_MARKER;
|
||||||
this._context = null;
|
this.context = null;
|
||||||
this._getter = null;
|
this.getter = null;
|
||||||
this._arguments = null;
|
this.arguments = null;
|
||||||
this.currentValue = null;
|
|
||||||
this.previousValue = null;
|
this.previousValue = null;
|
||||||
|
this.currentValue = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
check():bool {
|
check():boolean {
|
||||||
var mode = this._mode;
|
var mode = this.mode;
|
||||||
var state = mode & MODE_MASK_STATE;
|
var state = mode & MODE_MASK_STATE;
|
||||||
var notify = mode & MODE_MASK_NOTIFY;
|
var notify = mode & MODE_MASK_NOTIFY;
|
||||||
var currentValue;
|
var newValue;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case MODE_STATE_MARKER:
|
case MODE_STATE_MARKER:
|
||||||
return false;
|
return false;
|
||||||
case MODE_STATE_PROPERTY:
|
case MODE_STATE_PROPERTY:
|
||||||
currentValue = this._getter(this._context);
|
newValue = this.getter(this.context);
|
||||||
break;
|
break;
|
||||||
case MODE_STATE_INVOKE_CLOSURE:
|
case MODE_STATE_INVOKE_CLOSURE:
|
||||||
currentValue = this._context(this._arguments);
|
newValue = this.context(this.arguments);
|
||||||
break;
|
break;
|
||||||
case MODE_STATE_INVOKE_METHOD:
|
case MODE_STATE_INVOKE_METHOD:
|
||||||
currentValue = this._getter(this._context, this._arguments);
|
newValue = this.getter(this.context, this.arguments);
|
||||||
break;
|
break;
|
||||||
case MODE_STATE_MAP:
|
case MODE_STATE_MAP:
|
||||||
|
throw 'not implemented';
|
||||||
case MODE_STATE_LIST:
|
case MODE_STATE_LIST:
|
||||||
|
throw 'not implemented';
|
||||||
|
default:
|
||||||
|
throw 'not implemented';
|
||||||
}
|
}
|
||||||
var previousValue = this.previousValue;
|
|
||||||
if (isSame(previousValue, currentValue)) return false;
|
|
||||||
if (previousValue instanceof String && currentValue instanceof String
|
var previousValue = this.currentValue;
|
||||||
&& previousValue == currentValue) {
|
if (previousValue === this) {
|
||||||
this.previousValue = currentValue;
|
// When the record is checked for the first time we should always notify
|
||||||
return false
|
this.currentValue = newValue;
|
||||||
}
|
this.previousValue = previousValue = null;
|
||||||
this.previousValue = currentValue;
|
|
||||||
if (this.protoRecord.changeContext instanceof ProtoRecord) {
|
|
||||||
// forward propaget to the next record
|
|
||||||
} else {
|
} else {
|
||||||
// notify throught dispatcher
|
this.currentValue = newValue;
|
||||||
this.watchGroup.dispatcher.onRecordChange(this, this.protoRecord.dispatcherContext);
|
this.previousValue = previousValue;
|
||||||
|
|
||||||
|
if (isSame(previousValue, newValue)) return false;
|
||||||
|
|
||||||
|
// In Dart, we can have `str1 !== str2` but `str1 == str2`
|
||||||
|
if (previousValue instanceof String &&
|
||||||
|
newValue instanceof String &&
|
||||||
|
previousValue == newValue) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// todo(vicb): compute this info only once in ctor ? (add a bit in mode not to grow the mem req)
|
||||||
|
if (this.protoRecord.dispatchMemento === null) {
|
||||||
|
// forward propagate to the next record
|
||||||
|
} else {
|
||||||
|
// notify through dispatcher
|
||||||
|
this.watchGroup.dispatcher.onRecordChange(this, this.protoRecord.dispatchMemento);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setContext(context) {
|
||||||
|
// use `this` as a marker for a fresh record
|
||||||
|
this.currentValue = this;
|
||||||
|
this.mode = MODE_STATE_PROPERTY;
|
||||||
|
this.context = context;
|
||||||
|
var factory = new FieldGetterFactory();
|
||||||
|
this.getter = factory.getter(context, this.protoRecord.fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The mode is devided into two partes. Which notification mechanism
|
// The mode is divided into two parts. Which notification mechanism
|
||||||
// to use and which dereference mode to execute.
|
// to use and which dereference mode to execute.
|
||||||
|
|
||||||
// We use dirty checking aka no notification
|
// We use dirty checking aka no notification
|
||||||
|
@ -160,11 +166,7 @@ const MODE_STATE_MAP = 0x0004;
|
||||||
const MODE_STATE_LIST = 0x0005;
|
const MODE_STATE_LIST = 0x0005;
|
||||||
|
|
||||||
function isSame(a, b) {
|
function isSame(a, b) {
|
||||||
if (a === b) {
|
if (a === b) return true;
|
||||||
return true;
|
if ((a !== a) && (b !== b)) return true;
|
||||||
} else if ((a !== a) && (b !== b)) {
|
return false;
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,38 +13,46 @@ export class ProtoWatchGroup {
|
||||||
* Parses [expression] into [ProtoRecord]s and adds them to [ProtoWatchGroup].
|
* Parses [expression] into [ProtoRecord]s and adds them to [ProtoWatchGroup].
|
||||||
*
|
*
|
||||||
* @param expression The expression to watch
|
* @param expression The expression to watch
|
||||||
* @param memento an opeque object which will be bassed to WatchGroupDispatcher on
|
* @param memento an opaque object which will be passed to WatchGroupDispatcher on
|
||||||
* detecting a change.
|
* detecting a change.
|
||||||
* @param shallow Should collections be shallow watched
|
* @param shallow Should collections be shallow watched
|
||||||
*/
|
*/
|
||||||
watch(
|
watch(expression:string,
|
||||||
expression:String,
|
memento,
|
||||||
memento,
|
shallow /*= false*/) // TODO(vicb): comment out when opt-params are supported
|
||||||
{shallow/*=false*/}:{shallow:bool})
|
|
||||||
{
|
{
|
||||||
/// IMPLEMENT
|
var protoRecord = new ProtoRecord(this, expression, memento);
|
||||||
|
|
||||||
|
if (this.headRecord === null) {
|
||||||
|
this.headRecord = this.tailRecord = protoRecord;
|
||||||
|
} else {
|
||||||
|
this.tailRecord.next = protoRecord;
|
||||||
|
protoRecord.prev = this.tailRecord;
|
||||||
|
this.tailRecord = protoRecord;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instantiate(dispatcher:WatchGroupDispatcher):WatchGroup {
|
instantiate(dispatcher:WatchGroupDispatcher):WatchGroup {
|
||||||
var watchGroup:WatchGroup = new WatchGroup(this, dispatcher);
|
var watchGroup:WatchGroup = new WatchGroup(this, dispatcher);
|
||||||
var head:Record = null;
|
|
||||||
var tail:Record = null;
|
var tail:Record = null;
|
||||||
var proto:ProtoRecord = this.headRecord;
|
var proto:ProtoRecord;
|
||||||
|
var prevRecord:Record = null;
|
||||||
|
|
||||||
while(proto != null) {
|
if (this.headRecord !== null) {
|
||||||
tail = proto.instantiate(watchGroup);
|
watchGroup.headRecord = tail = new Record(watchGroup, this.headRecord);
|
||||||
if (head == null) head = tail;
|
|
||||||
proto = proto.next;
|
for (proto = this.headRecord.next; proto != null; proto = proto.next) {
|
||||||
|
prevRecord = tail;
|
||||||
|
tail = new Record(watchGroup, proto);
|
||||||
|
tail.prev = prevRecord;
|
||||||
|
prevRecord.next = tail;
|
||||||
|
tail.checkPrev = prevRecord;
|
||||||
|
prevRecord.checkNext = tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
watchGroup.tailRecord = tail;
|
||||||
}
|
}
|
||||||
|
|
||||||
proto = this.headRecord;
|
|
||||||
while(proto != null) {
|
|
||||||
proto.instantiateComplete();
|
|
||||||
proto = proto.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
watchGroup.headRecord = head;
|
|
||||||
watchGroup.tailRecord = tail;
|
|
||||||
return watchGroup;
|
return watchGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,14 +68,15 @@ export class WatchGroup {
|
||||||
this.dispatcher = dispatcher;
|
this.dispatcher = dispatcher;
|
||||||
this.headRecord = null;
|
this.headRecord = null;
|
||||||
this.tailRecord = null;
|
this.tailRecord = null;
|
||||||
|
this.context = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
insertChildGroup(newChild:WatchGroup, insertAfter:WatchGroup) {
|
insertChildGroup(newChild:WatchGroup, insertAfter:WatchGroup) {
|
||||||
/// IMPLEMENT
|
throw 'not implemented';
|
||||||
}
|
}
|
||||||
|
|
||||||
remove() {
|
remove() {
|
||||||
/// IMPLEMENT
|
throw 'not implemented';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,12 +84,18 @@ export class WatchGroup {
|
||||||
* dereference themselves on. Since the WatchGroup can be reused the context
|
* dereference themselves on. Since the WatchGroup can be reused the context
|
||||||
* can be re-set many times during the lifetime of the WatchGroup.
|
* can be re-set many times during the lifetime of the WatchGroup.
|
||||||
*
|
*
|
||||||
* @param context the new context for change dection for the curren WatchGroup
|
* @param context the new context for change detection for the current WatchGroup
|
||||||
*/
|
*/
|
||||||
setContext(context) {
|
setContext(context) {
|
||||||
|
for (var record:Record = this.headRecord;
|
||||||
|
record != null;
|
||||||
|
record = record.next) {
|
||||||
|
record.setContext(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WatchGroupDispatcher {
|
export class WatchGroupDispatcher {
|
||||||
|
// The record holds the previous value at the time of the call
|
||||||
onRecordChange(record:Record, context) {}
|
onRecordChange(record:Record, context) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
import {describe, it, xit, expect} from 'test_lib/test_lib';
|
|
||||||
import {ProtoWatchGroup, WatchGroup, WatchGroupDispatcher, ChangeDetection} from 'change_detection/change_detection';
|
|
||||||
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('change_detection', function() {
|
|
||||||
describe('ChangeDetection', function() {
|
|
||||||
xit('should do simple watching', function() {
|
|
||||||
var person = new Person('misko', 38);
|
|
||||||
var pwg = new ProtoWatchGroup();
|
|
||||||
pwg.watch('name', 'nameToken');
|
|
||||||
pwg.watch('age', 'ageToken');
|
|
||||||
var dispatcher = new LoggingDispatcher();
|
|
||||||
var wg = pwg.instantiate(dispatcher);
|
|
||||||
wg.setContext(person);
|
|
||||||
var cd = new ChangeDetection(wg);
|
|
||||||
cd.detectChanges();
|
|
||||||
expect(dispatcher.log).toEqual(['ageToken=38']);
|
|
||||||
dispatcher.clear();
|
|
||||||
cd.detectChanges();
|
|
||||||
expect(dispatcher.log).toEqual([]);
|
|
||||||
person.age=1;
|
|
||||||
person.name="Misko";
|
|
||||||
cd.detectChanges();
|
|
||||||
expect(dispatcher.log).toEqual(['nameToken=Misko', 'ageToken=1']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class Person {
|
|
||||||
constructor(name:string, age:number) {
|
|
||||||
this.name = name;
|
|
||||||
this.age = age;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoggingDispatcher extends WatchGroupDispatcher {
|
|
||||||
constructor() {
|
|
||||||
this.log = null;
|
|
||||||
}
|
|
||||||
clear() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import {describe, it, xit, expect} from 'test_lib/test_lib';
|
||||||
|
|
||||||
|
import {List, ListWrapper} from 'facade/collection';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ChangeDetector,
|
||||||
|
ProtoWatchGroup,
|
||||||
|
WatchGroup,
|
||||||
|
WatchGroupDispatcher
|
||||||
|
} from 'change_detection/change_detector';
|
||||||
|
|
||||||
|
import {Record} from 'change_detection/record';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('change_detection', function() {
|
||||||
|
describe('ChangeDetection', function() {
|
||||||
|
it('should do simple watching', function() {
|
||||||
|
var person = new Person('misko', 38);
|
||||||
|
var pwg = new ProtoWatchGroup();
|
||||||
|
pwg.watch('name', 'name', false); // TODO(vicb): remove opt shallow when supported
|
||||||
|
pwg.watch('age', 'age', false);
|
||||||
|
var dispatcher = new LoggingDispatcher();
|
||||||
|
var wg = pwg.instantiate(dispatcher);
|
||||||
|
wg.setContext(person);
|
||||||
|
var cd = new ChangeDetector(wg);
|
||||||
|
cd.detectChanges();
|
||||||
|
expect(dispatcher.log).toEqual(['name=misko', 'age=38']);
|
||||||
|
dispatcher.clear();
|
||||||
|
cd.detectChanges();
|
||||||
|
expect(dispatcher.log).toEqual([]);
|
||||||
|
person.age = 1;
|
||||||
|
person.name = "Misko";
|
||||||
|
cd.detectChanges();
|
||||||
|
expect(dispatcher.log).toEqual(['name=Misko', 'age=1']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class Person {
|
||||||
|
constructor(name:string, age:number) {
|
||||||
|
this.name = name;
|
||||||
|
this.age = age;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
return 'name=' + this.name + ' age=' + this.age.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoggingDispatcher extends WatchGroupDispatcher {
|
||||||
|
constructor() {
|
||||||
|
this.log = null;
|
||||||
|
this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.log = ListWrapper.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecordChange(record:Record, context) {
|
||||||
|
ListWrapper.push(this.log, context + '=' + record.currentValue.toString());
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ export * from './annotations/directive';
|
||||||
export * from './annotations/component';
|
export * from './annotations/component';
|
||||||
export * from './annotations/template_config';
|
export * from './annotations/template_config';
|
||||||
|
|
||||||
export * from 'change_detection/change_detection';
|
export * from 'change_detection/change_detector';
|
||||||
export * from 'change_detection/watch_group';
|
export * from 'change_detection/watch_group';
|
||||||
export * from 'change_detection/record';
|
export * from 'change_detection/record';
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue