feat(ChangeDetector): Add support for chained properties
This commit is contained in:
parent
63494a74bf
commit
c90a7114d3
|
@ -44,19 +44,19 @@ export class Record {
|
|||
@FIELD('final watchGroup:WatchGroup')
|
||||
@FIELD('final protoRecord:ProtoRecord')
|
||||
/// order list of all records. Including head/tail markers
|
||||
@FIELD('_next:Record')
|
||||
@FIELD('_prev:Record')
|
||||
@FIELD('next:Record')
|
||||
@FIELD('prev:Record')
|
||||
/// next record to dirty check
|
||||
@FIELD('_checkNext:Record')
|
||||
@FIELD('_checkPrev:Record')
|
||||
@FIELD('checkNext:Record')
|
||||
@FIELD('checkPrev:Record')
|
||||
// next notifier
|
||||
@FIELD('_notifierNext:Record')
|
||||
@FIELD('notifierNext:Record')
|
||||
|
||||
@FIELD('_mode:int')
|
||||
@FIELD('_context')
|
||||
@FIELD('_getter')
|
||||
@FIELD('_arguments')
|
||||
@FIELD('mode:int')
|
||||
@FIELD('context')
|
||||
@FIELD('getter')
|
||||
@FIELD('previousValue')
|
||||
@FIELD('currentValue')
|
||||
constructor(watchGroup/*:wg.WatchGroup*/, protoRecord:ProtoRecord) {
|
||||
this.protoRecord = protoRecord;
|
||||
this.watchGroup = watchGroup;
|
||||
|
@ -71,7 +71,8 @@ export class Record {
|
|||
this.getter = null;
|
||||
this.arguments = null;
|
||||
this.previousValue = null;
|
||||
this.currentValue = null;
|
||||
// `this` means that the record is fresh
|
||||
this.currentValue = this;
|
||||
}
|
||||
|
||||
check():boolean {
|
||||
|
@ -119,10 +120,9 @@ export class Record {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
this.next.setContext(this.currentValue);
|
||||
} else {
|
||||
// notify through dispatcher
|
||||
this.watchGroup.dispatcher.onRecordChange(this, this.protoRecord.dispatchMemento);
|
||||
|
@ -132,8 +132,6 @@ export class Record {
|
|||
}
|
||||
|
||||
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();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {ProtoRecord, Record} from './record';
|
||||
import {FIELD} from 'facade/lang';
|
||||
import {ListWrapper} from 'facade/collection';
|
||||
|
||||
export class ProtoWatchGroup {
|
||||
@FIELD('final headRecord:ProtoRecord')
|
||||
|
@ -19,10 +20,18 @@ export class ProtoWatchGroup {
|
|||
*/
|
||||
watch(expression:string,
|
||||
memento,
|
||||
shallow /*= false*/) // TODO(vicb): comment out when opt-params are supported
|
||||
shallow = false)
|
||||
{
|
||||
var protoRecord = new ProtoRecord(this, expression, memento);
|
||||
var parts = expression.split('.');
|
||||
var protoRecords = ListWrapper.createFixedSize(parts.length);
|
||||
|
||||
for (var i = parts.length - 1; i >= 0; i--) {
|
||||
protoRecords[i] = new ProtoRecord(this, parts[i], memento);
|
||||
memento = null;
|
||||
}
|
||||
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var protoRecord = protoRecords[i];
|
||||
if (this.headRecord === null) {
|
||||
this.headRecord = this.tailRecord = protoRecord;
|
||||
} else {
|
||||
|
@ -31,6 +40,7 @@ export class ProtoWatchGroup {
|
|||
this.tailRecord = protoRecord;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instantiate(dispatcher:WatchGroupDispatcher):WatchGroup {
|
||||
var watchGroup:WatchGroup = new WatchGroup(this, dispatcher);
|
||||
|
|
|
@ -17,8 +17,8 @@ export function main() {
|
|||
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);
|
||||
pwg.watch('name', 'name');
|
||||
pwg.watch('age', 'age');
|
||||
var dispatcher = new LoggingDispatcher();
|
||||
var wg = pwg.instantiate(dispatcher);
|
||||
wg.setContext(person);
|
||||
|
@ -33,18 +33,53 @@ export function main() {
|
|||
cd.detectChanges();
|
||||
expect(dispatcher.log).toEqual(['name=Misko', 'age=1']);
|
||||
});
|
||||
|
||||
it('should watch chained properties', function() {
|
||||
var address = new Address('Grenoble');
|
||||
var person = new Person('Victor', 36, address);
|
||||
var pwg = new ProtoWatchGroup();
|
||||
pwg.watch('address.city', 'address.city', false);
|
||||
var dispatcher = new LoggingDispatcher();
|
||||
var wg = pwg.instantiate(dispatcher);
|
||||
wg.setContext(person);
|
||||
var cd = new ChangeDetector(wg);
|
||||
cd.detectChanges();
|
||||
expect(dispatcher.log).toEqual(['address.city=Grenoble']);
|
||||
dispatcher.clear();
|
||||
cd.detectChanges();
|
||||
expect(dispatcher.log).toEqual([]);
|
||||
address.city = 'Mountain View';
|
||||
cd.detectChanges();
|
||||
expect(dispatcher.log).toEqual(['address.city=Mountain View']);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class Person {
|
||||
constructor(name:string, age:number) {
|
||||
constructor(name:string, age:number, address:Address = null) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return 'name=' + this.name + ' age=' + this.age.toString();
|
||||
toString():string {
|
||||
var address = this.address == null ? '' : ' address=' + this.address.toString();
|
||||
|
||||
return 'name=' + this.name +
|
||||
' age=' + this.age.toString() +
|
||||
address;
|
||||
}
|
||||
}
|
||||
|
||||
class Address {
|
||||
constructor(city:string) {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
toString():string {
|
||||
return this.city;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
name: facade
|
||||
environment:
|
||||
sdk: '>=1.4.0'
|
||||
dependencies:
|
||||
dev_dependencies:
|
||||
test_lib:
|
||||
path: ../test_lib
|
||||
|
|
Loading…
Reference in New Issue