11import { CONST } from 'angular2/src/facade/lang' ;
22import { BaseException } from 'angular2/src/facade/exceptions' ;
3- import {
4- isListLikeIterable ,
5- iterateListLike ,
6- ListWrapper ,
7- MapWrapper
8- } from 'angular2/src/facade/collection' ;
3+ import { isListLikeIterable , iterateListLike , ListWrapper } from 'angular2/src/facade/collection' ;
94
105import {
116 isBlank ,
@@ -17,17 +12,21 @@ import {
1712} from 'angular2/src/facade/lang' ;
1813
1914import { ChangeDetectorRef } from '../change_detector_ref' ;
20- import { IterableDiffer , IterableDifferFactory } from '../differs/iterable_differs' ;
15+ import { IterableDiffer , IterableDifferFactory , TrackByFn } from '../differs/iterable_differs' ;
2116
2217@CONST ( )
2318export class DefaultIterableDifferFactory implements IterableDifferFactory {
2419 supports ( obj : Object ) : boolean { return isListLikeIterable ( obj ) ; }
25- create ( cdRef : ChangeDetectorRef ) : DefaultIterableDiffer { return new DefaultIterableDiffer ( ) ; }
20+ create ( cdRef : ChangeDetectorRef , trackByFn ?: TrackByFn ) : DefaultIterableDiffer {
21+ return new DefaultIterableDiffer ( trackByFn ) ;
22+ }
2623}
2724
25+ var trackByIdentity = ( index : number , item : any ) => item ;
26+
2827export class DefaultIterableDiffer implements IterableDiffer {
29- private _collection = null ;
3028 private _length : number = null ;
29+ private _collection = null ;
3130 // Keeps track of the used records at any point in time (during & across `_check()` calls)
3231 private _linkedRecords : _DuplicateMap = null ;
3332 // Keeps track of the removed records at any point in time during `_check()` calls.
@@ -42,6 +41,10 @@ export class DefaultIterableDiffer implements IterableDiffer {
4241 private _removalsHead : CollectionChangeRecord = null ;
4342 private _removalsTail : CollectionChangeRecord = null ;
4443
44+ constructor ( private _trackByFn ?: TrackByFn ) {
45+ this . _trackByFn = isPresent ( this . _trackByFn ) ? this . _trackByFn : trackByIdentity ;
46+ }
47+
4548 get collection ( ) { return this . _collection ; }
4649
4750 get length ( ) : number { return this . _length ; }
@@ -104,31 +107,37 @@ export class DefaultIterableDiffer implements IterableDiffer {
104107 var mayBeDirty : boolean = false ;
105108 var index : number ;
106109 var item ;
107-
110+ var itemTrackBy ;
108111 if ( isArray ( collection ) ) {
109112 var list = collection ;
110113 this . _length = collection . length ;
111114
112115 for ( index = 0 ; index < this . _length ; index ++ ) {
113116 item = list [ index ] ;
114- if ( record === null || ! looseIdentical ( record . item , item ) ) {
115- record = this . _mismatch ( record , item , index ) ;
117+ itemTrackBy = this . _trackByFn ( index , item ) ;
118+ if ( record === null || ! looseIdentical ( record . trackById , itemTrackBy ) ) {
119+ record = this . _mismatch ( record , item , itemTrackBy , index ) ;
116120 mayBeDirty = true ;
117- } else if ( mayBeDirty ) {
118- // TODO(misko): can we limit this to duplicates only?
119- record = this . _verifyReinsertion ( record , item , index ) ;
121+ } else {
122+ if ( mayBeDirty ) {
123+ // TODO(misko): can we limit this to duplicates only?
124+ record = this . _verifyReinsertion ( record , item , itemTrackBy , index ) ;
125+ }
126+ record . item = item ;
120127 }
128+
121129 record = record . _next ;
122130 }
123131 } else {
124132 index = 0 ;
125133 iterateListLike ( collection , ( item ) => {
126- if ( record === null || ! looseIdentical ( record . item , item ) ) {
127- record = this . _mismatch ( record , item , index ) ;
134+ itemTrackBy = this . _trackByFn ( index , item ) ;
135+ if ( record === null || ! looseIdentical ( record . trackById , itemTrackBy ) ) {
136+ record = this . _mismatch ( record , item , itemTrackBy , index ) ;
128137 mayBeDirty = true ;
129138 } else if ( mayBeDirty ) {
130139 // TODO(misko): can we limit this to duplicates only?
131- record = this . _verifyReinsertion ( record , item , index ) ;
140+ record = this . _verifyReinsertion ( record , item , itemTrackBy , index ) ;
132141 }
133142 record = record . _next ;
134143 index ++ ;
@@ -190,7 +199,8 @@ export class DefaultIterableDiffer implements IterableDiffer {
190199 *
191200 * @internal
192201 */
193- _mismatch ( record : CollectionChangeRecord , item , index : number ) : CollectionChangeRecord {
202+ _mismatch ( record : CollectionChangeRecord , item : any , itemTrackBy : any ,
203+ index : number ) : CollectionChangeRecord {
194204 // The previous record after which we will append the current one.
195205 var previousRecord : CollectionChangeRecord ;
196206
@@ -203,19 +213,20 @@ export class DefaultIterableDiffer implements IterableDiffer {
203213 }
204214
205215 // Attempt to see if we have seen the item before.
206- record = this . _linkedRecords === null ? null : this . _linkedRecords . get ( item , index ) ;
216+ record = this . _linkedRecords === null ? null : this . _linkedRecords . get ( itemTrackBy , index ) ;
207217 if ( record !== null ) {
208218 // We have seen this before, we need to move it forward in the collection.
209219 this . _moveAfter ( record , previousRecord , index ) ;
210220 } else {
211221 // Never seen it, check evicted list.
212- record = this . _unlinkedRecords === null ? null : this . _unlinkedRecords . get ( item ) ;
222+ record = this . _unlinkedRecords === null ? null : this . _unlinkedRecords . get ( itemTrackBy ) ;
213223 if ( record !== null ) {
214224 // It is an item which we have evicted earlier: reinsert it back into the list.
215225 this . _reinsertAfter ( record , previousRecord , index ) ;
216226 } else {
217227 // It is a new item: add it.
218- record = this . _addAfter ( new CollectionChangeRecord ( item ) , previousRecord , index ) ;
228+ record =
229+ this . _addAfter ( new CollectionChangeRecord ( item , itemTrackBy ) , previousRecord , index ) ;
219230 }
220231 }
221232 return record ;
@@ -248,15 +259,17 @@ export class DefaultIterableDiffer implements IterableDiffer {
248259 *
249260 * @internal
250261 */
251- _verifyReinsertion ( record : CollectionChangeRecord , item , index : number ) : CollectionChangeRecord {
262+ _verifyReinsertion ( record : CollectionChangeRecord , item : any , itemTrackBy : any ,
263+ index : number ) : CollectionChangeRecord {
252264 var reinsertRecord : CollectionChangeRecord =
253- this . _unlinkedRecords === null ? null : this . _unlinkedRecords . get ( item ) ;
265+ this . _unlinkedRecords === null ? null : this . _unlinkedRecords . get ( itemTrackBy ) ;
254266 if ( reinsertRecord !== null ) {
255267 record = this . _reinsertAfter ( reinsertRecord , record . _prev , index ) ;
256268 } else if ( record . currentIndex != index ) {
257269 record . currentIndex = index ;
258270 this . _addToMoves ( record , index ) ;
259271 }
272+ record . item = item ;
260273 return record ;
261274 }
262275
@@ -457,31 +470,20 @@ export class DefaultIterableDiffer implements IterableDiffer {
457470 }
458471
459472 toString ( ) : string {
460- var record : CollectionChangeRecord ;
461-
462473 var list = [ ] ;
463- for ( record = this . _itHead ; record !== null ; record = record . _next ) {
464- list . push ( record ) ;
465- }
474+ this . forEachItem ( ( record ) => list . push ( record ) ) ;
466475
467476 var previous = [ ] ;
468- for ( record = this . _previousItHead ; record !== null ; record = record . _nextPrevious ) {
469- previous . push ( record ) ;
470- }
477+ this . forEachPreviousItem ( ( record ) => previous . push ( record ) ) ;
471478
472479 var additions = [ ] ;
473- for ( record = this . _additionsHead ; record !== null ; record = record . _nextAdded ) {
474- additions . push ( record ) ;
475- }
480+ this . forEachAddedItem ( ( record ) => additions . push ( record ) ) ;
481+
476482 var moves = [ ] ;
477- for ( record = this . _movesHead ; record !== null ; record = record . _nextMoved ) {
478- moves . push ( record ) ;
479- }
483+ this . forEachMovedItem ( ( record ) => moves . push ( record ) ) ;
480484
481485 var removals = [ ] ;
482- for ( record = this . _removalsHead ; record !== null ; record = record . _nextRemoved ) {
483- removals . push ( record ) ;
484- }
486+ this . forEachRemovedItem ( ( record ) => removals . push ( record ) ) ;
485487
486488 return "collection: " + list . join ( ', ' ) + "\n" + "previous: " + previous . join ( ', ' ) + "\n" +
487489 "additions: " + additions . join ( ', ' ) + "\n" + "moves: " + moves . join ( ', ' ) + "\n" +
@@ -512,7 +514,7 @@ export class CollectionChangeRecord {
512514 /** @internal */
513515 _nextMoved : CollectionChangeRecord = null ;
514516
515- constructor ( public item : any ) { }
517+ constructor ( public item : any , public trackById : any ) { }
516518
517519 toString ( ) : string {
518520 return this . previousIndex === this . currentIndex ?
@@ -550,13 +552,13 @@ class _DuplicateItemRecordList {
550552 }
551553 }
552554
553- // Returns a CollectionChangeRecord having CollectionChangeRecord.item == item and
555+ // Returns a CollectionChangeRecord having CollectionChangeRecord.trackById == trackById and
554556 // CollectionChangeRecord.currentIndex >= afterIndex
555- get ( item : any , afterIndex : number ) : CollectionChangeRecord {
557+ get ( trackById : any , afterIndex : number ) : CollectionChangeRecord {
556558 var record : CollectionChangeRecord ;
557559 for ( record = this . _head ; record !== null ; record = record . _nextDup ) {
558560 if ( ( afterIndex === null || afterIndex < record . currentIndex ) &&
559- looseIdentical ( record . item , item ) ) {
561+ looseIdentical ( record . trackById , trackById ) ) {
560562 return record ;
561563 }
562564 }
@@ -599,7 +601,7 @@ class _DuplicateMap {
599601
600602 put ( record : CollectionChangeRecord ) {
601603 // todo(vicb) handle corner cases
602- var key = getMapKey ( record . item ) ;
604+ var key = getMapKey ( record . trackById ) ;
603605
604606 var duplicates = this . map . get ( key ) ;
605607 if ( ! isPresent ( duplicates ) ) {
@@ -610,17 +612,17 @@ class _DuplicateMap {
610612 }
611613
612614 /**
613- * Retrieve the `value` using key. Because the CollectionChangeRecord value maybe one which we
615+ * Retrieve the `value` using key. Because the CollectionChangeRecord value may be one which we
614616 * have already iterated over, we use the afterIndex to pretend it is not there.
615617 *
616618 * Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we
617619 * have any more `a`s needs to return the last `a` not the first or second.
618620 */
619- get ( value : any , afterIndex : number = null ) : CollectionChangeRecord {
620- var key = getMapKey ( value ) ;
621+ get ( trackById : any , afterIndex : number = null ) : CollectionChangeRecord {
622+ var key = getMapKey ( trackById ) ;
621623
622624 var recordList = this . map . get ( key ) ;
623- return isBlank ( recordList ) ? null : recordList . get ( value , afterIndex ) ;
625+ return isBlank ( recordList ) ? null : recordList . get ( trackById , afterIndex ) ;
624626 }
625627
626628 /**
@@ -629,7 +631,7 @@ class _DuplicateMap {
629631 * The list of duplicates also is removed from the map if it gets empty.
630632 */
631633 remove ( record : CollectionChangeRecord ) : CollectionChangeRecord {
632- var key = getMapKey ( record . item ) ;
634+ var key = getMapKey ( record . trackById ) ;
633635 // todo(vicb)
634636 // assert(this.map.containsKey(key));
635637 var recordList : _DuplicateItemRecordList = this . map . get ( key ) ;
0 commit comments