Angular: ng-if animation not working on directive - angularjs

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

Related

ng-if does not add behaviour while ng-show does

Case :
<div class="slide" ng-show="condition" >
I have a directive set as class that gives the slide behavior to the div when it shows.
When i use ng-if instead of ng-show so that the code does not get executed unless the condition really applies, the slide behavior is not working anymore, while with ng-show it works .
Is there a work around this? timeout maybe?
<div class="slide" ng-if="condition" >
Try this code
<div ng-class="{slide: true}" ng-if="condition" >
This is as expected. ngIf animations are defined differently than ngShow animations.
The proper way to do animations is to include the ngAnimate module and then include animations CSS for each of the directives you want to animate (like ngIf, ngShow, ngRepeat).
/* ng-if */
.slide.ng-enter,
.animate-if.ng-leave {
transition: all 1s;
}
.slide.ng-enter,
.slide.ng-leave.ng-leave-active {
transform: translateX(-100px);
}
.slide.ng-leave,
.slide.ng-enter.ng-enter-active {
transform: translateX(0);
}
/* ng-show */
.slide {
transform: translateX(0);
}
.slide.ng-hide-add.ng-hide-add-active,
.slide.ng-hide-remove.ng-hide-remove-active {
transition: all 1s;
}
.slide.ng-hide {
transform: translateX(-100px);
}

Angular animate not working with swappable divs

I'm trying to make animated transitions when swapping two divs using ng-show and ng-hide on both. This is currently my code:
<div ng-show="loginToggler === 'register'">
Register
</div>
<div ng-show="loginToggler === 'login'">
Login
</div>
My css partial for animating :
.ng-hide-add { animation:0.5s lightSpeedOut ease; }
.ng-hide-remove { animation:0.5s lightSpeedIn ease; }
Animations are working properly only when the first div is appearing/disappearing. None of suitable transition effects apply on my second div (it works the same way when I swap my divs position in code - only the one which is earlier in my code has the transition effects applied).
Tried with the second div such as ng-hide="loginToggler === 'register'" ? that way both of them will have .ng-hide at the same time when transitioning

ng-repeat animation not working

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

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.

ngAnimate on ngShow. Preventing animation when it starts first time

I was playing around with recently added angular.js animations feature, and this doesn't work as desired
<style>
.myDiv{
width:400px;
height:200px;
background-color:red;
}
.fadeIn-setup,.fadeOut-setup {
-webkit-transition: 1s linear opacity;
-moz-transition: 1s linear opacity;
-o-transition: 1s linear opacity;
transition: 1s linear opacity;
}
.fadeIn-setup{
opacity:0;
}
.fadeOut-setup{
opacity:1;
}
.fadeIn-setup.fadeIn-start {
opacity: 1;
}
.fadeOut-setup.fadeOut-start{
opacity:0;
}
</style>
<div ng-app>
<div ng-controller='ctrl'>
<input type='button' value='click' ng-click='clicked()' />
<div ng-show="foo == true" class='myDiv' ng-animate="{show: 'fadeIn', hide:'fadeOut'}">
</div>
</div>
</div>
function ctrl($scope){
$scope.foo = false;
$scope.clicked = function(){
$scope.foo = !($scope.foo);
}
}
http://jsfiddle.net/Kx4TS/1
it spoils away myDiv on the dom.ready and starts it fading out. Whereas it initially should be hidden. How to fix that?
This issue still persists even now with 1.2.22. However, one of these solutions solves it very easily.
After trying all the solutions mentioned here I wanted to specifically highlight cocoggu's suggestion to ac360 as it is by far the most concise and it "just works".
As he suggests, simply adding the ng-hide class to the offending element resolves the problem immediately. - It prevents the initial animation glitch and allows all subsequent animations to behave as expected.
Working Solution thanks to cocoggu
<div id="offending-element" class="ng-hide"></div>
I have found 2 different solutions for your problem
The easiest solution:
Add a inline style to the div style="display:none"
The other solution is to add an initial class to the div with ng-class="state"
and reset the class when the button is clicked
function ctrl($scope){
$scope.state = 'hide';
$scope.foo = false;
$scope.clicked = function() {
$scope.state = undefined;
$scope.foo = !($scope.foo);
}
}
together with the following css:
.hide {
display: none;
}
I think I would use the first and most simple solution
this should do the trick:
<div ng-class="ng-hide" ng-show="foo == true" class='myDiv'">
this is the important part here: ng-class="ng-hide"
EDIT:
As pointed out in the comments, the above would not work. It should look like this:
<div class="ng-hide" ng-show="foo == true" class='myDiv'">
This has been fixed now. Upgrade to 1.1.5.

Resources