现在是时候让我们的页面动起来了——使用AngularJS,我们同时测试我们给controller添加的代码的可行性。 有很多方法去组织代码编写程序,对于Angular程序,你们鼓励使用 模型-视图-控制器 (MVC设计模式)(本站也有 介绍)去解耦代码,分离关注点。针对这个想法,让我们用一点点Angular和JavaScrip给我们的应用增加模型、视图、控制器组件。
现在三个手机的数据自动生成。
工作空间重置介绍 重置你的工作区间到第二步
git checkout -f step-2
刷新你的浏览器,或者在线上检出这一步:第二步例子。 大部分重要的修改都列在下面,你在 GitHub上能看全部不同:
视图与模板
在Angular中,通过HTML模板,视图是模型的投影。这意味着无论模型修改什么,Angular都刷新合适的绑定点来会刷新视图。 视图是由Angular使用下面的模板构建:
<html ng-app="phonecatApp">
<head>
...
<script src="bower_components/angular/angular.js"></script>
<script src="js/controllers.js"></script>
</head>
<body ng-controller="PhoneListCtrl">
<ul>
<li ng-repeat="phone in phones">
<span>{{phone.name}}</span>
<p>{{phone.snippet}}</p>
</li>
</ul>
</body>
</html>
我们用 ngRepeat命令与两个 Angular表达式替换了手写的手机列表。
<li>
标签中ng-repeat="phone in phones"
属性是Angular重复器的指令。重复器告诉Angular像模板一样给列表中的每个手机创建一个<li>
元素。表达式用双花括号包裹,
{{phone.name}}
和{{phone.snippet}}
将会用值替换掉表达式。
我们增加了命令,叫 ng-controller
, 依附 <body>
标签的 PhoneListGtrl
控制器,在这些点:
在双花括号中的表达式
{{phone.name}}
和{{phone.snippet}}
表示数据绑定,他引用到我们在PhoneListCtrl
控制器里应用模型中。
注意:我们已经使用ng-app="phonecatApp"
定义一个Angular模块读取,phonecatApp
是模块的名称,模块会包括PhoneListsCtrl
。
模型与控制器
现在数据模型在PhoneListCtrl控制器中被实例化,控制器有一个简单的构造器接受$scope参数。 app/js/controllers.js:
var phonecatApp = angular.module('phonecatApp', []);
phonecatApp.controller('PhoneListCtrl', function ($scope) {
$scope.phones = [
{'name': 'Nexus S',
'snippet': 'Fast just got faster with Nexus S.'},
{'name': 'Motorola XOOM™ with Wi-Fi',
'snippet': 'The Next, Next Generation tablet.'},
{'name': 'MOTOROLA XOOM™',
'snippet': 'The Next, Next Generation tablet.'}
];
});
这里我们定义了一个叫 PhoneListCtrl
的控制器,注册到AngularJS模块phoncatApp中。注意当启动angular应用,现在我们 ng-app
命令(在 <html>
上)指定了phoneApp模块名字当作模块去读取。 虽然控制器现在工作不是很好,但他扮演了关键角色,他提供了上下文环境给我们的模型,控制器允许我们在模型与视图中建立数据绑定,我们连接不同的点在介绍,数据和逻辑之间,如下:
ngController命令在
body
标签上,指向我们的控制器的名字,PhoneListCtrl(在javascrip文件上)PhoneListCtrl
控制器连接手机数据到$scope,$scope会注入到我们的控制器中,这个范围是根范围的原型后代,当程序被创建的时候就被定义,控制器范围对所有在<body ng-controller="PhoneListCtrl">
标签内的绑定可见。
范围
范围的概念十分关键,一个范围可以看作是胶水,允许模版、模型与控制器一同工作,Angular使用范围,让在模版、模型、控制器中的数据相互隔离,但是又同步。对模型做出的修改都会反应到视图中,发生在视图中的修改也会反应到模型中。 学习更多的关于Angular范围,看 angular范围文档。
测试
当你们开发中,用Angular方法从视图操作控制器,让测试变得容易,如果我们的控制器对全局命名空间可见,那么我们就能简单地用mock范围对象实例化他。
describe('PhoneListCtrl', function(){
it('should create "phones" model with 3 phones', function() {
var scope = {},
ctrl = new PhoneListCtrl(scope);
expect(scope.phones.length).toBe(3);
});
});
测试实例化 PhoneListCtrl
,并验证了范围中手机含有三条记录,这个例子证明了在Angular中创建单元测试是多么简单,当测试成为如此决定性的部分,我们让测试在Angular容易创建,所以鼓励开发者写测试。
测试非全局控制器
在实际中,你可能不想你的控制器工作在全局变量中,相反,你可以看到我们通过匿名构造器注册一个到 phonecatApp模块
上。 在这个案子中,Angular提供了一个服务 $controller
他会通过名字检索控制器,这里用 $controller
是相同的测试。
在每个测试之前,我们告诉Angular去读取phonecatApp模块。
我们要求Angular
注册
$controller
服务到我们的测试方法中。我们使用$controller去创建
PhoneListCtrl
实例。使用这位实例,我们验证范围内手机列表中三条记录。
写并跑测试
在写测试的时候,Angular开发者比较喜欢Jasmine的行为驱动开发框架的语法。虽然Angular不要求你使用Jasmin,我们用Jasmine v1.3写了教程中的所有测试,你可以在 Jasmine的首页和 Jasmine的文档中学习更多关于Jasmine。 angular种子项目已经预配置使用Karma运行所有的测试,但是你必须确保 Karma和他所需插件已经装上,你可以运行 npm install
安装。 运行测试,并观察文件修改: npm test
Karmma将会自动启动一个Chrome的新实例,可以忽略他让他在后台运行,Karma会使用他执行所有的测试。
在终端你可能会看见下面或相似的输出:
info: Karma server started at http://localhost:9876/
info (launcher): Starting browser "Chrome"
info (Chrome 22.0): Connected on socket id tPUm9DXcLHtZTKbAEO-n
Chrome 22.0: Executed 1 of 1 SUCCESS (0.093 secs / 0.004 secs)
好,测试通过或者没有……
重新运行测试,只要修改任何源码或test.js,Karma会注意这个修改并重新给你运行测试,怎么样,很贴心吧。
确保你没有最小化Karma打开的浏览器,在某些OS中,分配给最小化浏览器的内存是有限的,这就当你的karma测试运行十分缓慢。
实验
添加另一个绑定给 index.html
。如:
<p>Total number of phones: {{phones.length}}</p>
在控制器中创建一个新的模型属性,在模板中绑定,如:
$scope.name = "World";
然后增加一个绑定到index.html
<p>Hello, {{name}}!</p>
刷新你的浏览器,验证他是否说“Hello,Wolrd!” 从 ./test/unit/shontrollerSpec.js
的控制器中更新单元测试,去反射当前的修改,如增加:
expect(scope.name).toBe('World');
修改 index.html
中的重复器,构造一个简单的表格:
<table>
<tr><th>row number</th></tr>
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i}}</td></tr>
</table>
现在,制作一个从1开始的逐一递增的绑定:
<table>
<tr><th>row number</th></tr>
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
</table>
兴奋点:尝试用一个额外的ng-repeat制作一个8x8的表格。 修改 toBe(4)
代替 expect(scope.phones.length).toBe(3)
,让单元测试失败。
总结
现在你有一个动态应用,特地分开了模型、视图与控制器组件,你测试了你所做,现在去 第三步学习如何给你的应用添加一个全文检索。 原文地址: https://docs.angularjs.org/tutorial/step_02