Reputation: 2437
The following code reads via a service and shows on the web page a list of 'page' objects for a specific 'page category' (string). Using the resolve object property in $routeProvider.when(), I am able to postpone updating the view until the new value is ready.
Two questions:
When asking for a new page list, I want to show a loading-icon. How can I detect (in a non-hackish way) the event when the reading from server starts (and the loading-icon should be displayed)? I guess I could do something like $('.pages-loading-icon').show() in the service, but find that to be too gui dependent too placed in the service.
When the new value is ready, I would like the old to fade out and the new to fade in. What is the 'angular' way to do this? I have tried to do it in the controller using $watch, but that causes the new value to be display shortly before the fadeout starts.
The code:
app.js:
$routeProvider.when('/:cat', { templateUrl: 'partials/view.html', controller: RouteCtrl, resolve: RouteCtrl.resolve});
controllers.js:
function RouteCtrl($scope, $routeParams, pages) {
$scope.params = $routeParams;
$scope.pages = pages;
}
RouteCtrl.resolve={
pages: function($routeParams, Pages){
if($routeParams.hasOwnProperty('cat')){
return Pages.query($routeParams.cat);
}
}
}
services.js: The last pages read is stored in currentPages and its category in lastCat.
factory('Pages', function($resource, $q, $timeout){
return {
res: $resource('jsonService/cat=:cat', {}, {
query: {method:'GET', params:{cat:'cat'}, isArray:true}
}),
lastCat: null,
currentPages: null,
query: function(cat) {
var res = this.res;
if(cat != this.lastCat){
var deferred = $q.defer();
var p = res.query({'cat':cat}, function(){
deferred.resolve(p);
});
this.lastCat = cat;
this.currentPages = deferred.promise;
}
return this.currentPages;
}
};
})
view.html
<ul >
<li ng-repeat="page in pages">
<a href="#{{params.cat}}/{{page.slug}}">{{page.title}}</a>
</li>
</ul>
Upvotes: 37
Views: 32245
Reputation: 4459
I like the 'ng-show'
... But the 'ui-if'
of the Angular UI project is better IMHO.. Since remove the HTML code from the DOM...
<span ui-if = "isViewLoading"> loading the view... </span>
_e
Upvotes: 1
Reputation: 36030
Not exactly sure whether this would work in your code as you have $resource integration.
But it may be worth to look into angular events: $routeChangeStart
and $routeChangeSuccess
.
in html:
<span ng-show="isViewLoading"> loading the view... </span>
in controller (which defines the scope of the html above):
$scope.isViewLoading = false;
$scope.$on('$routeChangeStart', function() {
$scope.isViewLoading = true;
});
$scope.$on('$routeChangeSuccess', function() {
$scope.isViewLoading = false;
});
$scope.$on('$routeChangeError', function() {
$scope.isViewLoading = false;
});
Upvotes: 61
Reputation: 9805
You will want to use the $rootScope
because monitoring a controller's $scope
is limited to the controller, and as it happens you're switching from one view (one controller) to another view (another controller). So it would make sense to have one-code-fit-all.
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams){ ... })
In that function set the $rootScope.isLoadingState
which will display your loading indicator.
Then use
$rootScope.$on('$stateChangeSuccess',
function(event, toState, toParams, fromState, fromParams){ ... })
To unset that variable.
Complete reference here
Upvotes: 2
Reputation: 319
You can create a directive that will show based on a broadcast that you send before and after a service call, which you could put on $rootScope. So if you're calling a factory to handle dishing out generic POST/GET calls then right before the call you can call a function on $rootScope, such as "$rootScope.startLoading()" and when it finishes call "$rootScope.doneLoading()", where each of these methods broadcast an event for you to pick up on your directive. Then you just handle on your directive to catch the events, "scope.$on("startLoading", func(..." and have it insert/show a loading div template into the DOM and run, then also have a catch for done loading to remove/hide it.
That way you can have a nice general loading overlay.
Upvotes: 0
Reputation: 73
You can also use the ng-show directive. Simply check whether you model is empty and display a DOM subset like so:
<!-- model not ready -->
<div style="width: 200" ng-show="lines == ''">
<div class="progress progress-striped active">
<div class="bar" style="width: 0%;"></div>
</div>
</div>
<!-- your model code -->
<ul class="nav nav-tabs nav-stacked">
<li ng-repeat="line in lines">
{{line.Id}}
</li>
</ul>
Upvotes: 1
Reputation: 10649
The routing system for angular seems to be a bit limited..
This solved it for me:
http://www.bennadel.com/blog/2420-Mapping-AngularJS-Routes-Onto-URL-Parameters-And-Client-Side-Events.htm
Also, about the events triggering twice: if you setup a route do map to "a/b/c" angular automatically sets up another route, "a/b/c/" (with trailing slash) to redirect to the first (and vice versa), and both trigger events. I just test for a presence of route parameters.
Regarding the limited condition of the routing sistem: there is this pull from angular, to extend it's capabilities.
Upvotes: 4