我想知道是否有一种方法(类似于Gmail)让AngularJS延迟显示一个新的路由,直到每个模型和它的数据已经使用各自的服务获取。
例如,如果有一个ProjectsController列出了所有的项目,而project_index.html是显示这些项目的模板,那么Project.query()将在显示新页面之前被完全获取。
在此之前,旧页面仍将继续显示(例如,如果我正在浏览另一个页面,然后决定查看这个Project索引)。
我想知道是否有一种方法(类似于Gmail)让AngularJS延迟显示一个新的路由,直到每个模型和它的数据已经使用各自的服务获取。
例如,如果有一个ProjectsController列出了所有的项目,而project_index.html是显示这些项目的模板,那么Project.query()将在显示新页面之前被完全获取。
在此之前,旧页面仍将继续显示(例如,如果我正在浏览另一个页面,然后决定查看这个Project索引)。
当前回答
一个可能的解决方案可能是在我们使用模型的元素中使用ng-cloak指令。
<div ng-cloak="">
Value in myModel is: {{myModel}}
</div>
我觉得这个最省力。
其他回答
这个提交是版本1.1.5及更高版本的一部分,它公开了$resource的$promise对象。包含此提交的ngResource版本允许像这样解析资源:
routeProvider美元
resolve: {
data: function(Resource) {
return Resource.get().$promise;
}
}
控制器
app.controller('ResourceCtrl', ['$scope', 'data', function($scope, data) {
$scope.data = data;
}]);
您可以使用$routeProvider解析属性来延迟路由更改,直到数据被加载。
angular.module('app', ['ngRoute']).
config(['$routeProvider', function($routeProvider, EntitiesCtrlResolve, EntityCtrlResolve) {
$routeProvider.
when('/entities', {
templateUrl: 'entities.html',
controller: 'EntitiesCtrl',
resolve: EntitiesCtrlResolve
}).
when('/entity/:entityId', {
templateUrl: 'entity.html',
controller: 'EntityCtrl',
resolve: EntityCtrlResolve
}).
otherwise({redirectTo: '/entities'});
}]);
注意,resolve属性是在route上定义的。
EntitiesCtrlResolve和EntityCtrlResolve是与EntitiesCtrl和EntityCtrl控制器定义在同一文件中的常量对象。
// EntitiesCtrl.js
angular.module('app').constant('EntitiesCtrlResolve', {
Entities: function(EntitiesService) {
return EntitiesService.getAll();
}
});
angular.module('app').controller('EntitiesCtrl', function(Entities) {
$scope.entities = Entities;
// some code..
});
// EntityCtrl.js
angular.module('app').constant('EntityCtrlResolve', {
Entity: function($route, EntitiesService) {
return EntitiesService.getById($route.current.params.projectId);
}
});
angular.module('app').controller('EntityCtrl', function(Entity) {
$scope.entity = Entity;
// some code..
});
我喜欢上面的答案,并从中学到很多东西,但上面的大多数答案都缺少一些东西。
我遇到过类似的情况,我在解析url时使用了从服务器的第一个请求中获取的一些数据。我面临的问题是,如果承诺被拒绝了怎么办。
我正在使用一个自定义提供程序,它曾经返回一个承诺,在配置阶段由$routeProvider的解析解决。
我在这里想强调的是当它这样做时的概念。
它看到url在url栏,然后分别当块在调用控制器和视图被引用到目前为止,很好。
假设我有以下配置阶段的代码。
App.when('/', {
templateUrl: '/assets/campaigns/index.html',
controller: 'CampaignListCtr',
resolve : {
Auth : function(){
return AuthServiceProvider.auth('campaign');
}
}
})
// Default route
.otherwise({
redirectTo: '/segments'
});
在根url在浏览器的第一个块运行得到调用,否则否则被调用。
让我们想象一个场景,我在地址栏中点击rootUrl authserviceprivid .auth()函数被调用。
假设返回的Promise处于拒绝状态,然后呢?
什么都没有渲染。
否则,block将不会被执行,因为它对于任何没有在配置块中定义的url和angularJs的配置阶段未知的url都是如此。
当这个承诺没有得到解决时,我们将不得不处理被解雇的事件。失败时$ routechangeerror在$rootScope上被触发。
它可以被捕获,如下面的代码所示。
$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);
});
在应用程序的运行块中放置事件跟踪代码通常是一个好主意。这段代码只在应用程序的配置阶段之后运行。
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);
});
}]);
这样我们就可以在配置阶段处理承诺失败。
延迟显示路由肯定会导致异步纠缠……为什么不简单地跟踪主实体的加载状态并在视图中使用它呢?例如,在你的控制器中,你可能同时使用ngResource上的success和error回调:
$scope.httpStatus = 0; // in progress
$scope.projects = $resource.query('/projects', function() {
$scope.httpStatus = 200;
}, function(response) {
$scope.httpStatus = response.status;
});
然后在视图中你可以做任何事情:
<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>
我喜欢darkporter的想法,因为对于刚接触AngularJS的开发团队来说,理解和工作起来很容易。
我创建了这个改编,它使用了2个div,一个用于加载器栏,另一个用于数据加载后显示的实际内容。错误处理将在其他地方完成。
在$scope中添加一个ready标志:
$http({method: 'GET', url: '...'}).
success(function(data, status, headers, config) {
$scope.dataForView = data;
$scope.ready = true; // <-- set true after loaded
})
});
在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>
参见:Boostrap进度条文档