Can ng-show directive be used with a delay
My suspicion is that you are looking for a general purpose spinner that includes a delay. The standard, show after 200ms
or something like that.
This is a perfect candidate for a directive, and actually pretty easy to accomplish.
I know this is a long code example, but the primary piece is the directive. It's pretty simple.
Listen to a few scope variables and shows after some configurable delay. If the operation takes longer than the delay, it will just get canceled and never show up.
(function() { 'use strict'; function SpinnerDirective($timeout) { return { restrict: 'E', template: '<i class="fa fa-cog fa-spin"></i>', scope: { show: '=', delay: '@' }, link: function(scope, elem, attrs) { var showTimer; //This is where all the magic happens! // Whenever the scope variable updates we simply // show if it evaluates to 'true' and hide if 'false' scope.$watch('show', function(newVal){ newVal ? showSpinner() : hideSpinner(); }); function showSpinner() { //If showing is already in progress just wait if (showTimer) return; //Set up a timeout based on our configured delay to show // the element (our spinner) showTimer = $timeout(showElement.bind(this, true), getDelay()); } function hideSpinner() { //This is important. If the timer is in progress // we need to cancel it to ensure everything stays // in sync. if (showTimer) { $timeout.cancel(showTimer); } showTimer = null; showElement(false); } function showElement(show) { show ? elem.css({display:''}) : elem.css({display:'none'}); } function getDelay() { var delay = parseInt(scope.delay); return angular.isNumber(delay) ? delay : 200; } } }; } function FakeService($timeout) { var svc = this, numCalls = 0; svc.fakeCall = function(delay) { numCalls += 1; return $timeout(function() { return { callNumber: numCalls }; }, delay || 50); }; } function MainCtrl(fakeService) { var vm = this; vm.makeCall = function(delay) { vm.isBusy = true; fakeService.fakeCall(delay) .then(function(result) { vm.result = result; }).finally(function() { vm.isBusy = false; }); } } angular.module('spinner', []) .service('fakeService', FakeService) .controller('mainCtrl', MainCtrl) .directive('spinner', SpinnerDirective);}());
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet" /><link href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet"><script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script><div class="container" ng-app="spinner"> <div class="row" ng-controller="mainCtrl as ctrl"> <div class="col-sm-12"> <h2>{{ctrl.result | json}} <spinner show="ctrl.isBusy" delay="200"></spinner> </h2> <button type="button" class="btn btn-primary" ng-click="ctrl.makeCall(2000)" ng-disabled="ctrl.isBusy">Slow Call </button> <button type="button" class="btn btn-default" ng-click="ctrl.makeCall()" ng-disabled="ctrl.isBusy">Fast Call </button> </div> </div></div>
Here's a simpler approach that worked for my needs. Depending on what your action is, you would tie function setDelay()
to the element. For example, in my case I tied setDelay()
to a select input.
Trigger HTML:
<select class="first-option" ng-change="setDelay()" ng-options="o.label for o in download.options" ng-model="optionModel" required></select>
In your controller, add a simple function setDelay
that will change the flag $scope.delay
:
$scope.setDelay = function(){ $scope.delay = true; $timeout(function(){ $scope.delay = false; }, 200);};
Then, you can simply use $scope.delay
as a flag in ng-show:
<div class="loading-div" ng-show="delay"> <img src="loading_spinner.gif"></div>
And show content after done loading:
<div ng-show="!delay"> Content is loaded.</div>
Now, every time the user selects a new value in the dropdown menu, it will trigger$scope.delay
to be set totrue
causing the spinner to show, and when it reaches 200
, it will be set to false
causing the spinner to hide.
I think a pure CSS solution is the best way to do it.
Here is a plunker showing how to do it. Using ng-animate classes for transition and applying a transition delay with a transition of 10ms (0s transition is not working with css).
Relevant part of the code :
.your-element-class.ng-hide { opacity: 0;}.your-element-class.ng-hide-add,.your-element-class.ng-hide-remove { transition: all linear 0.01s 1s;}
The only reason to use a custom directive for it would be using this tons of times in your code with different delays value. A custom directive allow more flexibility with the delay timing.