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 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
|
||||||
@FIELD('_next:Record')
|
@FIELD('next:Record')
|
||||||
@FIELD('_prev:Record')
|
@FIELD('prev:Record')
|
||||||
/// next record to dirty check
|
/// next record to dirty check
|
||||||
@FIELD('_checkNext:Record')
|
@FIELD('checkNext:Record')
|
||||||
@FIELD('_checkPrev:Record')
|
@FIELD('checkPrev:Record')
|
||||||
// next notifier
|
// next notifier
|
||||||
@FIELD('_notifierNext:Record')
|
@FIELD('notifierNext:Record')
|
||||||
|
|
||||||
@FIELD('_mode:int')
|
@FIELD('mode:int')
|
||||||
@FIELD('_context')
|
@FIELD('context')
|
||||||
@FIELD('_getter')
|
@FIELD('getter')
|
||||||
@FIELD('_arguments')
|
|
||||||
@FIELD('previousValue')
|
@FIELD('previousValue')
|
||||||
|
@FIELD('currentValue')
|
||||||
constructor(watchGroup/*:wg.WatchGroup*/, protoRecord:ProtoRecord) {
|
constructor(watchGroup/*:wg.WatchGroup*/, protoRecord:ProtoRecord) {
|
||||||
this.protoRecord = protoRecord;
|
this.protoRecord = protoRecord;
|
||||||
this.watchGroup = watchGroup;
|
this.watchGroup = watchGroup;
|
||||||
@ -71,7 +71,8 @@ export class Record {
|
|||||||
this.getter = null;
|
this.getter = null;
|
||||||
this.arguments = null;
|
this.arguments = null;
|
||||||
this.previousValue = null;
|
this.previousValue = null;
|
||||||
this.currentValue = null;
|
// `this` means that the record is fresh
|
||||||
|
this.currentValue = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
check():boolean {
|
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)
|
// 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) {
|
if (this.protoRecord.dispatchMemento === null) {
|
||||||
// forward propagate to the next record
|
this.next.setContext(this.currentValue);
|
||||||
} else {
|
} else {
|
||||||
// notify through dispatcher
|
// notify through dispatcher
|
||||||
this.watchGroup.dispatcher.onRecordChange(this, this.protoRecord.dispatchMemento);
|
this.watchGroup.dispatcher.onRecordChange(this, this.protoRecord.dispatchMemento);
|
||||||
@ -132,8 +132,6 @@ export class Record {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setContext(context) {
|
setContext(context) {
|
||||||
// use `this` as a marker for a fresh record
|
|
||||||
this.currentValue = this;
|
|
||||||
this.mode = MODE_STATE_PROPERTY;
|
this.mode = MODE_STATE_PROPERTY;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
var factory = new FieldGetterFactory();
|
var factory = new FieldGetterFactory();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {ProtoRecord, Record} from './record';
|
import {ProtoRecord, Record} from './record';
|
||||||
import {FIELD} from 'facade/lang';
|
import {FIELD} from 'facade/lang';
|
||||||
|
import {ListWrapper} from 'facade/collection';
|
||||||
|
|
||||||
export class ProtoWatchGroup {
|
export class ProtoWatchGroup {
|
||||||
@FIELD('final headRecord:ProtoRecord')
|
@FIELD('final headRecord:ProtoRecord')
|
||||||
@ -19,10 +20,18 @@ export class ProtoWatchGroup {
|
|||||||
*/
|
*/
|
||||||
watch(expression:string,
|
watch(expression:string,
|
||||||
memento,
|
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) {
|
if (this.headRecord === null) {
|
||||||
this.headRecord = this.tailRecord = protoRecord;
|
this.headRecord = this.tailRecord = protoRecord;
|
||||||
} else {
|
} else {
|
||||||
@ -31,6 +40,7 @@ export class ProtoWatchGroup {
|
|||||||
this.tailRecord = protoRecord;
|
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);
|
||||||
|
@ -17,8 +17,8 @@ export function main() {
|
|||||||
it('should do simple watching', function() {
|
it('should do simple watching', function() {
|
||||||
var person = new Person('misko', 38);
|
var person = new Person('misko', 38);
|
||||||
var pwg = new ProtoWatchGroup();
|
var pwg = new ProtoWatchGroup();
|
||||||
pwg.watch('name', 'name', false); // TODO(vicb): remove opt shallow when supported
|
pwg.watch('name', 'name');
|
||||||
pwg.watch('age', 'age', false);
|
pwg.watch('age', 'age');
|
||||||
var dispatcher = new LoggingDispatcher();
|
var dispatcher = new LoggingDispatcher();
|
||||||
var wg = pwg.instantiate(dispatcher);
|
var wg = pwg.instantiate(dispatcher);
|
||||||
wg.setContext(person);
|
wg.setContext(person);
|
||||||
@ -33,18 +33,53 @@ export function main() {
|
|||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
expect(dispatcher.log).toEqual(['name=Misko', 'age=1']);
|
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 {
|
class Person {
|
||||||
constructor(name:string, age:number) {
|
constructor(name:string, age:number, address:Address = null) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.age = age;
|
this.age = age;
|
||||||
|
this.address = address;
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString():string {
|
||||||
return 'name=' + this.name + ' age=' + this.age.toString();
|
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
|
name: facade
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=1.4.0'
|
sdk: '>=1.4.0'
|
||||||
dependencies:
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
test_lib:
|
test_lib:
|
||||||
path: ../test_lib
|
path: ../test_lib
|
||||||
|
Loading…
x
Reference in New Issue
Block a user