Consider you have a simple, standard $http
request to a REST api:
...
function makeCall() {
var restURL = "http://my-rest-api/endpoint";
return $http.get(restURL);
}
...
If the execution time of this call varies for whatever reason (sometimes returns immediately, other times takes around a second to e), what is the best practice for normalizing the response time to the user?
In the past, I’ve done things like this:
...
function makeCall() {
var restURL = "http://my-rest-api/endpoint";
return $http.get(restURL);
}
...
function myController($scope) {
var promise = makeCall(); //make rest call
$scope.waitMessage = true;
$timeout(function() {
//before resolving the promise, wait a certain number of ms, then
//resolve and display data to user
promise.then(function(response) {
$scope.output = response.data;
$scope.waitMessage = false;
});
}, 1000);
}
This is an attempt to make the request take a fixed amount of time, regardless of if it returned immediately or not. During the delay time, I display an interstitial of some kind saying “Loading, please wait…” or the like, controlled by a variable like $scope.waitMessage
.
Any best practices for this sort of use case?
4
I’m not sure if it’s a “best practice”, but my way is to create a directive that throttles variable changes, using the lodash/underscore throttle
function:
app.directive('throttle', function($window) {
var WAIT_TIME = 750;
return {
scope: true,
link: function(scope, element, attrs) {
var name = attrs.throttle;
function setValue(value) {
// Shadow the value in the child scope
scope[name] = value;
// The leading edge of the _.throttle callback
// is called within a digest, but later ones are not
scope.$$phase || scope.$apply();
}
scope.$parent.$watch(name, $window._.throttle(setValue, WAIT_TIME));
}
};
});
That can be used for a simple text state variable to determine what to show to the user:
<div throttle="state">
<div ng-if="state == 'loading'">Loading...</div>
<div ng-if="state == 'loaded'">[Loaded template]</div>
</div>
So referring to the code in your question, in the controller you could have something like:
$scope.state = 'loading';
makeCall().then(function(response) {
$scope.output = response.data;
$scope.state = 'loaded';
});
The benefits of doing this rather than via $timeout
s in your controller or services:
-
Better separation of responsibilities. The controller/services don’t need to know about exactly how/when the data will be shown to the user. This is just in the template/directive.
-
Throttling means you’ve set a minimum time any given state will be shown to the user. If the user has been in that state already for at least that time, then there will be no extra time waiting.
I originally posted this on my blog at http://charemza.name/blog/posts/angularjs/templates/angularjs-throttled-variable-changes/