Reputation: 5594
How do I have AngularJS show a loading spinner until the data has finished loading?
If my controller has $scope.items = [{name: "One"}]
set up statically, and an AJAX loader which populates $scope.items[0]['lateLoader'] = "Hello"
, I'd like the spinner to show until the AJAX load is complete, and then populate the bound span with the retrieved data.
<ul ng-repeat="item in items">
<li>
<p>Always present: {{item.name}}</p>
<p>Loads later: <span ng-bind="item.lateLoader"><i class="icon icon-refresh icon-spin"></i></span></p>
</li>
</ul>
This code populates the bound span immediately, and as item.lateLoader
is empty the spinner is replaced with nothing.
How should I do this cleanly?
Upvotes: 22
Views: 88656
Reputation: 823
@lucuma,
Great answer, an alternative can be to inject $timeout and replace the native timeout function:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $timeout) {
$scope.name = 'World';
$scope.items = [{name: "One"}];
$timeout(function() {
$scope.$apply(function() {
$scope.items[0].lateLoader = 'i just loaded';
});
}, 1000);
});
Upvotes: 0
Reputation: 5027
I actually have been using a solution for this for a while now that works great and is better than using a timeout in my opinion. I am using $resource, but you could do the same thing with $http. On my $resource objects, I add the bit in the following code that sets loaded to true.
$scope.customer = $resource(dataUrl + "/Course/Social/" + courseName)
.get({}, function (data) { data.loaded = true; });
Then, in my view I can use:
ng-hide="customer.loaded"
Of course, I wouldn't use a $resource directly in a controller, I extract that to a customerRepository service, but it was more simple to show it this way here.
Upvotes: 22
Reputation: 18339
I would create a custom directive as per the other answer, but here is how you could do it without the directive which might be a good idea to learn before getting into more complex functionality.. A couple things to note:
Demo: http://plnkr.co/edit/4XZJqnIpie0ucMNN6egy?p=preview
View:
<ul >
<li ng-repeat="item in items">
<p>Always present: {{item.name}}</p>
<p>Loads later: {{item.lateLoader}} <i ng-hide="item.lateLoader" class="icon icon-refresh icon-spin">loading image i don't have</i></p>
</li>
</ul>
Controller:
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.items = [{name: "One"}];
setTimeout(function() {
$scope.$apply(function() {
$scope.items[0].lateLoader = 'i just loaded';
});
}, 1000);
});
Upvotes: 23
Reputation: 17589
I would create custom directive and put default markup with spinner.
Here are some links on custom directives
1) Egghead videos are awesome! http://www.egghead.io/video/xoIHkM4KpHM
2) Official Angular docs on directives http://docs.angularjs.org/guide/directive
3) Good overview of angular in 60ish minutes http://weblogs.asp.net/dwahlin/archive/2013/04/12/video-tutorial-angularjs-fundamentals-in-60-ish-minutes.aspx
Upvotes: 5