| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
ngRepeat does not trigger a move animation for items that come after inserted or deleted items. The documentation says that a move animation occurs "when an adjacent item is filtered out causing a reorder or when the item contents are reordered". With this fix, items that are moved can use an animation, such as changing the border or background color, to show a change. This will fix issue angular#15068.
…mparison operators with type.
| }; | ||
| // var getBlockStart = function(block) { | ||
| // return block.clone[0]; | ||
| // }; |
There was a problem hiding this comment.
Could you please remove this commented-out code?
Sorry, something went wrong.
The previous Travis test failed because of a network issue with saucelabs.com.
| for (var itemKey in collection) { | ||
| if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') { | ||
| collectionKeys.push(itemKey); | ||
| for (collectionKey in collection) { |
There was a problem hiding this comment.
Why this change? Key of the current item makes more sense to me than key of collection.
Sorry, something went wrong.
There was a problem hiding this comment.
You are probably right. I went back and forth on several names and don't remember why I settled on collectionKey. Possibly for consistency in how I named other keys. Let me know if you want me to change it.
Sorry, something went wrong.
|
Hi @pondermatic, thanks for this PR. This is a much requested feature, and I'd be happy if we can get this in. Since ngRepeat is a performance critical part of the code, we'll have to review it thoroughly, though (and run some benchmarks). |
Sorry, something went wrong.
|
The diff is pretty ugly because I changed several variable names. I'll work on a version that preserves as many names as possible. |
Sorry, something went wrong.
|
I've made some tests with the orderBy benchmark (after some fixes), rendering 500 ngRepeated spans from scratch, and found that the new version is about 15% slower than the current master. I'm not sure how much truth is in these numbers, but the difference was consistent (still in the millisecond area, though) The unit tests won't say much about performance as they usually have repeats with small numbers. |
Sorry, something went wrong.
|
I've made a version that minimizes the changes, but the diff is still hard to follow. It'll probably be better to explain the key changes. I'm going to do some profiling and see if I can speed things up. |
Sorry, something went wrong.
| keyIdentifier, nextBlockMap[nextKey].key, nextLength); | ||
|
|
||
| delete nextBlockMap[nextKey].key; | ||
| delete nextBlockMap[nextKey].value; |
There was a problem hiding this comment.
This is new, right? Is this necessary? The tests do not fail without this.
If we are doing this for every item, this might actually impact performance.
Sorry, something went wrong.
There was a problem hiding this comment.
The previous version did not store key and value in nextBlockMap. I am deleting them to keep them from needlessly using memory when nextBlockMap is assigned to lastBlockMap.
The orderby-bp benchmark ran about the same with or without deleting the properties.
Sorry, something went wrong.
|
I've been running the orderby-bp benchmark, updated by @Narretz today 0784977.
c54f7a9 before pondermatic 263be6b after pondermatic My version is 5.29% slower. :( When I removed the lines that delete the collection item's key and value from nextBlockMap, pass 1 was faster but pass 2 was much slower. Either way, the last collection is retained in memory which is not good. Then I put the key/value in its own array so that deleting is unnecessary. I also tried an array of just the keys, which should use less memory than 263be6b, especially if the values are large objects. The mean of those benchmark passes was 1111.31. I plan on committing revised code with this memory tweak and another tweak. |
Sorry, something went wrong.
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.
…angular.js into ngRepeat-animations
| var getBlockStart = function(block) { | ||
| return block.clone[0]; | ||
| }; | ||
|
|
There was a problem hiding this comment.
getBlockStart function is no longer used.
Sorry, something went wrong.
|
Commit 7a98172 ran the orderby-bp benchmark in 1037.09 ms for pass 1 and 1079.71 ms for pass 2. ngRepeat is now as fast as or a little bit faster than it was before. |
Sorry, something went wrong.
|
Hi @Narretz. Thank you for catching that bug! Your fix and unit test work perfectly. |
Sorry, something went wrong.
There was a problem hiding this comment.
LGTM, except a few nitpicks. I want someone else from the team to look at this before merging as ngRepeat is an important directive.
Sorry, something went wrong.
| expect(item.event).toBe('leave'); | ||
| expect(item.element.text()).toBe('2'); | ||
| }) | ||
| ); |
There was a problem hiding this comment.
Please put a newline between the two its
Sorry, something went wrong.
| $rootScope.items = [items[0], items[1], items[3]]; | ||
| $rootScope.$digest(); | ||
|
|
||
| // The leaving item should maintain it's position until it is removed |
There was a problem hiding this comment.
it's -> its (I know, I added this test ;))
Sorry, something went wrong.
|
Here's a plunker with ngRepeat and the fixed version to play around with: http://plnkr.co/edit/fu2SDoNfxCrUZPA0v0zm?p=preview |
Sorry, something went wrong.
| if (item.event === 'leave') { | ||
| break; | ||
| } | ||
| } |
There was a problem hiding this comment.
A comment to explain why this is needed would be helpful.
Sorry, something went wrong.
There was a problem hiding this comment.
I am going to revert this code. The ngRepeat tests assume the animation queue is in a certain order. During refactoring of ngRepeat, the order was different in this test so I altered the test. My latest version of ngRepeat, c969ae2, passes the original version of this test.
Sorry, something went wrong.
There was a problem hiding this comment.
Should tests assume that animation events are queued in a certain order? If not, then perhaps the queue should be tested like this:
// Test each queue item's event type
while ($animate.queue.length) {
item = $animate.queue.shift();
switch (item.element.text()) {
case '2':
expect(item.event).toBe('leave');
break;
case '3':
expect(item.event).toBe('move');
break;
default:
expect(item).toBeUndefined();
}
}Maybe we should add a Jasmine matcher in the "ngRepeat Animations" block that compares item.event and item.text() values between two arrays? Should this be new GitHub issue?
Sorry, something went wrong.
There was a problem hiding this comment.
Apparently, the animation queue order IS different than it used to be.
Options:
As far as I can tell, tests should not assume the order of events in the queue. So I propose the following matcher be added to the 'ngRepeat animations' group of tests:
beforeEach(function() {
jasmine.addMatchers({
toContainQueueItem: function() {
return {
compare: generateCompare(false),
negativeCompare: generateCompare(true)
};
function generateCompare(isNot) {
/**
* Matcher that checks that the expected element text is in the actual Array of
* animation queue items and that the event matches.
* @param {array} actual
* @param {string} expectedElementText
* @param {string} expectedEvent optional if isNot = true
* @returns {{pass: boolean, message: message}}
*/
return function(actual, expectedElementText, expectedEvent) {
if (expectedEvent === undefined) {
expectedEvent = '';
}
var actualLength = actual.length;
var i;
var item;
var message = valueFn(
'Expected animation queue to ' + (isNot ? 'not ' : '') + 'contain an item where the element\'s text is "'
+ expectedElementText + '"' + (isNot ? '' : ' and the event is "' + expectedEvent + '"')
);
var pass = isNot;
for (i = 0; i < actualLength; i++) {
item = actual[i];
if (item.element.text() === expectedElementText) {
pass = item.event === expectedEvent;
break;
}
}
return {'pass': pass, 'message': message};
};
}
}
});
});The tests should use the matcher like this:
expect($animate.queue).not.toContainQueueItem('1');
expect($animate.queue).toContainQueueItem('2', 'leave');
expect($animate.queue).toContainQueueItem('3', 'move');
$animate.queue = [];What does everyone think?
Sorry, something went wrong.
| }) | ||
| ); | ||
|
|
||
| it('should fire off the move animation for filtered items', |
There was a problem hiding this comment.
This description could be more clear. Perhaps:
"should fire the move animation for items that shifted due to removal of previous items"
Or something like that.
Sorry, something went wrong.
There was a problem hiding this comment.
How about this description?
should fire off the move animation for items that change position when other items are filtered out
Sorry, something went wrong.
There was a problem hiding this comment.
+1
Sorry, something went wrong.
There was a problem hiding this comment.
should the tests be checking the previousNode value that is passed to the move animation?
Sorry, something went wrong.
|
Generally looks like a good refactoring. Thanks @pondermatic |
Sorry, something went wrong.
In the 'should fire off the leave animation' test, revert change in how the animation queue is shifted to find item '2'. At one time during the refactoring of ngRepeat, the order of events in the queue changed. Improve title of the spec that checks for 'move' animation events when filtering items changes other items positions.
Improve title of the spec that checks for 'move' animation events when filtering items changes other items positions. Remove unused item variable in two tests.
There was a problem hiding this comment.
LGTM - except for the failing tests :-)
Sorry, something went wrong.
The 'ngRepeat animations' test group should not assume the order of items in the animation queue. This matcher searches the animation queue for an item with an expected element text and event.
|
I can't believe I pushed that change without running tests. :) |
Sorry, something went wrong.
| }; | ||
| })); | ||
|
|
||
| beforeEach(function() { |
There was a problem hiding this comment.
Tests should not rely on the order of items in the animation queue. This Jasmine matcher looks for a queued item with a given element text and matches against that item's event.
Sorry, something went wrong.
|
I decided to do some more tests, and it looks like we created a performance problem with the new behavior when ngAnimate is used. See this updated plnkr: http://plnkr.co/edit/fu2SDoNfxCrUZPA0v0zm?p=preview The second repeat renders 10000 elements (I know that you generally should avoid these huge lists)
@matsko had done some interesting changes with regard to caching animation styles here: #14166 With ngAnimate enabled and no actual animation styles, the whole process is aborted earlier with this PR, but there is no noticable performance gain. So the question is, are we okay with the perf dropoff with large repeats? There are many ways to disable animations on perf critical parts of the page, but this might still bite people whose apps will suddenly perform worse. I suggest we should at least look into making ngAnimate faster when no actual css animations are present. Consistenly slow is checking when animations are allowed, because we have to traverse the node parent again again and again: angular.js/src/ngAnimate/animateQueue.js Line 621 in f403925 angular.js/src/ngAnimate/animation.js Line 51 in f403925 |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
bug fix
What is the current behavior? (You can also link to an open issue here)
#15068