a place to put the code that exposes the right data and functions to the view template.But what about the rest of the code we need to support our applications? The mostobvIoUs place to put this would be in functions on the controllers.
笨拙的翻译如下:
在任何正式的应用中,弄清如何组织你代码中的功能逻辑是一项艰巨的任务。之前我们已经见过如何在 controllers 中正确地放置 data 和 functions 到 view templates 中去。但对于我们应用程序中的其他补充代码呢?最明显的地方可能是你 controllers 中的 functions 中。
This works fine for small apps and the examples that we’ve seen so far,but it quicklybecomes unmanageable in real apps. The controllers would become a dumping groundfor everything and anything we need to do. They’d be hard to understand and likely hard to change.
笨拙翻译:
但是这种方法只适用于目前我们见到过的那些小型的应用和例子而已,在真正的应用中,这样做会让程序变得难以控制。这些 controllers 会变成一个啥都放的垃圾场。它们会变得难以改变。
Enter modules. They provide a way to group dependencies for a functional area withinyour application,and a mechanism to automatically resolve dependencies (also knownas dependency injection). Generically,we call these dependencies services,as they providespecific services to our application.
翻译:
进入模块的世界吧。它们为你的应用程序的功能区域提供一个方法来组织依赖关系,它们提供一个机制用于自动地组织应用程序的依赖关系(也即如前所诉的依赖注入)。通常我们把这些叫做 dependencies serives,因为它们为我们的应用程序提供特定的 services。
For example,if in our shopping website a controller needs to get a list of items for salefrom the server,we’d want some object—let’s call it Items—to take care of getting theitems from the server. The Items object,in turn,needs some way to communicate withthe database on the server over XHR or WebSockets.
Doing this without modules looks something like this:
举个例子,假如我们的 shopping 网站需要从 server 获取一个商品列表,我们会需要一些对象——把它叫做 items——用于从服务器获取这些 items。这个 items 对象,依次地,需要某种方法来连接 server 上的 database 通过 XHR 或 webSockets。假如不用模块的话,我们可以吧代码写成这样:
function ItemsViewController($scope) { // make request to server … // parse response into Item objects … // set Items array on $scope so the view can display it ... }While this would certainly work,it has a number of potential problems.
• If some other controller also needs to get Items from the server,we now have toreplicate this code. This makes maintenance a burden,as now if we make schemaor other changes,we have to update that code in several places.
• With other factors like server authentication,parsing complexity,and so on,it isdifficult to reason about the boundaries of responsibility for this controller object,
and reading the code is harder.
• To unit test this bit of code,we’d either need to actually have a server running,ormonkey patch XMLHttpRequest to return mock data. Having to run the server will
make tests very slow,it’s a pain to set up,and it usually introduces flakiness intotests. The monkey patching route solves the speed and flakiness problems,but it
means you have to remember to un-patch any patched objects between tests,andit brings additional complexity and brittleness by forcing you to specify the exact
on-the-wire format for your data (and in having to update the tests whenever thisformat changes).With modules,and the dependency injection we get from them,we can write our controllermuch more simply,like this:
这种做法当然有效,它有大量潜在的问题:
1.假如还有另外一些 controller 需要从服务器获取 items,我们需要重复代码。这就让部署成为一种折磨,此时假如我们对它进行一些改变例如改变协议,我们需要更新代码好几次。
2.比如服务器端验证,我们不得不或者真的有一个 server 在运行,或者 mock 数据。运行服务器会让测试变得缓慢,搭建服务器也是一件很痛苦的事情,而且会让测试变得不可靠,而且让你不得不指定数据的实际格式。而通过 modules 和 dependency injection,我们可以把我们的 controller 写的异常简单,像这样:
function ShoppingController($scope,Items) {
$scope.items = Items.query();
}
You’re probably now asking yourself,“Sure,that looks cool,but where does Items comefrom?” The preceding code assumes that we’ve defined Items as a service.
Services are singleton (single-instance) objects that carry out the tasks necessary tosupport your application’s functionality. Angular comes with many services like $loca
tion,for interacting with the browser’s location,$route,for switching views based onlocation (URL) changes,and $http,for communicating with servers.
很可能你在这样问自己,“好吧,那看起来很酷,但那 items 从哪儿来的呢?”之前的代码已经假设我们把 items 定义成了 service。Services 是单实例的对象 that 分离出应用程序的功能。 Angular 引进了许多像 $location 来处理浏览器 location,$route 来根据 location(URL) 的改变来切换 views,$http 来处理服务器通讯。
You can,and should,create your own services to do all of the tasks unique to yourapplication. Services can be shared across any controllers that need them. As such,
they’re a good mechanism to use when you need to communicate across controllers andshare state. Angular’s bundled services start with a $,so while you can name themanything you like,its a good idea to avoid starting them with $ to avoid naming collisions.
你能够而且应该创建你自己的 services 去做你的应用程序中的任务。Services 可以被多个需要它们的 controllers 共享。像这样,它们是一个很好的机制去让你实现 controllers 之间的通讯和状态共享。Angular 绑定的服务以 $ 开头,虽然你也可以把服务命名为任何你想要的名称,但还是应避免以 $ 开头,为防止命名冲突。
You define services with the module object’s API. There are three functions for creatinggeneric services,with different levels of complexity and ability:
你可以借助模块提供的 API 来创建 services。三种方法,在复杂度和能力上依次分为三个等级:
1.provider(name,ObjectOR constructor() )
A configurable service with complex creation logic. If you pass an Object,it should have afunction named $get that returns an instance of the service. Otherwise,Angular assumes you’vepassed a constructor that,when called,creates the instance.
一个可配置的服务伴随着复杂的创建逻辑。如果传入一个对象,它必须包含一个名为 $get 并返回该服务的实例的的方法。否则,Angular 会假设你传入的是一个构造函数,每当调用这个构造函数,它创建一个实例。
2.factory(name,$getFunction() )
A non-configurable service with complex creation logic. You specify a function that,whencalled,returns the service instance. You could think of this as provider(name,{ $get:$getFunction() } ).
一个不可配置的服务伴随着负责的构造逻辑。你指定一个函数,这个函数每逢调用,它返回服务的实例。你可以把它看成是一个 provider(name,{$get:$getFunction()})
3.service(name,constructor() )
A non-configurable service with simple creation logic. Like the constructor option with provider,Angular calls it to create the service instance.
一个不可配置的服务伴随着简单的构造逻辑。类似 provider 提供的 contructor 选项,Angular 会调用它来创建一个实例。
We’ll look at the configuration option for provider() later,but let’s discuss an examplewith factory() for our preceding Items example. We can write the service like this:
我们稍后再看 provider() 的配置选项,现在让我们来讨论用 factory() 来实现我们之前的那个 items 的例子。我们可以把 service 写成这样:
var home = angular.module("myApp",[]); home.factory("Items",function () { return { query: function () { return [ {name:'The Handsome Heifer',cuisine:'BBQ'},{name:'Greens Green Greens',cuisine:'Salads'},{name:'House of Fine Fish',cuisine:'Seafood'} ]; } }; }); home.controller("DeathrayMenuController",function ($scope,Items) { $scope.directory = Items.query(); $scope.selectRestaurant = function(row) { $scope.selectedRow = row; }; });
When Angular creates the ShoppingController,it will pass in $scope and the newItems service that we’ve just defined. This is done by parameter name matching. That
is,Angular looks at the function signature for our ShoppingController class,andnotices that it is asking for an Items object. Since we’ve defined Items as a service,it
knows where to get it.
当 Angular 创建 ShoppingController,它会传入 $scope 和刚才创建的这个 Items service。这些工作是由parameter name matching 完成的。
The result of looking up these dependencies as strings means that the arguments ofinjectable functions like controller constructors are order-independent. So instead of
this:
function ShoppingController($scope,Items) {...}也可以写成这个熊样:
function ShoppingController(Items,$scope) {...}and it all still functions as we intended.
To get this to work with our template,we need to tell the ng-app directive the name ofour module,like the following:
为了让它和我们的 template 合同工作,我们需要把 module 的名称告诉 ng-app,就像这样:
<html ng-app='ShoppingModule'>To complete the example,we could implement the rest of the template as:
为了完成这个例子,我们需要把模板搞成这个样子:
<body ng-controller="ShoppingController"> <h1>Shop!</h1> <table> <td>{{item.title}}</td> <td>{{item.description}}</td> <td>{{item.price | currency}}</td> </tr> </table> </div>done!