resolve
property of $routeProvider
allows to execute some jobs BEFORE corresponding view is rendered.
What if I want to display a spinner
I'm two years late to this, and yes these other solutions work but I find it easier to just handle all this in a just a run block like so
.run(['$rootScope','$ionicLoading', function ($rootScope,$ionicLoading){
$rootScope.$on('loading:show', function () {
$ionicLoading.show({
template:'Please wait..'
})
});
$rootScope.$on('loading:hide', function () {
$ionicLoading.hide();
});
$rootScope.$on('$stateChangeStart', function () {
console.log('please wait...');
$rootScope.$broadcast('loading:show');
});
$rootScope.$on('$stateChangeSuccess', function () {
console.log('done');
$rootScope.$broadcast('loading:hide');
});
}])
You don't need anything else. Pretty easy huh. Here's an example of it in action.
You can use a directive that listens on $routeChangeStart
and for example shows the element when it fires:
app.directive('showDuringResolve', function($rootScope) {
return {
link: function(scope, element) {
element.addClass('ng-hide');
var unregister = $rootScope.$on('$routeChangeStart', function() {
element.removeClass('ng-hide');
});
scope.$on('$destroy', unregister);
}
};
});
Then you place it on the specific view's loader, for example:
View 1:
<div show-during-resolve class="alert alert-info">
<strong>Loading.</strong>
Please hold.
</div>
View 2:
<span show-during-resolve class="glyphicon glyphicon-refresh"></span>
The problem with this solution (and many other solutions for that matter) is that if you browse to one of the routes from an external site there will be no previous ng-view template loaded, so your page might just be blank during resolve.
This can be solved by creating a directive that will act as a fallback-loader. It will listen for $routeChangeStart
and show a loader only if there is no previous route.
A basic example:
app.directive('resolveLoader', function($rootScope, $timeout) {
return {
restrict: 'E',
replace: true,
template: '<div class="alert alert-success ng-hide"><strong>Welcome!</strong> Content is loading, please hold.</div>',
link: function(scope, element) {
$rootScope.$on('$routeChangeStart', function(event, currentRoute, previousRoute) {
if (previousRoute) return;
$timeout(function() {
element.removeClass('ng-hide');
});
});
$rootScope.$on('$routeChangeSuccess', function() {
element.addClass('ng-hide');
});
}
};
});
The fallback loader would be placed outside the element with ng-view:
<body>
<resolve-loader></resolve-loader>
<div ng-view class="fadein"></div>
</body>
Demo of it all: http://plnkr.co/edit/7clxvUtuDBKfNmUJdbL3?p=preview
In ui-router 1.0 $stateChange* events are deprecated. Use transition hook instead. See migration guide below for more details.
https://ui-router.github.io/guide/ng1/migrate-to-1_0#state-change-events
The problem with '$stateChangeStart' and '$stateChangeSuccess' is "$rootScope.stateIsLoading" doesn't get refreshed when you go back to last state. Is there any solution on that? I also used:
$rootScope.$on('$viewContentLoading',
function(event){....});
and
$rootScope.$on('$viewContentLoaded',
function(event){....});
but there is the same issue.
To further Pranay's answer this is how I did it.
JS:
app.run(['$rootScope',function($rootScope){
$rootScope.stateIsLoading = false;
$rootScope.$on('$routeChangeStart', function() {
$rootScope.stateIsLoading = true;
});
$rootScope.$on('$routeChangeSuccess', function() {
$rootScope.stateIsLoading = false;
});
$rootScope.$on('$routeChangeError', function() {
//catch error
});
}]);
HTML
<section ng-if="!stateIsLoading" ng-view></section>
<section ng-if="stateIsLoading">Loading...</section>
i think this is pretty neat
app.run(['$rootScope', '$state',function($rootScope, $state){
$rootScope.$on('$stateChangeStart',function(){
$rootScope.stateIsLoading = true;
});
$rootScope.$on('$stateChangeSuccess',function(){
$rootScope.stateIsLoading = false;
});
}]);
and then on view
<div ng-show='stateIsLoading'>
<strong>Loading.</strong>
</div>