Delaying AngularJS route change until model loaded to prevent flicker

后端 未结 13 2312
误落风尘
误落风尘 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:05

    I have had a complex multi-level sliding panel interface, with disabled screen layer. Creating directive on disable screen layer that would create click event to execute the state like

    $state.go('account.stream.social.view');
    

    were producing a flicking effect. history.back() instead of it worked ok, however its not always back in history in my case. SO what I find out is that if I simply create attribute href on my disable screen instead of state.go , worked like a charm.

    <a class="disable-screen" back></a>
    

    Directive 'back'

    app.directive('back', [ '$rootScope', function($rootScope) {
    
        return {
            restrict : 'A',
            link : function(scope, element, attrs) {
                element.attr('href', $rootScope.previousState.replace(/\./gi, '/'));
            }
        };
    
    } ]);
    

    app.js I just save previous state

    app.run(function($rootScope, $state) {      
    
        $rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams) {         
    
            $rootScope.previousState = fromState.name;
            $rootScope.currentState = toState.name;
    
    
        });
    });
    
    0 讨论(0)
  • 2020-11-22 03:13

    Delaying showing the route is sure to lead to an asynchronous tangle... why not simply track the loading status of your main entity and use that in the view. For example in your controller you might use both the success and error callbacks on ngResource:

    $scope.httpStatus = 0; // in progress
    $scope.projects = $resource.query('/projects', function() {
        $scope.httpStatus = 200;
      }, function(response) {
        $scope.httpStatus = response.status;
      });
    

    Then in the view you could do whatever:

    <div ng-show="httpStatus == 0">
        Loading
    </div>
    <div ng-show="httpStatus == 200">
        Real stuff
        <div ng-repeat="project in projects">
             ...
        </div>
    </div>
    <div ng-show="httpStatus >= 400">
        Error, not found, etc. Could distinguish 4xx not found from 
        5xx server error even.
    </div>
    
    0 讨论(0)
  • 2020-11-22 03:13

    I liked above answers and learned a lot from them but there is something that is missing in most of the above answers.

    I was stuck in a similar scenario where I was resolving url with some data that is fetched in the first request from the server. Problem I faced was what if the promise is rejected.

    I was using a custom provider which used to return a Promise which was resolved by the resolve of $routeProvider at the time of config phase.

    What I want to stress here is the concept of when it does something like this.

    It sees the url in url bar and then respective when block in called controller and view is referred so far so good.

    Lets say I have following config phase code.

    App.when('/', {
       templateUrl: '/assets/campaigns/index.html',
       controller: 'CampaignListCtr',
       resolve : {
          Auth : function(){
             return AuthServiceProvider.auth('campaign');
          }
       }
    })
    // Default route
    .otherwise({
       redirectTo: '/segments'
    });
    

    On root url in browser first block of run get called otherwise otherwise gets called.

    Let's imagine a scenario I hit rootUrl in address bar AuthServicePrivider.auth() function gets called.

    Lets say Promise returned is in reject state what then???

    Nothing gets rendered at all.

    Otherwise block will not get executed as it is for any url which is not defined in the config block and is unknown to angularJs config phase.

    We will have to handle the event that gets fired when this promise is not resolved. On failure $routeChangeErorr gets fired on $rootScope.

    It can be captured as shown in code below.

    $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
        // Use params in redirection logic.
        // event is the routeChangeEvent
        // current is the current url
        // previous is the previous url
        $location.path($rootScope.rootPath);
    });
    

    IMO It's generally a good idea to put event tracking code in run block of application. This code run just after the config phase of the application.

    App.run(['$routeParams', '$rootScope', '$location', function($routeParams, $rootScope, $location){
       $rootScope.rootPath = "my custom path";
       // Event to listen to all the routeChangeErrors raised
       // by the resolve in config part of application
       $rootScope.$on('$routeChangeError', function(event, current, previous, rejection){
           // I am redirecting to rootPath I have set above.
           $location.path($rootScope.rootPath);
       });
    }]);
    

    This way we can handle promise failure at the time of config phase.

    0 讨论(0)
  • 2020-11-22 03:15

    Using AngularJS 1.1.5

    Updating the 'phones' function in Justen's answer using AngularJS 1.1.5 syntax.

    Original:

    phones: function($q, Phone) {
        var deferred = $q.defer();
    
        Phone.query(function(phones) {
            deferred.resolve(phones);
        });
    
        return deferred.promise;
    }
    

    Updated:

    phones: function(Phone) {
        return Phone.query().$promise;
    }
    

    Much shorter thanks to the Angular team and contributors. :)

    This is also the answer of Maximilian Hoffmann. Apparently that commit made it into 1.1.5.

    0 讨论(0)
  • 2020-11-22 03:15

    One possible solution might be to use the ng-cloak directive with the element where we are using the models e.g.

    <div ng-cloak="">
      Value in  myModel is: {{myModel}}
    </div>
    

    I think this one takes least effort.

    0 讨论(0)
  • 2020-11-22 03:16

    This commit, which is part of version 1.1.5 and above, exposes the $promise object of $resource. Versions of ngResource including this commit allow resolving resources like this:

    $routeProvider

    resolve: {
        data: function(Resource) {
            return Resource.get().$promise;
        }
    }
    

    controller

    app.controller('ResourceCtrl', ['$scope', 'data', function($scope, data) {
    
        $scope.data = data;
    
    }]);
    
    0 讨论(0)
提交回复
热议问题