fix(core): ensure ngFor only inserts/moves/removes elements when necessary (#10287)
Closes #9960 Closes #7239 Closes #9672 Closes #9454 Closes #10287
This commit is contained in:
@ -63,6 +63,56 @@ export class DefaultIterableDiffer implements IterableDiffer {
|
||||
}
|
||||
}
|
||||
|
||||
forEachOperation(
|
||||
fn: (item: CollectionChangeRecord, previousIndex: number, currentIndex: number) => void) {
|
||||
var nextIt = this._itHead;
|
||||
var nextRemove = this._removalsHead;
|
||||
var addRemoveOffset = 0;
|
||||
var moveOffsets: number[] = null;
|
||||
while (nextIt || nextRemove) {
|
||||
// Figure out which is the next record to process
|
||||
// Order: remove, add, move
|
||||
let record = !nextRemove ||
|
||||
nextIt &&
|
||||
nextIt.currentIndex < getPreviousIndex(nextRemove, addRemoveOffset, moveOffsets) ?
|
||||
nextIt :
|
||||
nextRemove;
|
||||
var adjPreviousIndex = getPreviousIndex(record, addRemoveOffset, moveOffsets);
|
||||
var currentIndex = record.currentIndex;
|
||||
|
||||
// consume the item, and adjust the addRemoveOffset and update moveDistance if necessary
|
||||
if (record === nextRemove) {
|
||||
addRemoveOffset--;
|
||||
nextRemove = nextRemove._nextRemoved;
|
||||
} else {
|
||||
nextIt = nextIt._next;
|
||||
if (record.previousIndex == null) {
|
||||
addRemoveOffset++;
|
||||
} else {
|
||||
// INVARIANT: currentIndex < previousIndex
|
||||
if (!moveOffsets) moveOffsets = [];
|
||||
let localMovePreviousIndex = adjPreviousIndex - addRemoveOffset;
|
||||
let localCurrentIndex = currentIndex - addRemoveOffset;
|
||||
if (localMovePreviousIndex != localCurrentIndex) {
|
||||
for (var i = 0; i < localMovePreviousIndex; i++) {
|
||||
var offset = i < moveOffsets.length ? moveOffsets[i] : (moveOffsets[i] = 0);
|
||||
var index = offset + i;
|
||||
if (localCurrentIndex <= index && index < localMovePreviousIndex) {
|
||||
moveOffsets[i] = offset + 1;
|
||||
}
|
||||
}
|
||||
var previousIndex = record.previousIndex;
|
||||
moveOffsets[previousIndex] = localCurrentIndex - localMovePreviousIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (adjPreviousIndex !== currentIndex) {
|
||||
fn(record, adjPreviousIndex, currentIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
forEachPreviousItem(fn: Function) {
|
||||
var record: CollectionChangeRecord;
|
||||
for (record = this._previousItHead; record !== null; record = record._nextPrevious) {
|
||||
@ -700,3 +750,13 @@ class _DuplicateMap {
|
||||
|
||||
toString(): string { return '_DuplicateMap(' + stringify(this.map) + ')'; }
|
||||
}
|
||||
|
||||
function getPreviousIndex(item: any, addRemoveOffset: number, moveOffsets: number[]): number {
|
||||
var previousIndex = item.previousIndex;
|
||||
if (previousIndex === null) return previousIndex;
|
||||
var moveOffset = 0;
|
||||
if (moveOffsets && previousIndex < moveOffsets.length) {
|
||||
moveOffset = moveOffsets[previousIndex];
|
||||
}
|
||||
return previousIndex + addRemoveOffset + moveOffset;
|
||||
}
|
||||
|
Reference in New Issue
Block a user