Reputation: 896
I've been doing some Googling around this already but I'm unable to find a solution that works.
I'm using AngularJS 1.5.5 and .NET Web API 2 to build a web application and I would quite simply like to hide the ng-view
element until all resolves have completed on the route.
I'm trying to use the $routeChangeStart
and $routeChangeSuccess
to set a variable on the $rootScope
that is used in the index html to display the loading indicator and hide the content until the variable is false
.
Here is my routing code for the routeChange properties:
_app.config([
'$routeProvider', '$httpProvider', '$provide',
function ($routeProvider, $httpProvider, $provide) {
$routeProvider.when('/Account',
{
templateUrl: '/Content/js/areas/account/account.html',
controller: 'accountController',
resolve: {
$accountResolver: function (accountService) {
return accountService.getMyAccountData();
}
},
caseInsensitiveMatch: true
});
$routeProvider.otherwise({ redirectTo: '404' });
}
]);
_app.run(['$rootScope', '$location', '$window', '$q', 'authService',
function ($rootScope, $location, $window, $q, authService) {
$rootScope.$on("$routeChangeStart",
function (e, curr, prev) {
$rootScope.$loadingRoute = true;
});
$rootScope.$on("$routeChangeSuccess",
function (evt, next) {
$rootScope.$loadingRoute = false;
});
$rootScope.$on("$routeChangeError",
function (evt, next) {
$rootScope.$loadingRoute = false;
});
}]);
And here is my html using that $loadingRoute
variable:
<body class="ng-cloak" data-ng-app="wishlist" data-ng-controller="appController">
<wl-header></wl-header>
<preloader ng-if="$loadingRoute"></preloader>
<section ng-view ng-if="!$loadingRoute" class="container ng-cloak"></section>
</body>
I understand that there's quite a lot of articles covering this but none seem to work in my case. $loadingRoute
gets set to true when the route change starts, as expected, which I will see if I add {{$loadingRoute}}
to the HTML before the <section></section>
tag. However before the $accountResolver
is resolved, the $routeChangeSuccess
gets fired, setting $rootScope.$loadingRoute = false
which is unexpected.
I was under the impression that $routeChangeSuccess
only got fired after all resolves had completed on the current route.
Am I doing something really obviously wrong here? Or has Angular simply changed?
Edit: I would also like to add that this approach worked in previous projects, so I'm at a real loss as to what's going wrong. I could set $rootScope.$loadingRoute
manually in each page controller but that feels too dirty and unmaintainable.
Edit 2:
_app.factory('accountService', [
'accountResource',
function (accountResource) {
var _self = this;
return {
register: function (authData) {
return accountResource.register(authData);
},
getMyAccountData: function () {
return accountResource.getMyAccountData();
}
}
}
]);
_app.factory('accountResource', [
'$resource', 'rootUrl',
function ($resource, rootUrl) {
var api = rootUrl() + 'api/Account';
return $resource(api,
{},
{
register: {
method: 'POST',
url: '{0}/register'.format(api)
},
getMyAccountData: {
method: 'GET',
url: '{0}/GetMyAccountData'.format(api)
}
});
}
])
Upvotes: 0
Views: 104
Reputation: 222750
In order for a resolver to delay route change, it should return a promise. Otherwise route change happens immediately, this is what happens when $routeChangeSuccess
is triggered before a promise from accountService.getMyAccountData()
is resolved.
The problem is $resource
methods (and so accountService.getMyAccountData()
) return self-filling object that is populated with data asynchronously. A promise for this data is available as $promise
property (see the reference), so it should be used for a resolver:
$accountResolver: function (accountService) {
return accountService.getMyAccountData().$promise;
}
If accountService
is supposed to be purely promise-based wrapper for accountResource
, a cleaner way to do this is to return a promise from its methods instead:
getMyAccountData: function () {
return accountResource.getMyAccountData().$promise;
}
Upvotes: 1