angular.module('myApp').factory('DataService',['$http',function($http) { function unwrapFriendList(data) { ... return unwrappedFriendList; } return { getFriendList: function() { return $http.get('/api/friends').then(unwrapFriendList); } } }]);
这是一个使用该数据的视图,在解析promise并将结果存储在$scope.friends中之后:
<div ng-repeat='friend in friends'> {{friend.firstName}} {{friend.lastName}} </div>
在将数据加载到控制器中时,我遇到了几种方法.
选项1:使用通过ng-route解析加载的数据的控制器
angular.module('myApp').controller('FriendListCtrl',['$scope','friendList',function($scope,friendList) { $scope.friends = friendList; }]);
路线部分:
angular.module('myApp',...).config(function($routeProvider) { $routeProvider .when('/friends',{ templateUrl: 'views/friends.html',controller: 'FriendListCtrl',resolve: { friendList: ['DataService',function(DataService) { return DataService.getFriendList(); }] } }) ... });
选项2:自动触发数据加载的控制器
angular.module('myApp').controller('FriendListCtrl','DataService',DataService) { DataService.getFriendList().then(function(friendList) { $scope.friends = friendList; }); }]);
问题
>还有其他常用的方法吗?如果是这样,请用代码示例说明.
>每种方法有哪些局限性?
>每种方法有哪些优点?
>在什么情况下我应该使用每种方法?
选项1:
使用resolve使控制器单元测试中的模拟依赖变得非常简单.在你的第一个选项中:
$routeProvider .when('/friends',{ templateUrl: 'views/friends.html',resolve: { friendList: ['DataService',function(DataService) { return DataService.getFriendList(); }] } }) angular.module('myApp') .controller('FriendListCtrl',friendList) { $scope.friends = friendList; }]);
由于friendList被注入到控制器中,因此在测试中模拟它就像将普通对象传递给$controller服务一样简单:
var friendListMock = [ // ... ]; $controller('FriendListCtrl',{ $scope: scope,friendList: friendListMock })
选项2:
您不能使用第二个选项执行此操作,并且必须监视/存根DataService.由于第二个选项中的数据数据请求会在控制器创建时立即调用,因此一旦您开始执行多个,有条件或依赖(稍后更多)数据请求,测试将变得非常混乱.
查看初始化
选项1:
在所有结算完成之前,解决方案会阻止视图初始化.这意味着视图中任何期望数据(包括指令)的内容都会立即生成.
选项2:
如果数据请求发生在控制器中,则视图将显示,但在请求完成之前将不会有任何数据(将来会在某个未知点).这类似于一些没有风格的内容,可能会很刺耳,但可以解决.
当您的视图中的组件期望数据并且未提供数据时,会出现真正的复杂情况,因为它们仍在被检索.然后,您必须通过强制每个组件等待或延迟初始化一段未知的时间来解决这个问题,或者让它们在初始化之前观察一些任意变量.很乱.
更喜欢做出决定
虽然您可以在控制器中进行初始数据加载,但已经可以更清晰,更具说明性的方式进行解析.
但是,默认的ngRoute解析器缺少一些关键功能,最值得注意的是依赖解析.如果您想向控制器提供2个数据,客户以及他们常用商店的详细信息,该怎么办?使用ngRoute并不容易:
resolve: { customer: function($routeParams,CustomerService) { return CustomerService.get($routeParams.customerId); },usualStore: function(StoreService) { // can't access 'customer' object here,so can't get their usual store var storeId = ...; return StoreService.get(storeId); } }
您可以通过在注入客户后从控制器加载ordinaryStore来解决这个问题,但是为什么在ui-router中使用依赖的解决方案干净地完成它时会烦恼:
resolve: { customer: function($stateParams,CustomerService) { return CustomerService.get($stateParams.customerId); },usualStore: function(StoreService,customer) { // this depends on the 'customer' resolve above return StoreService.get(customer.usualStoreId); } }