-
Notifications
You must be signed in to change notification settings - Fork 27.3k
fix(ngRepeat): trigger move animation #15072
base: master
Are you sure you want to change the base?
Changes from 1 commit
2331c42
263be6b
f3c6062
47bd3a4
7a98172
d8320a1
dcc80c1
c969ae2
410312c
4d6feb9
3b17d5d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
In the ngRepeatAction function: * The collection keys and values are accessed directly instead of copying them to nextBlockMap temporarily. * Checking the collectionIsLikeArray boolean value is faster than comparing value and type of collectionKeys and collection.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -425,16 +425,17 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani | |
//watch props | ||
$scope.$watchCollection(rhs, function ngRepeatAction(collection) { | ||
var | ||
block, // last object information {scope, element, id} | ||
collectionKey, | ||
block, // last object information {scope, element, id} | ||
collectionIsLikeArray, | ||
collectionKeys = [], | ||
elementsToRemove, | ||
index, key, value, // key/value of iteration | ||
index, key, value, | ||
lastBlockOrder = [], | ||
lastKey, | ||
nextBlockMap = createMap(), | ||
nextBlockOrder = [], | ||
nextKey, nextLength, | ||
nextKey, | ||
nextLength, | ||
previousNode = $element[0], // node that cloned nodes should be inserted after | ||
// initialized to the comment node anchor | ||
trackById, | ||
|
@@ -445,23 +446,23 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani | |
} | ||
|
||
// get collectionKeys | ||
if (isArrayLike(collection)) { | ||
collectionKeys = collection; | ||
collectionIsLikeArray = isArrayLike(collection); | ||
if (collectionIsLikeArray) { | ||
trackByIdFn = trackByIdExpFn || trackByIdArrayFn; | ||
} else { | ||
trackByIdFn = trackByIdExpFn || trackByIdObjFn; | ||
// if object, extract keys, in enumeration order, unsorted | ||
for (collectionKey in collection) { | ||
if (hasOwnProperty.call(collection, collectionKey) && collectionKey.charAt(0) !== '$') { | ||
collectionKeys.push(collectionKey); | ||
for (key in collection) { | ||
if (hasOwnProperty.call(collection, key) && key.charAt(0) !== '$') { | ||
collectionKeys.push(key); | ||
} | ||
} | ||
} | ||
nextLength = collectionKeys.length; | ||
nextLength = collectionIsLikeArray ? collection.length : collectionKeys.length; | ||
|
||
// setup nextBlockMap | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The "setup nextBlockMap" loop has a similar purpose to the "locate existing items" loop in the previous version. It does not delete previously seen blocks from lastBlockMap. Therefore, lastBlockMap does not need to be restored if a duplicate block is detected. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indentation of the comment here is wrong There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Narretz Is it? Looks good to me. |
||
for (index = 0; index < nextLength; index++) { | ||
key = (collection === collectionKeys) ? index : collectionKeys[index]; | ||
key = collectionIsLikeArray ? index : collectionKeys[index]; | ||
value = collection[key]; | ||
trackById = trackByIdFn(key, value, index); | ||
|
||
|
@@ -472,16 +473,17 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani | |
expression, trackById, value); | ||
} | ||
|
||
nextBlockMap[trackById] = {id: trackById, clone: undefined, scope: undefined, index: index, key: key, value: value}; | ||
nextBlockMap[trackById] = {id: trackById, clone: undefined, scope: undefined, index: index}; | ||
nextBlockOrder[index] = trackById; | ||
} | ||
|
||
// setup lastBlockOrder, used to determine if block moved | ||
for (lastKey in lastBlockMap) { | ||
lastBlockOrder.push(lastKey); | ||
for (key in lastBlockMap) { | ||
lastBlockOrder.push(key); | ||
} | ||
|
||
for (index = 0; index < nextLength; index++) { | ||
key = collectionIsLikeArray ? index : collectionKeys[index]; | ||
nextKey = nextBlockOrder[index]; | ||
|
||
if (lastBlockMap[nextKey]) { | ||
|
@@ -501,9 +503,7 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani | |
block.index = nextBlockMap[nextKey].index; | ||
} | ||
|
||
updateScope(block.scope, index, | ||
valueIdentifier, nextBlockMap[nextKey].value, | ||
keyIdentifier, nextBlockMap[nextKey].key, nextLength); | ||
updateScope(block.scope, index, valueIdentifier, collection[key], keyIdentifier, key, nextLength); | ||
|
||
nextBlockMap[nextKey] = block; | ||
previousNode = getBlockEnd(block); | ||
|
@@ -524,24 +524,19 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani | |
// However, we need to keep the reference to the jqlite wrapper as it might be changed later | ||
// by a directive with templateUrl when its template arrives. | ||
nextBlockMap[nextKey].clone = clone; | ||
updateScope(scope, nextBlockMap[nextKey].index, | ||
valueIdentifier, nextBlockMap[nextKey].value, | ||
keyIdentifier, nextBlockMap[nextKey].key, nextLength); | ||
|
||
delete nextBlockMap[nextKey].key; | ||
delete nextBlockMap[nextKey].value; | ||
updateScope(scope, nextBlockMap[nextKey].index, valueIdentifier, collection[key], keyIdentifier, key, nextLength); | ||
}); | ||
} | ||
} | ||
|
||
// leave | ||
// This must go after enter and move because leave prevents getting element's parent. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you explain this further? The leave animation section seems to have changed quite a bit from the original. Is this necessary for this PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The main change is there no need to keep track of deleted elements because we aren't doing any more processing where we might need to ignore the element. The other change is to skip blocks that don't need removed. |
||
for (lastKey in lastBlockMap) { | ||
if (nextBlockMap[lastKey]) { | ||
for (key in lastBlockMap) { | ||
if (nextBlockMap[key]) { | ||
continue; | ||
} | ||
|
||
block = lastBlockMap[lastKey]; | ||
block = lastBlockMap[key]; | ||
elementsToRemove = getBlockNodes(block.clone); | ||
$animate.leave(elementsToRemove); | ||
block.scope.$destroy(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is faster to check the collectionIsLikeArray boolean than to compare value and type of collectionKeys and collection inside a loop.