ng-repeat animation not working - angularjs

My animation with ng-repeat does not seem to work . Here is the plunkr
http://plnkr.co/edit/kYtzM9d0rzGmrniybz9c?p=preview
Any inputs.

1. You have registered two modules:
<html ng-app="plunker">
And:
<body ng-app="testApp">
Remove ng-app from the html tag.
2. You need to load angular-animate.js
3. As you are moving the elements within the array, it's neither enter or leave you should use, but instead move: .ng-move {
4. You are using the ng-animate directive (ng-animate="'animate'") which is deprecated since 1.2. You are also passing it a class that does not exist.
This would work:
.ng-move {
transition: 1.75s;
opacity: 0;
}
.ng-move.ng-move-active {
opacity: 1;
}
But I would recommend giving it a specific class to be able to specify which ng-repeat uses which animation:
.move-animation.ng-move {
transition: 1.75s;
opacity: 0;
}
.move-animation.ng-move.ng-move-active {
opacity: 1;
}
And:
<td class="move-animation" ng-repeat="cust in customers" ng-click="swap(this.$index)">
Demo: http://plnkr.co/edit/fiMORm5ZFLejV1aOUrbR?p=preview

Related

Parent > Child animation unexpected - different Angular 1.3 to 1.4

I'm not sure if this is a bug, or I have a lack of understanding of the expected behaviour.
On the following codepen (With Angular 1.4) I expect the parent to animate followed by the child items to animate. However, the parent does not animate and the child items still wait for the transition period.
In this codepen (With Angular 1.3) the animations work as expected.
Is this a bug, am I missing something, or has something changed in 1.3 to 1.4 that I have not followed?
Code for completion
HTML
<div ng-app="app" ng-controller="TestCtrl as test">
<button ng-click="toggleItems()">Toggle Items</button>
<div class="parent" ng-show="items">
<p class="child" ng-repeat="item in items">{{item.name}}</p>
</div>
</div>
CSS
.parent {
background:#f00;
transition:all linear 0.5s;
}
.parent.ng-hide {
opacity:0;
}
.child {
background:#0f0;
transition:all linear 0.5s;
}
.child.ng-enter {
opacity:0;
}
JS
var app = angular.module('app', ['ngAnimate'])
app.controller('TestCtrl', function($scope){
$scope.items = null;
$scope.toggleItems = function(){
if( $scope.items ){
$scope.items = null;
} else {
$scope.items = [
{name: 'item 1'},
{name: 'item 2'},
{name: 'item 3'},
];
}
};
});
I found out what was the issue. But it finally seems like an Angular bug or just an outdated angular css:
1.3.5
Using ng-hide with ng-animate will apply :
ng-hide-animate ng-hide-add ng-hide-add-active when added.
ng-hide-animate ng-hide-remove ng-hide-remove-active when removed.
This works well since i found this line of CSS about ng-hide :
.ng-hide:not(.ng-hide-animate) {
display: none !important;
}
This mean your div will not be display when there is no animation. When there is an animation it will show and produce the animation.
1.4.0
In the official documentation Angular added this :
CSS Class-based Animations Class-based animations (animations that are
triggered via ngClass, ngShow, ngHide and some other directives) have
a slightly different naming convention. Class-based animations are
basic enough that a standard transition or keyframe can be referenced
on the class being added and removed.
For example if we wanted to do a CSS animation for ngHide then we
place an animation on the .ng-hide CSS class:
<div ng-show="bool" class="fade"> Show and hide me </div>
<button ng-click="bool=true">Toggle</button>
<style> .fade.ng-hide { transition:0.5s linear all; opacity:0; }
</style>
This mean that on 1.4.0 using ng-hide will simple add and remove ng-hide class.
**/!** In your codePen exemple i found that this line is still there in the CSS
.ng-hide:not(.ng-hide-animate) {
display: none !important;
}
Wich that each time you add ng-hide or remove it, you'll simple set "display:none" wich mean that no animation can be run. You'll need to override this behavior to use proper animations.
Doing this :
.parent.ng-hide {
display:block!important;
opacity:0;
}
Could do the trick but i'm not really sure that this is a safe way to do this.
At least i found the explanation, it would require more investigation to know how to deal with it in a proper way.
Hope it helped.

Angular: ng-if animation not working on directive

I'm using Angular 1.3 and animate.css.
I'm trying to apply a simple fadeIn/out animation on a directive i have using this code:
.vl-fade-in-out{
&.ng-enter{
animation: fadeIn 1s;
}
&.ng-leave{
animation: fadeOut 1s;
}
}
But no animation is applied, however, the same animation does apply if i use it directly on a html element (like div).
//this is not animating
<my-directive class="vl-fade-in-out" ng-if="show"></my-directive>
//this is animating
<div class="vl-fade-in-out" ng-if="show"></div>
Also if i apply fadeIn/out effect using transition it works even when applied on the directive:
.vl-fade-in-out{
&.ng-enter{
transition:1s linear all;
opacity: 0;
&.ng-enter-active{
opacity: 1;
}
}
&.ng-leave{
transition:1s linear all;
opacity: 1;
&.ng-leave-active{
opacity: 0;
}
}
}
Am I doing something wrong?
Pen: http://codepen.io/anon/pen/aOLzGE
#Claies was right in his second comment.
<my-directive> is not a recognized element in Chrome so it does not assign it any default attributes that you would expect. Assigning display: block to my-directive or .vl-fade-in-out will allow it to be animated.
Or you could use replace: true in the myDirective return in order to replace the custom element with the content of the template if your content contains a normal element.
Use restrict:'C' with <div class="test">. it will work

How to make simple animation using ngAnimate

I am not able to understand how the ngAnimate works exactly. here is my doubt.
1) ngAnimate - only works on directives?
2) how to make ng-animate work without the directive
3) Any of above way, how to add call back after animation complete?
Because i see all the animation examples only with directives.
I have a small demo here, any one help me to animation both without directive and with directive approach to simply adding a class name as `fade'?
my CODE:
<div class="container" ng-app="myApp">
<div class="content" ng-controller="count">
<h1 ng-click="animate()">Click ME</h1>
<h2>Let me Fade</h2>
</div>
</div>
<div class="container" ng-app="myApp">
<div class="content" ng-controller="count">
<h1 ng-click="animate()">Click ME</h1>
<h2>Let me Fade</h2>
</div>
</div>
Demo to update
I am not able to understand how the ngAnimate works exactly. here is
my doubt.
ngAnimate is a module that provides support for animations in angular apps. There are two ways to make use of animations when ngAnimate is used: by using CSS and JavaScript. For CSS based animations, angularjs adds a class ng-enter/ng-leave whenever an element is shown/removed from 'view'. You simply need to play with these classes to make the animation work!
Prerequisite:
You would need to add the library for angular-animate
<script src="ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-animate.js">
</script>
and include ngAnimate as the dependency in your myApp module.
var myApp = angular.module('myApp', ['ngAnimate']);
1) ngAnimate - only works on directives?
Yes. You cannot use ngAnimate without directive.
According to documentation, following directives are "animation aware":
ngRepeat, ngView, ngInclude, ngSwitch, ngIf, ngClass,
ngShow, ngHide, ngModel, ngMessages and ngMessage
2) how to make ng-animate work without the directive
You cannot!. Remember, even ng-click is a directive
3) Any of above way, how to add call back after animation complete?
Yes, You can add a callback after the animation is complete using the $animate service(which would usually be done in a custom directive) and use $animate.leave(element, [options]);
Have a look at this example for triggering events after the animation ends.
Finally, here is the updated demo you mentioned in question.
You may toggle a flag to true/false with each click on <h1> and make content inside <h2> hide/show based on flag.
<div class="container" ng-app="myApp">
<div class="content" ng-controller="count">
<h1 ng-click="animate()">Click ME</h1>
<h2 ng-if="flag" class="fade">Let me Fade</h2>
</div>
</div>
Also, you'd need to handle fade-effect with css
.fade.ng-enter {
transition:0.5s linear all;
opacity:0;
}
.fade.ng-enter.ng-enter-active {
opacity:1;
}
.fade.ng-leave {
transition:0.5s linear all;
opacity:1;
}
.fade.ng-leave.ng-leave-active {
opacity:0;
}
Hope it helps!
<div class="container" ng-app="myApp">
<div class="content" ng-controller="count">
<h1 ng-click="animate()">Click ME</h1>
<h2 ng-if="clicked" class="animate-if">Let me Fade</h2>
</div>
I added a variable named clicked which is set to true or false to animate the Let me Fade Text
var myApp = angular.module('myApp', []);
myApp.controller('count', function($scope) {
$scope.clicked=false;
$scope.animate = function () {
$scope.clicked=!$scope.clicked;
}
});
In this JS file upon clicking the click me button the variable clicked is set to true or false .
**
h2.fade {
opacity : 0;
transition: opacity 1s ease-in-out;
}
.animate-enter, .animate-leave {
transition: 500ms ease-in all;
position: relative;
display: block;
}
.animate-enter.animate-enter-active, .animate-leave {
left: 0;
}
.animate-leave.animate-leave-active, .animate-enter {
left: 500px;
}
**
Here in the css file i added css for the class animate which acts upon clicked variable if the variable is true it goes for animate-enter-active
otherwise it goes for leave-active

How to implement a flip over effect using AngularJS animations?

What would be the best way to achieve a flip over effect using AngularJS animations?
I would like the flip over effect to occur on click. Every time it's clicked, it should flip over to the other side.
Ideally, I guess, I'm looking for a directive implementation that uses Angular animations.
PLNKR - here is a seed of a configurable angular directive that provides 3d flipping functionality. I do not see any good reason why to use ngAnimate for it.
basic usage
<flip flip-width="200px" flip-height="100px">
<flip-panel>
content-front
</flip-panel>
<flip-panel>
content-back
</flip-panel>
</flip>
Comments
It appends css-styles on its own, to be fully independent.
In a proper, generic directive all names should be configurable.
flip-width and flip-height sets style of flip and both flip-panels.
Directive makes some basic check, if both front and back are set.
First flip-panel is front and the second is back.
Due to usage of transclusion content of the flip-panel may be arbitrary html. (you are right Misha no transclusion needed)
It only works in -webkit. (update to make it work in Firefox, just duplicate all pieces with -webkit with no prefix - you do not need -moz)
UPDATE
PLNKR - here is an updated and extended version. It shows what I meant by making the directive configurable. In more details:
Introduced flipConfig via provider, that allows to set in app.config:
default dimensions
css class names
speed of the transition
if the flip action is triggered by a click on the panel
Introduced flip-show attribute that specifies which side to show.
Changing flip-show we can trigger the flip action from outside of the directive.
It works in Firefox and [almost:-)] in IE11.
(btw: it is just a seed and it may be improved in a lot of ways. E.g: specifying axis, specifying origin of the transform, specifying radius and margin of the panels, allowing flip on hover, defaults colors, margins and so on)
I had the same usecase just recently for an angular memory game.
My implementation is the same by the idea of the other answers. I also released the flipping code along with a DEMO.
Github: https://github.com/zwacky/angular-flippy
P.s.: Looks i'm late to the party ;)
You can use ng-click and ng-class to add a class when the flip container is clicked.
<div class="flip-container" ng-click="flip = !flip" ng-class="{'flip': flip}">
<div class="flipper">
<div class="front" style="background: lightblue;">
front
</div>
<div class="back" style="background: lightgreen;">
back
</div>
</div>
</div>
This is essentially the angular way of doing what Walsh suggested in his article:
Adding the flip class to the container element will flip the card using JavaScript -- no user hover required. A JavaScript comment like document.querySelector("#myCard").classList.toggle("flip") will do the flip!
The only change to David Walsh's css was removing the :hover selector - the html structure is unchanged. It works nicely in chrome and firefox.. but the flip isn't as pretty in IE.
Here is a working demo: http://plnkr.co/edit/0dn775vpuoOeh2PS1T6k?p=preview
Update
I created a simple directive to encapsulate this basic technique. It allows you to flip over a black card, to reveal a picture on the other side.
app.directive("flipReveal", function() {
return {
restrict: 'E',
replace: true,
templateUrl: 'template.html',
scope: {
url: '=',
flip: '='
}
}
})
Here is a link to a new demo: http://plnkr.co/X4pSav
Disclaimer Based on #artur's answer https://stackoverflow.com/a/23139242/1319998 , but hopefully both simplified and made more flexible.
A custom directive is the way to go, one that can be used as:
<flip flip-side="{{side}}">
<flip-front>
Front side contents
</flip-front>
<flip-back>
Rear contents
</flip-back>
</flip>
I think it should have certain properties:
Programatically controlled by an attribute. In this case, a string that is equal to 'front' or 'back'
<flip flip-side="{{side}}">....</flip>
this would allow programmatic access via the surrounding scope.
Integrated with ngAnimate/$animate. Specifically, if ngAnimate is removed or disabled, the animation should not occur, but the reveal of the other side happen immediately. Using $animate.addClass/$animate.removeClass would achieve this, adding/removing a flip-visible class together with display:block and display:none styles to make sure the right side is visible/hidden when the animations are disabled.
flip > flip-front, flip > flip-back {
display: none;
}
flip > .flip-visible {
display: block;
}
Controlled by CSS, with defaults. So if you want to change the duration of the flip, it's a CSS, and not a Javascript, addition.
So it will have a style sheet to add styles required for the various stages of $animate.addClass / $animate.removeClass CSS animations explained at Year of Moo and $animate docs . The class will be flip-visible, so the extra classes will be .flip-visible-add, .flip-visible-add-active, .flip-visible-remove, and .flip-visible-remove-active classes.
The full set of styles can be seen at http://plnkr.co/edit/bbYbMhiURnm6FqC9patp?p=preview, but the main construction is of the form:
.flip-visible-add {
// Initial setup: time and initial, pre-animation, transform
}
.flip-visible-add.flip-visible-add-active {
// Target transform
}
Putting all this together, the directive is quite short:
app.directive("flip", function($animate) {
return {
restrict : "E",
controller: function($scope, $element, $attrs) {
var elements = {
'front': $element.find('flip-front'),
'back': $element.find('flip-back')
};
$attrs.$observe('flipSide', function(visibleSide) {
visibleSide = visibleSide || 'front';
var otherSide = visibleSide == 'front' ? 'back' : 'front';
$animate.removeClass(elements[otherSide], 'flip-visible');
$animate.addClass(elements[visibleSide], 'flip-visible');
});
}
}
});
This can all be seen in an example, together with the stylesheets to make it all work, at http://plnkr.co/edit/bbYbMhiURnm6FqC9patp?p=preview
I realise there is a benefit to not integrating with the $animate service, and having a purely class-based solution.
If you use $animate with addClass and removeClass, but interrupt the animation (say, by clicking quickly and repeatedly on the element), the animation will 'jerk' to its end/starting point, and then animate from that position, at least on Chrome. Using a pure CSS solutions avoids this issue, and always animates from the exact current point, giving a smoother effect.
An added benefit is the solution is also simpler, and you don't need a custom directive.
For example, the HTML can be as follows:
<flip class="{{side === 'front' ? 'flip-front' : 'flip-back'}}">
<flip-front>
Front side contents
</flip-front>
<flip-back>
Rear contents
</flip-back>
</flip>
I use custom elements, but they don't need to have any directives attached: they are just for CSS to hook into:
flip > flip-front, flip > flip-back {
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
/* Time can be overriden */
transition: -webkit-transform 0.5s;
transition: transform 0.5s;
}
/* Front visible */
flip > flip-front {
-webkit-transform: perspective(800px) rotateY(0);
transform: perspective(800px) rotateY(0);
}
flip > flip-back {
-webkit-transform: perspective(800px) rotateY(180deg);
transform: perspective(800px) rotateY(180deg);
}
/* Back visible */
flip.flip-back > flip-front {
-webkit-transform: perspective(800px) rotateY(-180deg);
transform: perspective(800px) rotateY(-180deg);
}
flip.flip-back > flip-back {
-webkit-transform: perspective(800px) rotateY(0);
transform: perspective(800px) rotateY(0);
}
This can be seen in a demo at http://plnkr.co/edit/A7IeGa1JEsZishmTDTaK?p=preview
I would simply add / remove a class on click.
If you want to hook into the angular animation system then take a look at the $animate service, in particular add/remove/setClass(). The service is usually used in directives. You might want to create a directive that reacts on a click event and triggers the animation. You even get informed when the animation has completed.
Chances are that it's not worth it ;)
You are going to want to create 3 divs.
<div class="wrapper">
<div class="front"></div>
<div class="back"></div>
</div>
You then position back behind front using z-index, and flip it upside down using rotateX (-180deg or so). Set a transition on wrapper as well.
Then, on click of wrapper, rotateX(+180deg). This will pretty much infinitely flip it over.
** Update: For angular, bind to click and use setClass to toggle between two classes on wrapper, one at rotateX(0deg) , the other at rotateX(180deg)
Here is a slightly modified version of artur's answer:
DEMO
angular.module('FlipDemo', []).directive("flip", function() {
return {
restrict : "A",
scope: true,
link: function(scope, element) {
var $panels = element.css({ position: 'relative' }).children().addClass("flip-panel");
var frontPanel = $panels.eq(0);
var backPanel = $panels.eq(1);
scope.showFrontPanel = function() {
frontPanel.removeClass("flip-hide-front-panel");
backPanel.addClass("flip-hide-back-panel");
};
scope.showBackPanel = function() {
backPanel.removeClass("flip-hide-back-panel");
frontPanel.addClass("flip-hide-front-panel");
};
scope.showFrontPanel();
}
}
});
.flip-panel {
position: absolute;
width: 100%;
height: 100%;
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-webkit-transition: -webkit-transform .4s;
-moz-transition: -moz-transform .4s;
-webkit-transform: perspective(800px) rotateY(0deg);
-moz-transform: perspective(800px) rotateY(0deg);
}
.flip-hide-back-panel {
-webkit-transform: perspective(800px) rotateY(180deg);
-moz-transform: perspective(800px) rotateY(180deg);
}
.flip-hide-front-panel {
-webkit-transform: perspective(800px) rotateY(-180deg);
-moz-transform: perspective(800px) rotateY(-180deg);
}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.1/angular.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body ng-app="FlipDemo">
<div style="width: 100px; height: 150px">
<div flip style="width: 100%; height: 100%">
<div style="background-color: green">
<div>Front</div>
<button ng-click="showBackPanel()">Show Back</button>
</div>
<div style="background-color: blue">
<div>Back</div>
<button ng-click="showFrontPanel()">Show Front</button>
</div>
</div>
</div>
<br>
<div style="width: 150px; height: 100px">
<div flip style="width: 100%; height: 100%">
<div style="background-color: green">
<div>Front</div>
<button ng-click="showBackPanel()">Show Back</button>
</div>
<div style="background-color: blue">
<div>Back</div>
<button ng-click="showFrontPanel()">Show Front</button>
</div>
</div>
</div>
</body>
</html>
Main differences:
Works in Chrome and Firefox.
More flexibility with when the flip happens.
Just one directive rather than two. Less code.
I took the CSS outside of the directive for clarity sake.

angularjs: ngAnimate not working

I am new to angularjs. I am trying angularjs with rails (using "angularjs-rails" gem). I am having trouble using ngAnimate. The functionality works fine i.e. angular is properly installed and "addEntry" function adds up the new entry, but animation is not working. I am not sure what I am missing here.
app = angular.module("MyApp", ['ngAnimate'])
#MainCtrl = ($scope) ->
$scope.entries = [{name: "Dummy1"}, {name: "Dummy2"}]
$scope.addEntry = ->
$scope.entries.push({name: "Dummy3"})
Here is the html file:
<div ng-controller="MainCtrl">
<button ng-click="addEntry()"></button>
<div ng-repeat="entry in entries" ng-animate="'demo'">
<a>{{entry.name}}</a>
</div>
</div>
Here is CSS to support ngAnimate:
.demo-enter {
-webkit-transition: all 1s linear;
transition: all 1s linear;
background: #000;
}
.demo-enter.demo-enter-active {
background: #ccc;
}
The ng-animate attribute is deprecated in 1.2 and no longer used. Instead, animations are now class based.
If you want your ng-repeat enter animaiton to be named 'demo' you have to decorate it with some additional classes following a special naming convention:
.demo.ng-enter {
transition: all linear 500ms;
opacity: 0;
}
.demo.ng-enter-active {
opacity: 1;
}
Then just put the 'demo' class on the element containing the ng-repeat:
<div ng-repeat="entry in entries" class="demo">
Demo: http://plnkr.co/edit/kNeXtl6TpGNvtQEpErwh?p=preview
Good source on the subject: http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html
If you want the fade animation, set opacity to 0 at the setup class, and opacity to 1 at the active
.animate-enter {
-webkit-transition: 1s linear all; /* Chrome */
transition: 1s linear all;
opacity: 0;
}
.animate-enter.animate-enter-active {
opacity: 1;
}

Resources