I have a list of items, each has a unique id
$scope.arr = [{val:0,id:'a'},{val:1,id:'b'},{val:2,id:'c'}];
Each item is absolute positioned according to their $index
<div class="item" ng-repeat="item in arr track by item.id"
ng-style="getAbsPos($index)" >{{item.id}}</div>
All I wanted is swapping arr[0]
and arr[2]
in the array, and display a moving animation of this action. It turns out to be very difficult.
I assume this css would work since the list is tracked by id
.item{
transition:all 3000ms;
}
but somehow angular decides only moving one of items' dom and re-create the other one (why?!). As result, only one item is animated.
= Question =
Is there a solution to fix this problem, so both items will be animated when they swap? Thanks.
(Have to actually swap the item's position in the array, so it can be easily accessed by correct index all the time)
= See Plunker demo =
After playing around, I did find a very hacky solution which does change the item order in array:
=Idea=
As Zack and many other suggested, we keep a record of display position(item.x) in each item, use it to determine dom position
<div class="item" ng-repeat="item in arr track by item.id" ng-style="getAbsPos(item.x)" >{{item.id}}</div>
when swap, reordering the array first, because dom position is determined by item.x, not $index, no animation will be triggered;
var a= arr[0]; var c = arr[2]; arr[0] = c; arr[2] = a;
swap the item.x value of the two items in async manner (using
$timeout
), so angular treats step 2 and 3 as two separated dom changes, and only step 3 will trigger animation.$timeout(function(){ var tempX = a.x; a.x = c.x; c.x = tempX; },10)
This may create some problems when batch swap operations are performed. But for user triggered simple two items swap (my use case), it seems works just ok.
Let me know if there is a better solution, thanks.
=Plunker demo=
One idea would be to use your own left marker, rather than $index. Here is an example using a directive that watches your objects .left attribute. In this scenario you could use the .left to reorder the actual array at some point if you need to post it to a server or something. Here is an accompanying JSFIDDLE.
HTML
<div class="item" ng-repeat="item in list" move-to="item.left">{{item.id}} / {{$index}}</div>
module.controller('myCtrl', function($scope) {
$scope.list = [
{val:0, id:'a', left: 0},
{val:1, id:'b', left: 100},
{val:2, id:'c', left: 200}
];
$scope.swap = function() {
var a_left = $scope.list[0].left
$scope.list[0].left = $scope.list[2].left;
$scope.list[2].left = a_left;
}
})
.directive('moveTo', function() {
return {
restrict: 'A',
link: function(scope, elem, attrs) {
scope.$watch(attrs.moveTo, function(newVal) {
elem.css('left', newVal + "px");
});
}
}
});
来源:https://stackoverflow.com/questions/23985029/angularjs-swap-two-items-in-ng-repeater-with-animation