AngularJS animate element on scope change - angularjs

I am new to AngularJS and at the moment I'm trying out AngularJS animations.
I wrote a CSS animation class that I would like to apply to a Twitter Bootstrap badge on the "Message" button when the value on the badge changes.
The unread message count is handled by the "MessagesCtrl" controller.
For now, the count changes on a button click ("Add Message").
Here is the complete application that I have so far:
<!DOCTYPE html>
<html ng-app="ExampleApp">
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular-animate.min.js></script>
<style rel="stylesheet">
#-webkit-keyframes shake {
0% { -webkit-transform: translate(2px, 2px) rotate(20deg); }
20% { -webkit-transform: translate(-4px, -4px) rotate(-20deg); }
40% { -webkit-transform: translate(2px, 2px) rotate(20deg); }
60% { -webkit-transform: translate(-2px, 2px) rotate(-20deg); }
80% { -webkit-transform: translate(4px, -4px) rotate(20deg); }
100% { -webkit-transform: translate(-2px, 2px) rotate(-20deg); }
}
.shake {
-webkit-animation-name: shake;
-webkit-animation-duration: 0.6s;
-webkit-transform-origin: 50% 50%;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: linear;
}
</style>
<script>
var app = angular.module("ExampleApp", ['ngAnimate']);
app.controller("MessagesCtrl", ['$scope', function ($scope) {
$scope.count = 0;
$scope.addMessage = function () {
$scope.count++;
};
}]);
app.directive('shake', ['$animate', 'MessagesCtrl', function ($animate) {
return {
scope: {
count: '='
},
link: function(scope, element, attrs) {
scope.$watch(attrs, function() {
$animate.addClass(element, 'shake').then(function() {
element.removeClass('shake');
});
});
}
};
}]);
</script>
</head>
<body ng-controller="MessagesCtrl">
<button class="btn btn-primary" type="button">
Messages <span shake id="badgeMessage" class="badge" ng-if="count > 0">{{ count }}</span>
</button>
<button ng-click="addMessage()">Add Message</button>
</body>
</html>
When you click the "Add Message" button, the unread count changes, but the shake animation isn't applied on the badge. Can anyone see why this isn't happening? I think the problem is that I'm
not including the $scope of the controller into the directive, but I'm not sure how to do this.
The animation is called inside the "shake" attribute directive.
Thank you for your time and assistance.

Pass count to the isolated scope you have set up:
<span shake id="badgeMessage" class="badge" count="count" ...
Also change your ng-if to ng-show.
Then watch the count:
scope.$watch('count', function(newValue, oldValue) {
if (newValue === oldValue) return;
$animate.addClass(element, 'shake').then(function() {
element.removeClass('shake');
});
});
Demo: http://plnkr.co/edit/pf5S0e705LK1q1OTGTcq?p=preview
If you want to continue to use ng-if instead you can do this to get it to animate the first time:
scope.$watch('count', function(newValue, oldValue) {
if (newValue === oldValue) return;
animate();
});
var animate = function() {
$animate.addClass(element, 'shake').then(function() {
element.removeClass('shake');
});
};
$timeout(animate);
Demo: http://plnkr.co/edit/sgSTOB5yqiaSgJL5Adl6?p=preview

Related

AngularJS with CSS3 Animations - Slideshow Issues

I am attempting to create a simple content slider with AngularJS. I am following this tutorial:https://www.sitepoint.com/creating-slide-show-plugin-angularjs/
However, the content is "jumping". The next slide appears below the current one; it does not appear in its appropriate place while the other is fading out. Here is an image that hopefully clarifies the issue:
The HTML:
<div class="row" id='topSlider' ng-controller="SliderCtrl">
<h2 class="col-sm-12">Latest Stories</h2>
<div class="col-sm-12">
<div class="slider" ng-repeat="story in stories" ng-show="story.visible">
<a class="containerLink" href="View/ViewPost?postID={{story.StoryID}}">
<div class="slide">
<h3 class="cursive">{{story.Title}}</h3>
<blockquote>{{story.StoryPara}}</blockquote>
</div>
</a>
</div>
</div>
<div class="row" id="arrows">
<span class="glyphicon glyphicon-arrow-left"></span>
<span class="glyphicon glyphicon-arrow-right"></span>
</div>
The controller:
app.controller("SliderCtrl", ["$scope", "$http", "$timeout", function ($scope, $http, $timeout) {
$scope.currentIndex = 0;
$http.post("/StoryWall/Home/GetLatestStories").then(function (response) {
$scope.stories = response.data;
$scope.stories[0].visible = true;
},
function (response) {
$scope.stories = response.data;
});
$scope.next = function () {
$scope.currentIndex < $scope.stories.length - 1 ? $scope.currentIndex++ : $scope.currentIndex = 0;
};
$scope.prev = function () {
$scope.currentIndex > 0 ? $scope.currentIndex-- : $scope.currentIndex = $scope.stories.length - 1;
};
$scope.$watch("currentIndex", function () {
$scope.stories.forEach(function (story) {
story.visible = false;
});
$scope.stories[$scope.currentIndex].visible = true;
});
var timer;
var sliderFunction = function () {
timer = $timeout(function () {
$scope.next();
timer = $timeout(sliderFunction, 2000);
}, 5000);
};
sliderFunction();
}]);
and the CSS:
.slider.ng-hide-add, .slider.ng-hide-remove {
-webkit-transition:all linear 1s;
-moz-transition:all linear 1s;
-o-transition:all linear 1s;
transition:all linear 1s;
display:block!important;
clear: none;
float: none;
}
.slider.ng-hide-add.ng-hide-add-active,
.slider.ng-hide-remove {
opacity: 0;
}
.slider.ng-hide-add,
.slider.ng-hide-remove.ng-hide-remove-active {
opacity: 1;
}
Please note, if I remove the CSS it works just fine. The containers hide and show as they should. However, I would like to be able to apply some animations. I am new to both CSS animations and AngularJS; I apologize if this is a redudant or obvious issue.
check this css -> https://daneden.github.io/animate.css/
it's my script
var app=angular.module("app",[]);
app.controller("control",function($scope){
$scope.tab="1";
});
you focus on ng-show method
for example
all slider item in div like that
<div ng-show="tab==1" class="slider-item animated slideInRight">
...............
</div>
and your button must be
<button ng-mouseover="tab=1">1</button>
I hope I could help you

Angular JS expression not evaluating

I have written a simple Angular JS code. I'm a beginner. However, one of my expressions is not getting evaluated. Need help. Please check the code below -
var myAppModule = angular.module('myAppModule', []);
myAppModule.controller('myController', function($scope){
// Hide colors by default
$scope.isHidden = true;
// a function, placed into the scope, which
// can toggle the value of the isHidden variable
$scope.showHideColors = function () {
$scope.isHidden = !$scope.isHidden;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html ng-app="myAppModule">
<head>
<title>Angular JS</title>
<script src="js/angular.min.js"></script>
<script src="js/myAppModule.js"></script>
<style>
body {
font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, sans-serif;
}
div {
margin: 20px;
padding: 20px;
font-size: 16px;
color:#ffffff;
}
#red {
background-color: red;
}
#green {
background-color: green;
}
#blue {
background-color: blue;
}
#purple {
background-color: purple;
}
#gray {
background-color: gray;
}
#olive {
background-color: olive;
}
</style>
</head>
<body ng-controller="myController">
<h2>AngularJS Socks</h2>
<p>Keep warm this winter with our 100% wool, 100% cool, AngularJS socks!</p>
<button ng-click="showHideColors()" type="button">
{{isHidden ? 'Show Available Colors' : 'Hide Available Colors'}}
</button>
<div id="red" ng-hide="isHidden">Red</div>
<div id="green" ng-hide="isHidden">Green</div>
<div id="blue" ng-hide="isHidden">Blue</div>
<div id="purple" ng-hide="isHidden">Purple</div>
<div id="gray" ng-hide="isHidden">Dark Slate Gray</div>
<div id="olive" ng-hide="isHidden">Olive</div>
</body>
</html>
The expression - {{isHidden ? 'Show Available Colors' : 'Hide Available Colors'}} is not getting evaluated but displaying as is on the button. No clue as to what i missed. Thanks in advance.
The code is missing closing bracket. You can see the working demo here - http://jsfiddle.net/me8j3zyc/
var app = angular.module('myAppModule', []);
app.controller('myController', function($scope) {
$scope.isHidden = true;
// a function, placed into the scope, which
// can toggle the value of the isHidden variable
$scope.showHideColors = function() {
$scope.isHidden = !$scope.isHidden;
} // <- This is missing.
});
This is because you havent closed your function
myAppModule.controller('myController', function($scope){
// Hide colors by default
$scope.isHidden = true;
// a function, placed into the scope, which
// can toggle the value of the isHidden variable
$scope.showHideColors = function() {
$scope.isHidden = !$scope.isHidden;
}})
Your expression is fine, but you have a typo error in your JS file:
var myAppModule = angular.module('myAppModule', []);
myAppModule.controller('myController', function($scope) {
// Hide colors by default
$scope.isHidden = true;
// a function, placed into the scope, which
// can toggle the value of the isHidden variable
$scope.showHideColors = function() {
$scope.isHidden = !$scope.isHidden;
} //MISSING
});
You can try this:
<button ng-if="isHidden" ng-click="showHideColors()" type="button">Show Available Colors</button>
<button ng-if="!isHidden" ng-click="showHideColors()" type="button">Hide Available Colors</button>

toggling one directive effects other dircetive inside ngrepeat

I wrote an angularjs directive to show and hide ajax spinners. The visibility of the spinner is toggled by show and hide buttons whose functionality is written inside the MainController. There is a variable inside the controller which is set to true and false based on the button click. This variable is passed to the directive using isolate scope. When I try to toggle one spinner, all the other spinners are also visible. How can I change my code to only toggle the particular spinner.
https://plnkr.co/edit/AFmBVbHaBPk66T7UjPC5?p=preview
// Code goes here
angular.module('app',[])
.controller('MainController',[MainController])
.directive('loadingDirective',[loadingDirective]);
function MainController(){
var mc = this;
mc.showMe = showMe;
mc.hideMe = hideMe;
mc.loading = false;
function showMe(){
mc.loading = true;
}
function hideMe(){
mc.loading = false;
}
}
function loadingDirective() {
return {
restrict: 'E',
replace:true,
scope:{
loading:"=loading"
},
template: '<span class="spinner">Loading…</span>',
link: function (scope, element, attr) {
scope.$watch('loading', function (val) {
if (val)
$(element).show();
else
$(element).hide();
});
}
};
}
/* Styles go here */
.spinner {
position: relative;
/* [1] */
display: inline-block;
width: 1em;
/* [2] */
height: 1em;
/* [2] */
font-size: 32px;
/* [3] */
border-bottom: 1px solid;
/* [4] */
vertical-align: middle;
overflow: hidden;
/* [5] */
text-indent: 100%;
/* [5] */
-webkit-animation: 0.5s spinner linear infinite;
animation: 0.5s spinner linear infinite;
/**
* 1. Make the spinner a circle.
*/
/**
* The (optically) non-spinning part of the spinner.
*
* 1. Border around entire element fills in the rest of the ring.
* 2. Paler than the part that appears to spin.
*/
}
.spinner, .spinner:after {
border-radius: 100%;
/* [1] */
}
.spinner:after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border: 1px solid;
/* [1] */
opacity: 0.5;
/* [2] */
}
/**
* Size variants (built by adjusting `font-size`).
*/
.spinner--small {
font-size: 16px;
}
.spinner--large {
font-size: 64px;
}
/**
* Color overrides.
*/
.spinner--light {
color: #fff;
}
.spinner--dark {
color: #333;
}
#-webkit-keyframes spinner {
to {
-webkit-transform: rotate(360deg);
}
}
#keyframes spinner {
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body>
<h1>Hello Plunker!</h1>
<div ng-controller="MainController as mc">
<div ng-repeat="i in [1,2,3,4,5]">
<loading-directive loading="mc.loading"></loading-directive>
<button ng-click="mc.showMe()">show</button>
<button ng-click="mc.hideMe()">hide</button>
</div>
</div>
</body>
</html>
If you want the spinners to have their own states, then they should be controlled by different variables.
In your example it is achievable by using an array to hold the variables
<div ng-repeat="i in [1,2,3,4,5]">
<loading-directive loading="mc.loading[i]"></loading-directive>
<button ng-click="mc.show(i)">show</button>
<button ng-click="mc.hide(i)">hide</button>
</div>
mc.loading = {};
function show(i){
mc.loading[i] = true;
}
function hide(i){
mc.loading[i] = false;
}
In a more real case example where you have some data and you use ng-repeat over them, you should assign the loading states inside the elements themselves.
This is a common technique to assign state to each items in ng-repeat
mc.fruits = [
{name:"apple"},
{name:"orange"},
{name:"starfruit"}
]
function load(fruit) { fruit.loading = true; }
function noLoad(fruit) { fruit.loading = false; }
<div ng-repeat="fruit in mc.fruits">
<loading-directive loading="fruit.loading"></loading-directive>
{{fruit.name}}
<button ng-click="mc.load(fruit)">show</button>
<button ng-click="mc.noLoad(fruit)">hide</button>
</div>
Working Plunkr: https://plnkr.co/edit/peGDxYJzKJgiHuPp4zmQ
You needed to define the isolated scope in the directive correctly. Essentially, your directive was still dependent on the controller as you were using the same variable mc.loading to determine the state of all directive instances.
By moving the deterministic variable $scope.loading as well as the buttons inside the directive, we are completely isolating each directive instance and making them all completely independent units.
HTML:
<div ng-controller="MainController as mc">
<div ng-repeat="i in [1,2,3,4,5]">
<loading-directive></loading-directive>
</div>
</div>
JS:
angular.module('app',[])
.controller('MainController',[MainController])
.directive('loadingDirective',[loadingDirective]);
function MainController(){
}
function loadingDirective() {
return {
restrict: 'E',
replace:true,
scope:{},
template: '<div><span ng-if="loading" class="spinner">Loading…</span>'
+ '<button ng-click="showMe()">show</button>'
+ '<button ng-click="hideMe()">hide</button></div>',
controller: function($scope) {
$scope.showMe = showMe;
$scope.hideMe = hideMe;
function showMe(){
$scope.loading = true;
}
function hideMe(){
$scope.loading = false;
}
}
};
}
The loading variable watched is common for all the directives used, hence when the model is changed the watch condition runs 5 times in your case, removing all the spinners.
I used the index to see what is being hidden or shown,
Updated fiddle: https://plnkr.co/edit/Jjfk6v7TJZHlQicM45ln?p=preview
HTML
<div ng-repeat="i in [1,2,3,4,5]">
<loading-directive data-index="{{$index}}" loading="mc.loading" offset="mc.offset"></loading-directive>
<button ng-click="mc.showMe($index)">show</button>
<button ng-click="mc.hideMe($index)">hide</button>
</div>
Angular
angular.module('app',[])
.controller('MainController',[MainController])
.directive('loadingDirective',[loadingDirective]);
function MainController(){
var mc = this;
mc.showMe = showMe;
mc.hideMe = hideMe;
mc.loading = false;
mc.offset =-1;
function showMe(offset){
mc.loading = true;
mc.offset = offset;
}
function hideMe(offset){
mc.loading = false;
mc.offset = offset;
console.log(offset);
}
}
function loadingDirective() {
return {
restrict: 'E',
replace:true,
scope:{
loading:"=loading",
offset:"=offset"
},
template: '<span class="spinner">Loading…</span>',
link: function (scope, element, attr) {
scope.$watch('[loading, offset]' , function (val) {
if(attr.index == scope.offset || scope.offset == -1){
if (val[0])
element.show();
else
element.hide();
}
});
}
};
}
SCRIPT:
function showMe(i){
mc.loading = true;
i=true;
}
IN HTML ADD THIS
<div ng-repeat="i in [1,2,3,4,5]">
<span><loading-directive loading="mc.loading" ng-show="i==true"></loading-directive>
<button ng-click="mc.showMe(i)">show</button></span>
<button ng-click="mc.hideMe()">hide</button>
</div>

ng-repeat staggered animation on page load in Angular 1.4+

In this other question, the answer provides a workaround (a dirty hack) to make ng-enter animations work on page load.
But, after upgrading to 1.4, this statement:
$rootElement.data("$$ngAnimateState").running = false;
does not work anymore.
Note: Using $timeout is not an option for me, because I've tried but I need to give a big timeout to make it work (more than 1.5 seconds, unacceptable).
You can call animation manually with $animateCss service. Please pay attention to animateOnLoad directive:
angular.module('app', ['ngAnimate']);
angular.module('app').controller('categoriesCtrl', ['$scope', function($scope) {
$scope.categories = ['12345', '6789', '9876', '54321'];
}]);
angular.module('app').directive('animateOnLoad',['$animateCss', function($animateCss) {
return {
'link': function(scope, element) {
$animateCss(element, {
'event': 'enter',
structural: true
}).start();
}
};
}]);
.category {
display:block;
font-size:20px;
background:black;
color:#fff;
margin:10px;
padding:10px;
text-align:center;
}
.category.ng-enter {
/* standard transition code */
-webkit-transition: 2s linear all;
transition: 2s linear all;
opacity:0;
}
.category.ng-enter.ng-enter-active {
/* standard transition styles */
opacity:1;
}
<script src="https://code.angularjs.org/1.4.4/angular.js"></script>
<script src="https://code.angularjs.org/1.4.4/angular-animate.js"></script>
<div data-ng-app="app" data-ng-controller="categoriesCtrl">
<div class="category" ng-repeat="category in categories" animate-on-load>
{{category}}
</div>
</div>

Programatically changing ngShow doesn't make use of ngAnimate classes

I'm trying to trigger ngShow via my controller, it works, but it doesn't make use of the ngAnimate classes that I need to get a fade transition.
It works like it should if I use a button to toggle ngShow, but not if I toggle it programmatically. Is this expected behavior? Can I get around it?
Plunk:
http://plnkr.co/edit/swJDP1KBBxcRfK9auYPs?p=preview
<body ng-controller="MainCtrl">
<input type="checkbox" ng-model="visible">
<div ng-show="visible" class="wrap" role="document">
Hello
</div>
</body>
var app = angular.module( "app", ['ngAnimate']);
app.run(function($rootScope) {
$rootScope.visible = false;
});
app.controller('MainCtrl', function($rootScope, $scope) {
$rootScope.visible = true;
});
.wrap.ng-hide-add-active {
display: block!important;
transition: 0.5s ease-in-out all;
}
.wrap.ng-hide-remove-active {
display: block!important;
transition: 0.5s ease-in-out all;
transition-delay: 0.5s;
}
.wrap.ng-hide {
opacity: 0;
}
You're run block and controller code likely get executed in the same digest cycle so Angular doesn't see the visible variable changing. This would work if you put your controller code in a timeout. e.g.
app.controller('MainCtrl', function($rootScope, $scope, $timeout) {
$timeout(function() {
$rootScope.visible = true;
});
});
http://plnkr.co/edit/5IhGE3ol5kI64OlT1e8v?p=preview

Resources