到现在为止,我们使用是硬编码的三条手机记录数据集。现在我们使用AngularJS一个内置服务$http来获取一个更大的手机记录数据集。我们将使用AngularJS的依赖注入(dependency injection (DI))功能来为PhoneListCtrl控制器提供这个AngularJS服务。
请重置工作目录:
git checkout -f step-5
刷新浏览器,你现在应该能看到一个20部手机的列表。
步骤4和步骤5之间最重要的不同在下面列出。你可以在GitHub里看到完整的差别。
数据
你项目当中的app/phones/phones.json文件是一个数据集,它以JSON格式存储了一张更大的手机列表。
下面是这个文件的一个样例:
[ { "age": 13, "id": "motorola-defy-with-motoblur", "name": "Motorola DEFY\u2122 with MOTOBLUR\u2122", "snippet": "Are you ready for everything life throws your way?" ... }, ... ]
控制器
我们在控制器中使用AngularJS服务$http向你的Web服务器发起一个HTTP请求,以此从app/phones/phones.json文件中获取数据。$http仅仅是AngularJS众多内建服务中之一,这些服务可以处理一些Web应用的通用操作。AngularJS能将这些服务注入到任何你需要它们的地方。
服务是通过AngularJS的依赖注入DI子系统来管理的。依赖注入服务可以使你的Web应用良好构建(比如分离表现层、数据和控制三者的部件)并且松耦合(一个部件自己不需要解决部件之间的依赖问题,它们都被DI子系统所处理)。
app/js/controllers.js
function PhoneListCtrl($scope, $http) { $http.get('phones/phones.json').success(function(data) { $scope.phones = data; }); $scope.orderProp = 'age'; }
$http向Web服务器发起一个HTTP GET请求,索取phone/phones.json(注意,url是相对于我们的index.html文件的)。服务器用json文件中的数据作为响应。(这个响应或许是实时从后端服务器动态产生的。但是对于浏览器来说,它们看起来都是一样的。为了简单起见,我们在教程里面简单地使用了一个json文件。)
$http服务用success返回[对象应答][ng.$q]。当异步响应到达时,用这个对象应答函数来处理服务器响应的数据,并且把数据赋值给作用域的phones数据模型。注意到AngularJS会自动检测到这个json应答,并且已经为我们解析出来了!
为了使用AngularJS的服务,你只需要在控制器的构造函数里面作为参数声明出所需服务的名字,就像这样:
function PhoneListCtrl($scope, $http) {...}
当控制器构造的时候,AngularJS的依赖注入器会将这些服务注入到你的控制器中。当然,依赖注入器也会处理所需服务可能存在的任何传递性依赖(一个服务通常会依赖于其他的服务)。
注意到参数名字非常重要,因为注入器会用他们去寻找相应的依赖。
'$'前缀命名习惯
你可以创建自己的服务,实际上我们在步骤11就会学习到它。作为一个命名习惯,AngularJS内建服务,作用域方法,以及一些其他的AngularJS API都在名字前面使用一个‘$'前缀。不要使用‘$'前缀来命名你自己的服务和模型,否则可能会产生名字冲突。
关于JS压缩
由于AngularJS是通过控制器构造函数的参数名字来推断依赖服务名称的。所以如果你要压缩PhoneListCtrl控制器的JS代码,它所有的参数也同时会被压缩,这时候依赖注入系统就不能正确的识别出服务了。
为了克服压缩引起的问题,只要在控制器函数里面给$inject属性赋值一个依赖服务标识符的数组,就像被注释掉那段最后一行那样:
PhoneListCtrl.$inject = ['$scope', '$http'];
另一种方法也可以用来指定依赖列表并且避免压缩问题——使用Javascript数组方式构造控制器:把要注入的服务放到一个字符串数组(代表依赖的名字)里,数组最后一个元素是控制器的方法函数:
var PhoneListCtrl = ['$scope', '$http', function($scope, $http) { /* constructor body */ }];
上面提到的两种方法都能和AngularJS可注入的任何函数完美协作,要选哪一种方式完全取决于你们项目的编程风格,建议使用数组方式。
测试
test/unit/controllerSpec.js:
由于我们现在开始使用依赖注入,并且我们的控制器也含有了许多依赖服务,所以为我们的控制器构造测试就有一点小小的复杂了。我们需要使用new操作并且提供给构造器包括$http的一些伪实现。然而,我们推荐的方法(而且更加简单噢)是在测试环境下创建一个控制器,使用的方法和AngularJS在产品代码于下面的场景下做的一样: