@@ -40,6 +40,9 @@ export class DefaultIterableDiffer implements IterableDiffer {
4040 private _movesTail : CollectionChangeRecord = null ;
4141 private _removalsHead : CollectionChangeRecord = null ;
4242 private _removalsTail : CollectionChangeRecord = null ;
43+ // Keeps track of records where custom track by is the same, but item identity has changed
44+ private _identityChangesHead : CollectionChangeRecord = null ;
45+ private _identityChangesTail : CollectionChangeRecord = null ;
4346
4447 constructor ( private _trackByFn ?: TrackByFn ) {
4548 this . _trackByFn = isPresent ( this . _trackByFn ) ? this . _trackByFn : trackByIdentity ;
@@ -84,6 +87,13 @@ export class DefaultIterableDiffer implements IterableDiffer {
8487 }
8588 }
8689
90+ forEachIdentityChange ( fn : Function ) {
91+ var record : CollectionChangeRecord ;
92+ for ( record = this . _identityChangesHead ; record !== null ; record = record . _nextIdentityChange ) {
93+ fn ( record ) ;
94+ }
95+ }
96+
8797 diff ( collection : any ) : DefaultIterableDiffer {
8898 if ( isBlank ( collection ) ) collection = [ ] ;
8999 if ( ! isListLikeIterable ( collection ) ) {
@@ -123,7 +133,7 @@ export class DefaultIterableDiffer implements IterableDiffer {
123133 // TODO(misko): can we limit this to duplicates only?
124134 record = this . _verifyReinsertion ( record , item , itemTrackBy , index ) ;
125135 }
126- record . item = item ;
136+ if ( ! looseIdentical ( record . item , item ) ) this . _addIdentityChange ( record , item ) ;
127137 }
128138
129139 record = record . _next ;
@@ -135,9 +145,12 @@ export class DefaultIterableDiffer implements IterableDiffer {
135145 if ( record === null || ! looseIdentical ( record . trackById , itemTrackBy ) ) {
136146 record = this . _mismatch ( record , item , itemTrackBy , index ) ;
137147 mayBeDirty = true ;
138- } else if ( mayBeDirty ) {
139- // TODO(misko): can we limit this to duplicates only?
140- record = this . _verifyReinsertion ( record , item , itemTrackBy , index ) ;
148+ } else {
149+ if ( mayBeDirty ) {
150+ // TODO(misko): can we limit this to duplicates only?
151+ record = this . _verifyReinsertion ( record , item , itemTrackBy , index ) ;
152+ }
153+ if ( ! looseIdentical ( record . item , item ) ) this . _addIdentityChange ( record , item ) ;
141154 }
142155 record = record . _next ;
143156 index ++ ;
@@ -150,9 +163,12 @@ export class DefaultIterableDiffer implements IterableDiffer {
150163 return this . isDirty ;
151164 }
152165
153- // CollectionChanges is considered dirty if it has any additions, moves or removals.
166+ /* CollectionChanges is considered dirty if it has any additions, moves, removals, or identity
167+ * changes.
168+ */
154169 get isDirty ( ) : boolean {
155- return this . _additionsHead !== null || this . _movesHead !== null || this . _removalsHead !== null ;
170+ return this . _additionsHead !== null || this . _movesHead !== null ||
171+ this . _removalsHead !== null || this . _identityChangesHead !== null ;
156172 }
157173
158174 /**
@@ -183,6 +199,7 @@ export class DefaultIterableDiffer implements IterableDiffer {
183199 }
184200 this . _movesHead = this . _movesTail = null ;
185201 this . _removalsHead = this . _removalsTail = null ;
202+ this . _identityChangesHead = this . _identityChangesTail = null ;
186203
187204 // todo(vicb) when assert gets supported
188205 // assert(!this.isDirty);
@@ -216,12 +233,18 @@ export class DefaultIterableDiffer implements IterableDiffer {
216233 record = this . _linkedRecords === null ? null : this . _linkedRecords . get ( itemTrackBy , index ) ;
217234 if ( record !== null ) {
218235 // We have seen this before, we need to move it forward in the collection.
236+ // But first we need to check if identity changed, so we can update in view if necessary
237+ if ( ! looseIdentical ( record . item , item ) ) this . _addIdentityChange ( record , item ) ;
238+
219239 this . _moveAfter ( record , previousRecord , index ) ;
220240 } else {
221241 // Never seen it, check evicted list.
222242 record = this . _unlinkedRecords === null ? null : this . _unlinkedRecords . get ( itemTrackBy ) ;
223243 if ( record !== null ) {
224244 // It is an item which we have evicted earlier: reinsert it back into the list.
245+ // But first we need to check if identity changed, so we can update in view if necessary
246+ if ( ! looseIdentical ( record . item , item ) ) this . _addIdentityChange ( record , item ) ;
247+
225248 this . _reinsertAfter ( record , previousRecord , index ) ;
226249 } else {
227250 // It is a new item: add it.
@@ -269,7 +292,6 @@ export class DefaultIterableDiffer implements IterableDiffer {
269292 record . currentIndex = index ;
270293 this . _addToMoves ( record , index ) ;
271294 }
272- record . item = item ;
273295 return record ;
274296 }
275297
@@ -469,6 +491,18 @@ export class DefaultIterableDiffer implements IterableDiffer {
469491 return record ;
470492 }
471493
494+ /** @internal */
495+ _addIdentityChange ( record : CollectionChangeRecord , item : any ) {
496+ record . item = item ;
497+ if ( this . _identityChangesTail === null ) {
498+ this . _identityChangesTail = this . _identityChangesHead = record ;
499+ } else {
500+ this . _identityChangesTail = this . _identityChangesTail . _nextIdentityChange = record ;
501+ }
502+ return record ;
503+ }
504+
505+
472506 toString ( ) : string {
473507 var list = [ ] ;
474508 this . forEachItem ( ( record ) => list . push ( record ) ) ;
@@ -485,9 +519,13 @@ export class DefaultIterableDiffer implements IterableDiffer {
485519 var removals = [ ] ;
486520 this . forEachRemovedItem ( ( record ) => removals . push ( record ) ) ;
487521
522+ var identityChanges = [ ] ;
523+ this . forEachIdentityChange ( ( record ) => identityChanges . push ( record ) ) ;
524+
488525 return "collection: " + list . join ( ', ' ) + "\n" + "previous: " + previous . join ( ', ' ) + "\n" +
489526 "additions: " + additions . join ( ', ' ) + "\n" + "moves: " + moves . join ( ', ' ) + "\n" +
490- "removals: " + removals . join ( ', ' ) + "\n" ;
527+ "removals: " + removals . join ( ', ' ) + "\n" + "identityChanges: " +
528+ identityChanges . join ( ', ' ) + "\n" ;
491529 }
492530}
493531
@@ -513,6 +551,9 @@ export class CollectionChangeRecord {
513551 _nextAdded : CollectionChangeRecord = null ;
514552 /** @internal */
515553 _nextMoved : CollectionChangeRecord = null ;
554+ /** @internal */
555+ _nextIdentityChange : CollectionChangeRecord = null ;
556+
516557
517558 constructor ( public item : any , public trackById : any ) { }
518559
0 commit comments