Angular JS animation not working, (using ngAnimate and TweenMax) - angularjs

ok, here is my code:
(function(){
//Define angular main module - module -
var app = angular.module('module', ['ngAnimate']);
//Define controller -TimeLine-
app.controller('TimeLine', function(){
this.initialize = true;
});
//Define controller -PersonalGravatar-
app.controller('PersonalGravatar', function(){
this.email = "mail#gmail.com";
this.getImage = function(email) {
// MD5 (Message-Digest Algorithm) by WebToolkit
var size = size || 460;
return 'http://www.gravatar.com/avatar/' + MD5(email) + '.jpg?s=' + size;
};
});
//Define animation - gravatar-
app.animation(".gravatar", function() {
console.log("im displaying correctly");
//that's it, this next 'return' is not working.
return {
console.log("im NOT displaying in console");
enter: function(element, done){
TweenMax.to(element, 2, { css:{left:500, onComplete:done} } );
},
leave: function(element, done){
// TweenMax.to(element, 2, { css:{left:500, onComplete:done} } );
}
};
});
})();
then my html code is:
<html class="no-js" ng-app="module">
... more code ...
<div id="app-main-container" ng-controller="TimeLine as animations">
<div id="gravatar" class="gravatar" ng-controller="PersonalGravatar as gravatar"
ng-if="animations.initialize">
<img ng-src="{{gravatar.getImage(gravatar.email)}}" alt="">
</div>
</div> <!-- /app main container -->
<script src="../1.2.18/angular.min.js"></script>
<cript src="../1.2.18/angular-animate.min.js"> </script>
<script src="../1.12.1/TweenMax.min.js"></script>
<script src="js/main.js"></script>
so I'm new in angularJS, I don't no why app.animation is not returning any animation.. thanks a LOT!!

Just $timeout...
app.controller('TimeLine', function($scope, $timeout){
return $timeout(function() {
$scope.initialize = true;
}, 100);
});

I created a Plunk of your example animating the enter and leave.
Notice how you no longer need to wrap your CSS properties in a css object.
enter: function(element, done){
TweenMax.from(element, 1, {x:500, autoAlpha: 0, scale: 0.5, onComplete:done});
},
leave: function(element, done){
TweenMax.to(element, 1, {x:500, autoAlpha: 0, scale: 0.5, onComplete:done});
}
Plunker http://plnkr.co/edit/6tQFdA?p=preview

Related

AngularJS HotTowel invoke spinner during ajax call

I am trying to invoking the spinner during any xhr call within the application. Whereas the spinner appeared when I click at menu or route to different page.
Index page
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel (optional) -->
<div data-cc-sidebar data-ng-controller="sidebar as vm">
<ul class="sidebar-menu">
<li data-ng-repeat="r in vm.navRoutes">
</li>
</ul>
</div>
</section>
<!-- /.sidebar -->
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper" data-ng-controller="shell as vm">
<!-- Content Header (Page header) -->
<section class="content-header"></section>
<!-- Main content -->
<section class="content">
<!-- Your Page Content Here -->
<div data-ng-show="vm.isBusy" class="page-splash dissolve-animation">
<div data-cc-spinner="vm.spinnerOptions"></div>
<div class="page-splash-message page-splash-message-subtle">{{vm.busyMessage}}</div>
</div>
<div data-ng-view class="shuffle-animation"></div>
</section><!-- /.content -->
</div><!-- /.content-wrapper -->
Shell.js
I have changed the shell.js file according to following instruction.
From
$rootScope.$on(events.spinnerToggle, function (data))
To
$rootScope.$on(events.spinnerToggle, function (event, data))
And found from following link comments
http://johnpapa.net/hot-towel-angular/
(function () {
'use strict';
var controllerId = 'shell';
angular.module('app').controller(controllerId,
['$rootScope', 'common', 'config', shell]);
function shell($rootScope, common, config) {
var vm = this;
var logSuccess = common.logger.getLogFn(controllerId, 'success');
var events = config.events;
vm.busyMessage = 'Please wait ...';
vm.isBusy = true;
vm.spinnerOptions = {
radius: 40,
lines: 7,
length: 0,
width: 30,
speed: 1.7,
corners: 1.0,
trail: 100,
color: '#F58A00'
};
activate();
function activate() {
logSuccess('SIPPRES loaded!', null, true);
common.activateController([], controllerId);
}
function toggleSpinner(on) { vm.isBusy = on; }
$rootScope.$on('$routeChangeStart',
function (event, next, current) { toggleSpinner(true); }
);
$rootScope.$on(events.controllerActivateSuccess,
function (data) { toggleSpinner(false); }
);
$rootScope.$on(events.spinnerToggle,
function (event,data) { toggleSpinner(data.show); }
);
};
})();
Directive looks like
app.directive('ccSpinner', ['$window', function ($window) {
// Description:
// Creates a new Spinner and sets its options
// Usage:
// <div data-cc-spinner="vm.spinnerOptions"></div>
var directive = {
link: link,
restrict: 'A'
};
return directive;
function link(scope, element, attrs) {
scope.spinner = null;
scope.$watch(attrs.ccSpinner, function (options) {
if (scope.spinner) {
scope.spinner.stop();
}
scope.spinner = new $window.Spinner(options);
scope.spinner.spin(element[0]);
}, true);
}
}]);
Thank you
I found my solution. Above code just working fine. Unfortunately, I placed my spinner.spinnerShow(); at wrong place in controller. Bellow controller function just working fine.
function updateColor(updatedColor) {
spinner.spinnerShow();
return datacontextSetting.updateColor(updatedColor).then(function (data) {
$scope.newColor = data;
$uibModalInstance.close($scope.newColor);
}, function (response) {
$scope.frmColor.$valid = false;
// Here is where we can catch the errors and start using the response.
if (!angular.isUndefined(response.statusCode)) {
$scope.errorMessage = response.statusCode + "\n";
}
if (response.modelState) {
for (var key in response.modelState) {
$scope.errorMessage += response.modelState[key] + "\n";
}
}
if (response.message) {
$scope.errorMessage += response.message;
}
}).finally(function () {
spinner.spinnerHide();
});
};
Thanks

How to disable and showing another button text untill the button is loaded in AngularJS?

How to disable the button untill the button is loaded in AngularJS?
This is my directive for indicate data loading status, and disable button untill $http request is processed.
But the problem is when i reload the page the button will automatically disable and reloaded.How to restrict that?
One more issue.
If I have two more button in the same page when i submit one of that button the entire button will disable and showing loading...
I need two things
When a page is loaded the the other buttons are not disable not showing loading...I want to disable the entire page and currrent submit button should be shown loading...
if one button is submit the other butttons are not showing loading...
This is my code script.js
var myApp = angular.module("myApp", []);
myApp.controller("myCtrl", function($scope, $http, $timeout) {
$scope.save = function() {
$http.pendingRequests.length = 1;
$timeout(function() {
$http.pendingRequests.length = 0;
}, 1000);
};
$scope.submit = function() {
$http.pendingRequests.length = 1;
$timeout(function() {
$http.pendingRequests.length = 0;
}, 1000);
};
});
myApp.directive("disableonrequest", function($http,$timeout)
{
return function(scope, element, attrs)
{
scope.$watch(function()
{
return $http.pendingRequests.length > 0;
}, function(request)
{
if (!request)
{
element.attr('disabled', false);
element.html("<span >" + attrs.notloading + "</span>");
}
else
{
element.attr('disabled', true);
element.html("<span >" + attrs.loading + "</span><i class='fa fa-refresh fa-spin'></i>");
}
});
}
});
view.html
<body ng-app="myApp" ng-controller="myCtrl">
<button ng-click="save()" loading="Loading..." notloading="Save" disableonrequest></button>
<button ng-click="submit()" loading="Loading..." notloading="Submit" disableonrequest></button>
</body>
When i click on each button both button will disable and show loading...
how to restrict that?
I want to disable the entire page when a button is clicked and submitted button should be shown loading...
Please help me. I am new in Angular JS
This happens because your $watch depends on the global variable $http.pendingRequests. And when the value of the variable changes, then angular starts change function for the two directives.
To avoid this, use different variables to store values loading. Example can watch jsfiddle
var myApp = angular.module("myApp", []);
myApp.controller("myCtrl", function($scope, $http, $timeout) {
$scope.saving = false;
$scope.submiting = false;
$scope.save = function() {
$http.pendingRequests.length = 1;
$scope.saving = true;
$timeout(function() {
$scope.saving = false;
$http.pendingRequests.length = 0;
}, 1000);
};
$scope.submit = function() {
$scope.submiting = true;
$http.pendingRequests.length = 1;
$timeout(function() {
$scope.submiting = false;
$http.pendingRequests.length = 0;
}, 1000);
};
});
myApp.directive("disableonrequest", function() {
return {
restrict: 'A',
scope: {
notloading: "#",
loading: "#",
proccess:"=",
},
link: function(scope, element, attrs) {
scope.$watch('proccess', function(request) {
console.log(request,element);
if (!request) {
element.attr('disabled', false);
element.html("<span >" + scope.notloading + "</span>");
} else {
element.attr('disabled', true);
element.html("<span >" + scope.loading + "</span><i class='fa fa-refresh fa-spin'></i>");
}
});
},
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp" ng-controller="myCtrl">
<button ng-click="save()" loading="Loading..." proccess="saving" notloading="Save" disableonrequest></button>
<button ng-click="submit()" loading="Loading..." proccess="submiting" notloading="Submit" disableonrequest></button>
</body>
UPDATED
Solution with block all page.
Live example on jsfiddle
var myApp = angular.module("myApp", []);
myApp.controller("myCtrl", function($scope, $http, $timeout) {
$scope.pendingRequests = $http.pendingRequests;
$scope.save = function() {
$http.pendingRequests.length++;
$timeout(function() {
$http.pendingRequests.length--;
}, 1000);
};
$scope.submit = function() {
$http.pendingRequests.length++;
$timeout(function() {
$http.pendingRequests.length--;
}, 1000);
};
});
myApp.directive("blockWhileLoad", function() {
return {
restrict: 'EA',
replace:true,
transclude:true,
scope: {
proccess: "=",
},
template:'<div><div ng-transclude></div><div ng-class="{\'blocker\':proccess>0}"></div></div>',
link: function(scope, element, attrs) {
},
}
});
.errors {
color: maroon
}
.blocker
{
position: fixed;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px;
z-index: 1038;
background: rgba(0,0,0,.2);
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp" ng-controller="myCtrl">
<fieldset ng-disabled="pendingRequests.length>0">
<input />
<button ng-click="save()">Save</button>
<button ng-click="submit()">Submit</button>
</fieldset>
<block-while-load proccess="pendingRequests.length">
<input />
www.google.com
<button ng-click="save()">Save</button>
<button ng-click="submit()">Submit</button>
</block-while-load>
</body>
Remember, angularjs use promises and the $http service extend $q, so you can use promises with that service.
From: https://docs.angularjs.org/api/ng/service/$http
//Before the request, set element as loading
element.attr('disabled', true);
element.html("<span >" + attrs.loading + "</span><i class='fa fa-refresh fa-spin'></i>");
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// when the http is fully loaded, set visible
element.attr('disabled', false);
element.html("<span >" + attrs.notloading + "</span>");
}, function errorCallback(response) {
// show an error message or just put the button available again
element.attr('disabled', false);
element.html("<span >" + attrs.notloading + "</span>");
});

handsontable in an angularjs directive - render an anchor that has an ng-click

So I'm using Handsontable to render a grid. (Yes, I am NOT using the ngHandsontable. I started out with that but ran into problems and so I went with just rendering a Handsontable from an angularjs directive.)
I want one column to hold an anchor tag.
I want the anchor tag to have the angularjs ng-click directive.
Everything renders correctly but the ng-click is not called.
Here is my example.
var APP = angular.module('APP', ['controllers']);
angular.module('controllers',[])
.controller('testController', function ($scope) {
$scope.doNgClick = function() {
alert('ng-click');
// console.log('ng-click');
};
$scope.simple = [
{
test: "<a href='javascript:void(0);' ng-click='doNgClick()'>Test</a>"
// test: "<a ng-click='doNgClick()'>Test</a>"
}
];
});
APP.directive('htable',function($compile) {
var directive = {};
directive.restrict = 'A';
directive.scope = {
data : '='
};
directive.link = function(scope,element,attrs) {
var container = $(element);
// var safeHtmlRenderer = function (instance, td, row, col, prop, value, cellProperties) {
// var escaped = Handsontable.helper.stringify(value);
// td.innerHTML = escaped;
// return td;
// };
var settings = {
data: scope.data,
readOnly: true,
colHeaders: ['Link'],
columns: [
{
data: "test",
renderer: "html",
// renderer: safeHtmlRenderer,
readyOnly: true
}
]
};
var hot = new Handsontable( container[0], settings );
hot.render();
// console.log(element.html());
// $compile(element.contents())(scope);
};//--end of link function
return directive;
});
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="//handsontable.com/dist/handsontable.full.css">
</head>
<body>
<div ng-app="APP">
<div ng-controller="testController">
<div htable data="simple"></div>
</div
</div>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.24/angular.min.js"></script>
<script src="//handsontable.com/dist/handsontable.full.js"></script>
</body>
</html>
After much reading and digging here is my own answer.
//-- With help from the following:
//--
//-- http://stackoverflow.com/questions/18364208/dynamic-binding-of-ng-click
//-- http://weblogs.asp.net/dwahlin/creating-custom-angularjs-directives-part-3-isolate-scope-and-function-parameters
//--
var APP = angular.module('APP', ['controllers']);
angular.module('controllers',[])
.controller('testController', function ($scope) {
$scope.click = function(msg) {
console.log('ctrl_doNgClick: ng-click: msg: '+msg);
};
$scope.simple = [
{
test: "<a href='javascript:void(0);' ng-click='dir_ctrl_click(\"blah1,blah1\")'>Test 1</a>"
},
{
test: "<a href='javascript:void(0);' ng-click='doClick(\"blah2,blah2\")'>Test 2</a>"
},
{
test: "<a href='javascript:void(0);' ng-click='doClick(\"blah3,blah3\")'>Test 3</a>"
}
];
});
APP.directive('htable',function($compile) {
var directive = {};
directive.restrict = 'A';
directive.scope = {
data : '=',
click : '&'
};
directive.controller = function($scope) {
$scope.dir_ctrl_click = function( msg ) {
console.log('controller: dir_ctrl_click: click via the directive controller method');
$scope.click()(msg);
};
};
directive.link = function(scope,element,attrs) {
var container = $(element);
scope.doClick = function(msg) {
console.log('link: doClick: click via the directive link method');
scope.click()(msg);
};
var linkHtmlRenderer = function (instance, td, row, col, prop, value, cellProperties) {
//-- here is the magic that works
//-- the method, in ng-click, must either be defined here in the link method or in the controller method (the example data contains both)
var el = angular.element(td);
el.html($compile(value)(scope));
return el;
};
var settings = {
data: scope.data,
readOnly: true,
colHeaders: ['Link'],
columns: [
{
data : "test",
renderer : linkHtmlRenderer,
readyOnly : true
}
]
};
var hot = new Handsontable( container[0], settings );
// hot.render();
};//--end of link function
return directive;
});
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="http://handsontable.com/dist/handsontable.full.css">
</head>
<body>
<div ng-app="APP">
<div ng-controller="testController">
<div htable data="simple" click="click"></div>
</div
</div>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.24/angular.min.js"></script>
<script src="http://handsontable.com/dist/handsontable.full.js"></script>
</body>
</html>

AngularJS: Custom directive is not correctly computing data requirements

I am building a custom directive to allow for a glyphicon to serve as an indicator (color), a notice (tooltip), a simple action (single click) and an ability for customized action in a modal (double click).
Here is a plunker of my progress.
On first load the colors are correct -- gray if value = 0, green for value of 1 and red for value of 2. The click also is correct -- a 0 clicked becomes 1, a 1 clicked becomes 2 and a 2 clicked becomes 1.
However, the colors are not responding correctly to the clicks. The first click seems to be ignored because the color stays the same and the second click does trigger a color change but now 1 = red and 2 = green (backwards).
Can anyone see what I am doing wrong that would make the color scheme fail?
Here is the code from the plunker --
app.js
(function() {
angular.module('app', ['ui.bootstrap'])
.directive('sglclick', SingleClickDirective)
.directive('loanProgressIcon', LoanProgressIconDirective)
.controller('MainController', MainController);
function SingleClickDirective($parse) {
return {
restrict: 'A',
link: function(scope, element, attr) {
var fn = $parse(attr['sglclick']);
var delay = 300,
clicks = 0,
timer = null;
element.on('click', function(event) {
clicks++; //count clicks
if (clicks === 1) {
timer = setTimeout(function() {
fn(scope, {
$event: event
});
clicks = 0; //after action performed, reset counter
}, delay);
} else {
clearTimeout(timer); //prevent single-click action
clicks = 0; //after action performed, reset counter
}
});
}
};
}
function LoanProgressIconDirective($compile) {
var progressMarkers = [{
'id': 1,
'cat': 'its_list',
'glyph': 'list-alt',
'tip': 'ITS List Verfified'
}, {
'id': 2,
'cat': 'fsa_compliant',
'glyph': 'home',
'tip': 'FSA Eligibility'
}, {
'id': 3,
'cat': 'has_liens',
'glyph': 'star',
'tip': 'Prior Lien Verfied'
}, {
'id': 4,
'cat': 'valid_leases',
'glyph': 'leaf',
'tip': 'Leases Valid'
}];
var statusColors = [
{ val: 0, color: '#CCC', class: 'pending'},
{ val: 1, color: '#006837', class: 'completed'},
{ val: 2, color: '#900', class: 'overdue'}
];
return {
restrict: 'A',
require : 'ngModel',
link: linker,
templateUrl: 'loanProgress.html',
scope: {
cat: '#',
ngModel: '='
}
};
function linker(scope, element, attrs, ctrl) {
scope.loan = {
id: progressMarkers[scope.cat]['id'],
glyphicon: progressMarkers[scope.cat]['glyph'],
tooltip: progressMarkers[scope.cat]['tip']
};
var styleChange = function () {
scope.loan.style = statusColors[scope.ngModel]['color'];
};
styleChange();
var setter = ctrl.$setViewValue;
ctrl.$setViewValue = function() {
setter.apply(this, arguments);
styleChange();
};
scope.progClicked = function() {
if(parseInt(scope.ngModel) === 0){
ctrl.$setViewValue(1);
} else if(parseInt(scope.ngModel) === 1){
ctrl.$setViewValue(2);
} else if(parseInt(scope.ngModel) === 2){
ctrl.$setViewValue(1);
}
};
scope.progDblClicked = function() {
alert('Icon ' + scope.ngModel + ' was double clicked.');
};
}
}
function MainController($scope) {
$scope.loan = {
its_list: 1,
fsa_compliant: 2,
has_liens: 1,
valid_leases: 0
};
}
})();
index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="jquery#*" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script data-require="bootstrap#3.0.2" data-semver="3.0.2" src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script>
<script data-require="angular.js#1.2.4" data-semver="1.2.4" src="http://code.angularjs.org/1.2.4/angular.js"></script>
<script data-require="ui-bootstrap#*" data-semver="0.11.0" src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.0.min.js"></script>
<link data-require="bootstrap-css#3.0.2" data-semver="3.0.2" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css" />
<link rel="stylesheet" href="app.css" />
<script src="app.js"></script>
</head>
<body ng-controller="MainController">
<div class="container">
<div class="row">
<div class="col-xs-12">
<table class="table table-striped">
<thead>
<tr>
<th colspan="4" class="text-left">LOAN PROGRESS ICONS</th>
</tr>
</thead>
<tbody>
<tr>
<th>ITS</th>
<th>FSA</th>
<th>LIEN</th>
<th>LEASES</th>
</tr>
<tr>
<td>
<span loan-progress-icon cat="0" ng-model="loan.its_list"></span>
</td>
<td>
<span loan-progress-icon cat="1" ng-model="loan.fsa_compliant"></span>
</td>
<td>
<span loan-progress-icon cat="2" ng-model="loan.has_liens"></span>
</td>
<td>
<span loan-progress-icon cat="3" ng-model="loan.valid_leases"></span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<p>{{loan | json}}</p>
</div>
</div>
</div>
</body>
</html>
Directive Template:
<span sglclick="progClicked()" ng-dblclick="progDblClicked()" class="glyphicon glyphicon-{{loan.glyphicon}}" tooltip="{{loan.tooltip}}" style="font-size:18px;color:{{loan.style}};cursor:pointer;"></span>
and app.css
th, td{
text-align:center;
}
.row{
margin: 15px 0;
}
.completed{
color: #006837;
}
.pending{
color: #CCCCCC;
}
.overdue{
color: #990000;
}
Thanks in advance!
The first problem is due to the fact that on is a jqLite/jQuery method and does not trigger the digest loop for you. This means that the UI will not be updated to reflect the changes to the model.
You either need to wrap the code that affects the model in a call to $apply or use $timeout instead of setTimeout.
With $apply:
timer = setTimeout(function() {
scope.$apply(fn(scope, {
$event: event
}));
clicks = 0;
}, delay);
With $timeout:
element.on('click', function(event) {
clicks++; //count clicks
if (clicks === 1) {
timer = $timeout(function() {
fn(scope, {
$event: event
});
clicks = 0;
}, delay);
} else {
$timeout.cancel(timer); //prevent single-click action
clicks = 0; //after action performed, reset counter
}
});
The second issue is the following code:
var setter = ctrl.$setViewValue;
ctrl.$setViewValue = function() {
setter.apply(this, arguments);
styleChange();
};
After calling $setViewValue the internal ngModelWatch will not detect the change and update the ngModel in your directive until later when the digest loop runs. Currently styleChange runs before this happens.
To run styleChange after the digest loop has finished you can use $timeout:
ctrl.$setViewValue = function() {
setter.apply(this, arguments);
$timeout(styleChange);
};
Demo: http://plnkr.co/edit/5MfQk49NKWFiEOqS2r5V?p=preview

AngularJS animate image on src change

I have an AnuglarJS app, where I load/change some images from a webservice...
Controller
.controller('PlayerCtrl', function($scope, programService) {
....
programService.refresh(function(data) {
$scope.program = data;
});
....
Template
<img src="{{program.image}}" />
When my app updates from the webservice the images changes as expected, I just want to make an fadeout / fadein when this happens, how can that be done?
Is it possible to always make a fadeout/in when a image src changes?
Thanks for the responses -
I ended up doing this, and it works ;)
--- Directive ---
.directive('fadeIn', function($timeout){
return {
restrict: 'A',
link: function($scope, $element, attrs){
$element.addClass("ng-hide-remove");
$element.on('load', function() {
$element.addClass("ng-hide-add");
});
}
};
})
--- Template ---
<img ng-src="{{program.image}}" class="animate-show" fade-in />
--- CSS ---
.animate-show.ng-hide-add, .animate-show.ng-hide-remove {
transition: all linear 0.5s;
display: block !important;
}
.animate-show.ng-hide-add.ng-hide-add-active, .animate-show.ng-hide-remove {
opacity: 0;
}
.animate-show.ng-hide-add, .animate-show.ng-hide-remove.ng-hide-remove-active {
opacity: 1;
}
Update 1.5.x - with Angular 1.5.x you can use ngAnimateSwap to achieve this effect.
Based on pkdkk's answer and the Angular.js 1.3.6 sources, my solution is as such (the CSS animation part is as used for standard ngShow):
// Copied from the Angular's sources.
var NG_HIDE_CLASS = 'ng-hide';
var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
app.directive('myFadeIn', function($animate, $timeout) {
return {
restrict: 'A',
link: function(scope, element, attrs){
element.addClass("ng-hide");
element.on('load', function() {
$timeout(function () {
$animate.removeClass(element, NG_HIDE_CLASS, {
tempClasses: NG_HIDE_IN_PROGRESS_CLASS
});
});
});
}
}
});
As christoph has mentioned, you should watch using $watch on the image source change.
But first make sure you use the ng-src rather than the src for the image tag.
<image id="new" ng-src="program.image" />
$scope.$watch('program.image', function(newValue, oldValue) {
if(newValue===oldValue) return;
$('img#new').hide();
$('img#new').fadeIn("slow", function() {});
})
In case others end up here wanting to perform animations on change of a background image, I'll post what I ended up using.
This directive assumes it's attached to a template like this:
<!-- Full screen background image and scarecrow for onload event-->
<div class="full-screen-image" data-background-image="{{backgroundImageUrl}}"></div>
<img class="hidden-full-screen-image hidden" data-ng-src="{{backgroundImageUrl}}"></div>
We want to set the background image source for the <div>, but attach an onload event so we know when the new image has arrived. To do that, we use an <img> with a .hidden class that has .hidden {display: none;}. Then we use the following directive to dynamically set the div's background image source and perform a fade to white then back from white on image change:
/***
*
* Directive to dynamically set background images when
* controllers update their backgroundImageUrl scope
* variables
*
* Template: <div data-background-image="{{backgroundImageUrl}}" />
* AND <img data-background-image="{{backgroundImageUrl}}" class="image-onload-target hidden" />
*
***/
var angular = require('angular');
angular.module('BackgroundImage', [])
.directive('backgroundImage', [
"$timeout",
function ($timeout) {
return function(scope, element, attrs){
attrs.$observe('backgroundImage', function(value) {
/***
*
* Define a callback to trigger once the image loads.
* The value provided to this callback = the value
* passed to attrs.$observe() above
*
***/
var imageLoadedCallback = function(value) {
// once the image load event triggers, remove the event
// listener to ensure the event is called only once
fadeOut();
target.removeEventListener('load', imageLoadedCallback);
$timeout(function() {
fadeIn(value);
}, 700);
}
/***
*
* Define fade in / out events to be called once a new image
* is passed to the attrs.backgroundImage in the directive
*
***/
var fadeOut = function() {
element.css({'opacity': '0'})
};
var fadeIn = function(value) {
element.css({
'background': 'url(' + value +') no-repeat center center fixed',
'background-size' : 'cover',
'opacity': '1'
});
};
// add an onload event to the hidden-full-screen-image
var target = document.querySelector('.image-onload-target');
target.addEventListener('load', imageLoadedCallback(value));
});
};
}]);
Working with Angular makes me love React all the more...
I know its late but according to #Aides answer i am posting here an working example that how can you achieve animation with change in ng-src using ngAnimateSwap (with Angular 1.5.x). I hope this helps someone in future:
HTML Markup:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-ngAnimateSwap-directive-production</title>
<link href="animations.css" rel="stylesheet" type="text/css">
<script src="//code.angularjs.org/snapshot/angular.min.js"></script>
<script src="//code.angularjs.org/snapshot/angular-animate.js"></script>
<script src="script.js"></script>
<script type="text/javascript">
angular.element(document.getElementsByTagName('head')).append(angular.element('<base href="' + window.location.pathname + '" />'));
</script>
</head>
<body ng-app="ngAnimateSwapExample" ng-controller="AppCtrl">
<div class="container">
<img ng-animate-swap="activeImage" class="cell swap-animation" ng-src="{{activeImage}}" alt="My Active Image" />
</div>
<div>
Current Image: {{activeImage}}
<br />
<button ng-click="previous()">Previous</button>
<button ng-click="next()">Next</button>
</div>
</body>
</html>
JS (script.js):
(function(angular) {
'use strict';
angular.module('ngAnimateSwapExample', ['ngAnimate'])
.controller('AppCtrl', ['$scope', '$timeout', function($scope, $timeout) {
var baseUrl = "http://lorempixel.com/400/200/sports";
$scope.images = [];
$scope.startIndex = 0;
for (var i = 0; i < 5; i++) {
$scope.images.push(baseUrl + "/" + i);
}
$scope.activeImage = $scope.images[$scope.startIndex];
/*
$interval(function() {
$scope.startIndex++;
if($scope.images[$scope.startIndex] && $scope.images[$scope.startIndex] != undefined){
$scope.activeImage = $scope.images[$scope.startIndex];
}
}, 2000);
*/
$scope.previous = function() {
$scope.startIndex--;
$timeout(function() {
if ($scope.images[$scope.startIndex] && $scope.images[$scope.startIndex] !== undefined) {
$scope.activeImage = $scope.images[$scope.startIndex];
}
}, 500);
};
$scope.next = function() {
$scope.startIndex++;
$timeout(function() {
if ($scope.images[$scope.startIndex] && $scope.images[$scope.startIndex] !== undefined) {
$scope.activeImage = $scope.images[$scope.startIndex];
}
}, 500);
};
}]);
})(window.angular);
Working plunker here.
My solution to this problem is to watch for changes on ng-src and using a timeout function to add a class which does the fadeIn effect.
HTML
<img ng-src="your-logic-will-go-here" class="animate-show ng-hide-add" fade-in>
Angular Code
.directive('fadeIn', function($timeout){
return {
restrict: 'A',
link: function($scope, $element, attrs){
$scope.$watch('selectedFormat.name', function(newValue, oldValue) {
if(newValue!=oldValue) {
$element.removeClass("ng-hide-add");
$element.addClass("ng-hide-remove");
$timeout(function () {
$element.addClass("ng-hide-add");
}, 100);
}
})
}
};
})
CSS
.animate-show.ng-hide-add, .animate-show.ng-hide-remove {
display: inline-block !important;
}
.animate-show.ng-hide-add.ng-hide-add-active, .animate-show.ng-hide-remove {
opacity: 0;
}
.animate-show.ng-hide-add{
transition: all linear 0.7s;
}
.animate-show.ng-hide-add, .animate-show.ng-hide-remove.ng-hide-remove-active {
opacity: 1;
}
You can't animate an img src change. You can, however, have multiple images and animate their opacity.
HTML/angular template
<div class="image-container">
<img src="image-one.jpg" ng-show="showImageOne">
<img src="image-two.jpg" ng-show="showImageTwo">
</div>
CSS
.image-container {
position: relative;
}
.image-container img {
position: absolute;
transition: 1s opacity linear;
}
.image-container img.ng-hide {
display: block!important;
opacity: 0;
}

Resources