Delaying AngularJS route change until model loaded to prevent flicker

后端 未结 13 2523
误落风尘
误落风尘 2020-11-22 02:28

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 it

13条回答
  •  执念已碎
    2020-11-22 03:26

    This snippet is dependency injection friendly (I even use it in combination of ngmin and uglify) and it's a more elegant domain driven based solution.

    The example below registers a Phone resource and a constant phoneRoutes, which contains all your routing information for that (phone) domain. Something I didn't like in the provided answer was the location of the resolve logic -- the main module should not know anything or be bothered about the way the resource arguments are provided to the controller. This way the logic stays in the same domain.

    Note: if you're using ngmin (and if you're not: you should) you only have to write the resolve functions with the DI array convention.

    angular.module('myApp').factory('Phone',function ($resource) {
      return $resource('/api/phone/:id', {id: '@id'});
    }).constant('phoneRoutes', {
        '/phone': {
          templateUrl: 'app/phone/index.tmpl.html',
          controller: 'PhoneIndexController'
        },
        '/phone/create': {
          templateUrl: 'app/phone/edit.tmpl.html',
          controller: 'PhoneEditController',
          resolve: {
            phone: ['$route', 'Phone', function ($route, Phone) {
              return new Phone();
            }]
          }
        },
        '/phone/edit/:id': {
          templateUrl: 'app/phone/edit.tmpl.html',
          controller: 'PhoneEditController',
          resolve: {
            form: ['$route', 'Phone', function ($route, Phone) {
              return Phone.get({ id: $route.current.params.id }).$promise;
            }]
          }
        }
      });
    

    The next piece is injecting the routing data when the module is in the configure state and applying it to the $routeProvider.

    angular.module('myApp').config(function ($routeProvider, 
                                             phoneRoutes, 
                                             /* ... otherRoutes ... */) {
    
      $routeProvider.when('/', { templateUrl: 'app/main/index.tmpl.html' });
    
      // Loop through all paths provided by the injected route data.
    
      angular.forEach(phoneRoutes, function(routeData, path) {
        $routeProvider.when(path, routeData);
      });
    
      $routeProvider.otherwise({ redirectTo: '/' });
    
    });
    

    Testing the route configuration with this setup is also pretty easy:

    describe('phoneRoutes', function() {
    
      it('should match route configuration', function() {
    
        module('myApp');
    
        // Mock the Phone resource
        function PhoneMock() {}
        PhoneMock.get = function() { return {}; };
    
        module(function($provide) {
          $provide.value('Phone', FormMock);
        });
    
        inject(function($route, $location, $rootScope, phoneRoutes) {
          angular.forEach(phoneRoutes, function (routeData, path) {
    
            $location.path(path);
            $rootScope.$digest();
    
            expect($route.current.templateUrl).toBe(routeData.templateUrl);
            expect($route.current.controller).toBe(routeData.controller);
          });
        });
      });
    });
    

    You can see it in full glory in my latest (upcoming) experiment. Although this method works fine for me, I really wonder why the $injector isn't delaying construction of anything when it detects injection of anything that is a promise object; it would make things soooOOOOOooOOOOO much easier.

    Edit: used Angular v1.2(rc2)

提交回复
热议问题