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,
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.
Animation is something which should not change the order of the elements internally but only change the position of the element.
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=
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=
http://plnkr.co/edit/Vjj9qCcoqMCyuOhNYKKY?p=preview
Related Topics
Detect Failure to Load Contents of an Iframe
Javascript: Scroll from One Div to the Other When Scrolling
Partial Password Masking on Input Field
Using JavaScript Calculated Values in Less
Detect and Log When External JavaScript or CSS Resources Fail to Load
How to Color Specific Letters in HTML Element Text
Change the :Before Selector from JavaScript
How to Get the Grid Coordinates of an Element Using JavaScript
Jquery: Animate Margins to Auto
Jquery Animate a -Webkit-Transform
Remove Url and Print Text from the Printed Page
Jquery Move Div with Arrow Keys
Javascript/Jquery Toggle Active Class Between 2 Buttons on a Button Group
Convert Gulp Watch in Gulp@3.9.1 to Gulp@4
Different Color Bars for Flot Categories Bar Chart
Is It Normal to Have Two Elements with Same Id in Two Div Elements with Other Id
How to Detect Internet Explorer (Ie) and Microsoft Edge Using JavaScript