List Reorder Animation with Angularjs

How can I animate sorting a list with orderBy using ng-repeat with ng-animate?

So, even if @Alex Osborn has shown a way to do what you want in the comments, here is my attempt:

angular.module('StackApp', []).controller('MainCtrl', function($scope) {  'use strict';
$scope.reverse = 'false';
$scope.myList = [{ id: 0, text: 'HTML5 Boilerplate' }, { id: 1, text: 'AngularJS' }, { id: 2, text: 'Karma' }, { id: 3, text: 'Hello' }, { id: 4, text: 'World' }, { id: 5, text: 'How' }, { id: 6, text: 'Are' }, { id: 7, text: 'You' }, { id: 8, text: '?' }, { id: 9, text: 'I' }, { id: 10, text: 'write' }, { id: 11, text: 'more' }, { id: 12, text: 'to' }, { id: 13, text: 'make' }, { id: 14, text: 'the' }, { id: 15, text: 'list' }, { id: 16, text: 'longer' }];
$scope.$watch('reverse', function() { $scope.setOrder(); });
$scope.setOrder = function() {
if ($scope.reverse === 'random') {
var t = [];
for (var i = 0; i < $scope.myList.length; i++) { var r = Math.floor(Math.random() * $scope.myList.length); while (inArray(t, r)) { r = Math.floor(Math.random() * $scope.myList.length); } t.push(r); $scope.myList[i].order = r; }
} else {
for (var i = 0; i < $scope.myList.length; i++) { if ($scope.reverse === 'false') { $scope.myList[i].order = i; } else { $scope.myList[i].order = ($scope.myList.length - 1 - i); } } } };
function inArray(a, value) { for (var i = 0; i < a.length; i++) { if (a[i] === value) { return true; } } return false; }
});
#list {  /* Needed, otherwise items would be at top of the page (see below) */  position: absolute;  /* full width, or it would look strange */  width: 100%;}#list li {  position: absolute;  /* Top: 0; this will be changed for every single list item by AngularJS */  top: 0;  /* Item height; hold this in sync with template file */  height: 40px;  /*  Simple transition */  -webkit-transition: top 0.5s ease-in-out;  -moz-transition: top 0.5s ease-in-out;  transition: top 0.5s ease-in-out;}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script><div ng-app="StackApp">  <div ng-controller="MainCtrl">    <h1>Animate Order</h1>    <form action="">      <label for="reverse">reverse = true</label>      <br>      <input type="radio" value="true" name="reverse" ng-model="reverse">      <br>      <br>      <label for="reverse">reverse = false</label>      <br>      <input type="radio" value="false" name="reverse" ng-model="reverse">      <br>      <br>      <label for="reverse">reverse = random (click button below to shuffle again)</label>      <br>      <input type="radio" value="random" name="reverse" ng-model="reverse">    </form>    <br>    <br>    <input type="button" ng-click="reverse = 'random';setOrder()" value="setOrder()">    <br>    <br>    <ul id="list" ng-style="{height: ((myList.length * 40) + 'px')}">      <li ng-repeat="item in myList" ng-style="{top: ((item.order * 40) + 'px')}">{{$index}} - {{item.order}}. {{item.text}}</li>    </ul>  </div></div>

What's a good way to move items in an list with AngularJS and ng-animate?

After a lot of messing around, I got it to work the way I wanted using the CSS next sibling selector, +: (Fiddle)

.person-move {
position: relative;
top: 26px;
}

.person-move.person-move-active {
transition: all 0.5s ease;
top: 0;
}

.person-move + div {
/* cannot have transition on this element */
/*transition: all 1s ease;*/
position: relative;
top: -26px;
}

.person-move.person-move-active + div {
transition: all 0.5s ease;
position: relative;
top: 0;
}

The key was to have the transition style only on the active class selectors. The examples I've seen like on yearofmoo have them on the base classes which I can't see any reason for since those are there solely to set up the initial condition for the animation I believe. Chrome doesn't like it anyway and tries to animate to the initial condition when using the sibling selector.

Animate orderBy list values in Angular

see, what angular ng-repeat does,

  1. the html nodes are overwritten according to changes in the object used in ng-repeat. that means, the order of the elements in the dom tree is itself changed.

  2. Animation is something which should not change the order of the elements internally but only change the position of the element.

  3. So in-order to make it an animation effect, don't sort the object(division in your case) on click. instead make it move around for that you need to create your own directive for defining the animation for the list.

by sorting the object itself, you will not be able to animate it.

How to trigger an ng-move with angular-animate when reordering an array?

Try giving a gap (In digestion) between removal and insertion, that will get the ng-enter and ng-leave animations to kick in.

    var temp = $scope.names.splice(order, 1).pop();
$timeout(function(){
$scope.names.splice(order+1, 0, temp);
});

Plnkr

If you want to avoid using timeout, restructure your data a bit, make it array of objects (which is always desirable anyways) and do:-

ViewModel:-

  $scope.names = [{'name':'Igor Minar'}, {'name':'Brad Green'}, {'name':'Dave Geddes'}, {'name':'Naomi Black'}, {'name':'Greg Weber'}, {'name':'Dean Sofer'}, {'name':'Wes Alvaro'}, {'name':'John Scott'}, {'name':'Daniel Nadasi'}];

In the handler:-

  $scope.moveDown = function(order){
var itm = $scope.names.splice(order+1, 1).pop(); //Get the item to be removed
$scope.names.splice(order, 0, angular.copy(itm)); //use angular.clone to get the copy of item with hashkey
}

2 things are important here, you would need to use angular.clone so that default tracker property ($$hashkey) will be removed from the shifting item,it seems like only when the item is removed and new item is inserted (based on tracker property) angular adds the animation classes to it. You cannot do it with primitive as you originally had.

Plnkr2

AngularJS: swap two items in ng-repeater with animation

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



Related Topics



Leave a reply



Submit