延迟AngularJS路由更改,直到加载模型以防止闪烁

本文翻译自:Delaying AngularJS route change until model loaded to prevent flicker

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. 我想知道AngularJS是否有一种方法(类似于Gmail) 延迟显示新路由,直到每个模型及其数据已使用其各自的服务获取

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. 例如,如果有一个ProjectsController列出了所有Projects和project_index.html ,它们是显示这些Projects的模板, Project.query()将在显示新页面之前完全获取。

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). 在此之前,旧页面仍将继续显示(例如,如果我正在浏览另一个页面,然后决定查看此项目索引)。


#1楼

参考:https://stackoom.com/question/oETC/延迟AngularJS路由更改-直到加载模型以防止闪烁


#2楼

$routeProvider resolve property allows delaying of route change until data is loaded. $ routeProvider resolve属性允许延迟路由更改,直到加载数据。

First define a route with resolve attribute like this. 首先使用这样的resolve属性定义路由。

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. 请注意, resolve属性是在路由上定义的。

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. 这里将phones注入控制器,并在resolve属性中定义。

The resolve.phones function is responsible for returning a promise. resolve.phones函数负责返回一个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 工作演示: http//mhevery.github.com/angular-phonecat/app/#/phones资料来源: https//github.com/mhevery/angular-phonecat/commit/ba33d3ec2d01b70eb5d3d531619bf90153496831


#3楼

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: 例如,在您的控制器中,您可以在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>

#4楼

I worked from Misko's code above and this is what I've done with it. 我从上面的Misko代码开始工作,这就是我用它做的。 This is a more current solution since $defer has been changed to $timeout . 这是一个更新的解决方案,因为$defer已更改为$timeout Substituting $timeout however will wait for the timeout period (in Misko's code, 1 second), then return the data hoping it's resolved in time. 替换$timeout会等待超时时间(在Misko的代码中,1秒),然后返回希望及时解决的数据。 With this way, it returns asap. 通过这种方式,它尽快返回。

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

PhoneListCtrl.resolve = {

  phones: function($q, Phone) {
    var deferred = $q.defer();

    Phone.query(function(phones) {
        deferred.resolve(phones);
    });

    return deferred.promise;
  }
}

#5楼

Here's a minimal working example which works for Angular 1.0.2 这是一个适用于Angular 1.0.2的最小工作示例

Template: 模板:

<script type="text/ng-template" id="/editor-tpl.html">
    Editor Template {{datasets}}
</script>

<div ng-view>

</div>

JavaScript: JavaScript的:

function MyCtrl($scope, datasets) {    
    $scope.datasets = datasets;
}

MyCtrl.resolve = {
    datasets : function($q, $http) {
        var deferred = $q.defer();

        $http({method: 'GET', url: '/someUrl'})
            .success(function(data) {
                deferred.resolve(data)
            })
            .error(function(data){
                //actually you'd want deffered.reject(data) here
                //but to show what would happen on success..
                deferred.resolve("error value");
            });

        return deferred.promise;
    }
};

var myApp = angular.module('myApp', [], function($routeProvider) {
    $routeProvider.when('/', {
        templateUrl: '/editor-tpl.html',
        controller: MyCtrl,
        resolve: MyCtrl.resolve
    });
});​
​

http://jsfiddle.net/dTJ9N/3/ http://jsfiddle.net/dTJ9N/3/

Streamlined version: 精简版:

Since $http() already returns a promise (aka deferred), we actually don't need to create our own. 由于$ http()已经返回一个promise(也称为deferred),我们实际上不需要创建自己的。 So we can simplify MyCtrl. 所以我们可以简化MyCtrl。 resolve to: 决心:

MyCtrl.resolve = {
    datasets : function($http) {
        return $http({
            method: 'GET', 
            url: 'http://fiddle.jshell.net/'
        });
    }
};

The result of $http() contains data , status , headers and config objects, so we need to change the body of MyCtrl to: $ http()的结果包含数据状态标题配置对象,因此我们需要将MyCtrl的主体更改为:

$scope.datasets = datasets.data;

http://jsfiddle.net/dTJ9N/5/ http://jsfiddle.net/dTJ9N/5/


#6楼

I like darkporter's idea because it will be easy for a dev team new to AngularJS to understand and worked straight away. 我喜欢darkporter的想法,因为AngularJS的新手开发团队很容易理解并立即开展工作。

I created this adaptation which uses 2 divs, one for loader bar and another for actual content displayed after data is loaded. 我创建了这个改编版,它使用2个div,一个用于加载程序栏,另一个用于加载数据后显示的实际内容。 Error handling would be done elsewhere. 错误处理将在其他地方完成。

Add a 'ready' flag to $scope: 在$ scope中添加'ready'标志:

$http({method: 'GET', url: '...'}).
    success(function(data, status, headers, config) {
        $scope.dataForView = data;      
        $scope.ready = true;  // <-- set true after loaded
    })
});

In html view: 在html视图中:

<div ng-show="!ready">

    <!-- Show loading graphic, e.g. Twitter Boostrap progress bar -->
    <div class="progress progress-striped active">
        <div class="bar" style="width: 100%;"></div>
    </div>

</div>

<div ng-show="ready">

    <!-- Real content goes here and will appear after loading -->

</div>

See also: Boostrap progress bar docs 另请参阅: Boostrap进度条文档

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值