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,16 +20,25 @@ 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); | ||||||
| 
 | 
 | ||||||
|     if (this.headRecord === null) { |     for (var i = parts.length - 1; i >= 0; i--) { | ||||||
|       this.headRecord = this.tailRecord = protoRecord; |       protoRecords[i] = new ProtoRecord(this, parts[i], memento); | ||||||
|     } else { |       memento = null; | ||||||
|       this.tailRecord.next = protoRecord; |     } | ||||||
|       protoRecord.prev = this.tailRecord; | 
 | ||||||
|       this.tailRecord = protoRecord; |     for (var i = 0; i < parts.length; i++) { | ||||||
|  |       var protoRecord = protoRecords[i]; | ||||||
|  |       if (this.headRecord === null) { | ||||||
|  |         this.headRecord = this.tailRecord = protoRecord; | ||||||
|  |       } else { | ||||||
|  |         this.tailRecord.next = protoRecord; | ||||||
|  |         protoRecord.prev = this.tailRecord; | ||||||
|  |         this.tailRecord = protoRecord; | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -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