我想知道是否有一种方法(类似于Gmail)让AngularJS延迟显示一个新的路由,直到每个模型和它的数据已经使用各自的服务获取。
例如,如果有一个ProjectsController列出了所有的项目,而project_index.html是显示这些项目的模板,那么Project.query()将在显示新页面之前被完全获取。
在此之前,旧页面仍将继续显示(例如,如果我正在浏览另一个页面,然后决定查看这个Project索引)。
我想知道是否有一种方法(类似于Gmail)让AngularJS延迟显示一个新的路由,直到每个模型和它的数据已经使用各自的服务获取。
例如,如果有一个ProjectsController列出了所有的项目,而project_index.html是显示这些项目的模板,那么Project.query()将在显示新页面之前被完全获取。
在此之前,旧页面仍将继续显示(例如,如果我正在浏览另一个页面,然后决定查看这个Project索引)。
当前回答
延迟显示路由肯定会导致异步纠缠……为什么不简单地跟踪主实体的加载状态并在视图中使用它呢?例如,在你的控制器中,你可能同时使用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>
其他回答
$routeProvider resolve属性允许延迟路由更改,直到数据被加载。
首先像这样定义一个具有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'});
}]);
注意,resolve属性是在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;
}
}
注意,控制器定义包含一个resolve对象,它声明了控制器构造函数应该可用的东西。在这里,手机被注入到控制器中,并在resolve属性中定义。
的决心。Phones函数负责返回一个promise。收集所有的承诺,并延迟路由更改,直到所有的承诺都被解决。
工作演示:http://mhevery.github.com/angular-phonecat/app/#/phones 来源:https://github.com/mhevery/angular-phonecat/commit/ba33d3ec2d01b70eb5d3d531619bf90153496831
一个可能的解决方案可能是在我们使用模型的元素中使用ng-cloak指令。
<div ng-cloak="">
Value in myModel is: {{myModel}}
</div>
我觉得这个最省力。
使用AngularJS 1.1.5
使用AngularJS 1.1.5语法更新justin回答中的“phones”函数。
原:
phones: function($q, Phone) {
var deferred = $q.defer();
Phone.query(function(phones) {
deferred.resolve(phones);
});
return deferred.promise;
}
更新:
phones: function(Phone) {
return Phone.query().$promise;
}
多亏了Angular团队和贡献者,这个版本变得更短了。:)
这也是马克西米利安·霍夫曼的答案。显然,这个commit被放到了1.1.5。
这个代码片段是依赖注入友好的(我甚至将它与ngmin和uglify结合使用),它是一个更优雅的基于领域驱动的解决方案。
下面的例子注册了一个Phone资源和一个常量phoneRoutes,其中包含了该(Phone)域的所有路由信息。在提供的答案中,我不喜欢解析逻辑的位置——主模块不应该知道任何事情,也不应该被提供给控制器的资源参数的方式所困扰。这样逻辑就保持在同一个域中。
注意:如果你正在使用ngmin(如果你没有:你应该),你只需要按照DI数组约定编写解析函数。
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;
}]
}
}
});
下一部分是在模块处于configure状态时注入路由数据,并将其应用到$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: '/' });
});
用这个设置测试路由配置也很简单:
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);
});
});
});
});
你可以在我最新的(即将到来的)实验中看到它的全部荣耀。 虽然这个方法对我来说工作得很好,但我真的想知道为什么$injector在检测到任何是promise对象的注入时不延迟任何东西的构造;这会让事情变得非常非常非常简单。
编辑:使用Angular v1.2(rc2)
这个提交是版本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;
}]);