AngularJS: swap two items in ng-repeater with animation

半世苍凉 提交于 2019-12-22 08:26:08

问题


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 =

http://plnkr.co/edit/5AVhz81x3ZjzQFJKM0Iw?p=preview


回答1:


After playing around, I did find a very hacky solution which does change the item order in array:

=Idea=

  1. 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>
    
  2. 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; 
    
  3. 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=

http://plnkr.co/edit/Vjj9qCcoqMCyuOhNYKKY?p=preview




回答2:


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!