Delaying AngularJS route change until model loaded to prevent flicker

angularjs, angularjs-routing, javascript

Solution

$routeProvider resolve property allows delaying of route change until data is loaded.

First define a route with `resolve` attribute like this.

angular.module('phonecat', ['phonecatFilters', 'phonecatServices', 'phonecatDirectives']).
  config(['$routeProvider', function($routeProvider) {
    $routeProvider.
      when('/phones', {
        templateUrl: 'partials/phone-list.html', 
        controller: PhoneListCtrl, 
        resolve: PhoneListCtrl.resolve}).
      when('/phones/:phoneId', {
        templateUrl: 'partials/phone-detail.html', 
        controller: PhoneDetailCtrl, 
        resolve: PhoneDetailCtrl.resolve}).
      otherwise({redirectTo: '/phones'});
}]);

notice that the `resolve` property is defined on route.

function PhoneListCtrl($scope, phones) {
  $scope.phones = phones;
  $scope.orderProp = 'age';
}

PhoneListCtrl.resolve = {
  phones: function(Phone, $q) {
    // see: https://groups.google.com/forum/?fromgroups=#!topic/angular/DGf7yyD4Oc4
    var deferred = $q.defer();
    Phone.query(function(successData) {
            deferred.resolve(successData); 
    }, function(errorData) {
            deferred.reject(); // you could optionally pass error data here
    });
    return deferred.promise;
  },
  delay: function($q, $defer) {
    var delay = $q.defer();
    $defer(delay.resolve, 1000);
    return delay.promise;
  }
}

Notice that the controller definition contains a resolve object which declares things which should be available to the controller constructor. Here the `phones` is injected into the controller and it is defined in the `resolve` property.

The `resolve.phones` function is responsible for returning a promise. All of the promises are collected and the route change is delayed until after all of the promises are resolved.

Working demo: http://mhevery.github.com/angular-phonecat/app/#/phones Source: https://github.com/mhevery/angular-phonecat/commit/ba33d3ec2d01b70eb5d3d531619bf90153496831

Problem

I am wondering if there is a way (similar to Gmail) for AngularJS to delay showing a new route until after each model and its data has been fetched using its respective services. For example, if there were a `ProjectsController` that listed all Projects and `project_index.html` which was the template that showed these Projects, `Project.query()` would be fetched completely before showing the new page. Until then, the old page would still continue to show (for example, if I were browsing another page and then decided to see this Project index).

Original source

Related problems