最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - AngularJS: swap two items in ng-repeater with animation - Stack Overflow

programmeradmin1浏览0评论

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 =

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

Share Improve this question edited Jun 2, 2014 at 0:12 Mark Ni asked Jun 1, 2014 at 22:08 Mark NiMark Ni 2,4011 gold badge28 silver badges34 bronze badges 5
  • possible duplicate of List reorder animation with angularjs – Blackhole Commented Jun 1, 2014 at 22:18
  • @Blackhole Hi, thanks. My situation requires actually reordering the array, the solution provided seems faking reordering by add extra position information to each item. – Mark Ni Commented Jun 1, 2014 at 22:28
  • Oh, in fact, there is no good solution in the other question as well ;) . Actually, it's look like a bug (see this ment, which perfectly describes your use case). – Blackhole Commented Jun 1, 2014 at 23:21
  • While playing with the Plunker, I've got this: plnkr.co/edit/npxFVh9FII0akVj6lIwp?p=preview - if you have the DOM elements inspector, and click the button several times before animation pletes, you'll see extra DOM nodes being added... strange behaviour. This does not help, but shows related unexpected behaviour. – J. Bruni Commented Jun 1, 2014 at 23:55
  • @J.Bruni that seems to be the feature of ngAnimate module, which auto add delay to dom changes according to css transition value. – Mark Ni Commented Jun 2, 2014 at 0:19
Add a ment  | 

2 Answers 2

Reset to default 4

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

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 acpanying 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"); 
            });
        }
    }
});
发布评论

评论列表(0)

  1. 暂无评论