原文:http://blog.javachen.com/2015/01/09/angular-phonecat-examples/javascript
AngularJS 官方网站提供了一个用于学习的示例项目:PhoneCat。这是一个Web应用,用户能够浏览一些Android手机,了解它们的详细信息,并进行搜索和排序操做。php
本文主要分析 AngularJS 官方网站提供的一个用于学习的示例项目 PhoneCat 的构建、测试过程以及代码的运行原理。但愿可以对 PhoneCat 项目有一个更加深刻全面的认识。这其中包括如下内容:css
对于 PhoneCat 项目的开发环境和测试环境的搭建,官方网站上提供了详细的指导: http://docs.angularjs.org/tutorial ,你能够找到一些中文的翻译。html
PhoneCat 项目的源代码托管在 GitHub 上,能够经过下面命令下载源代码:前端
$ git clone --depth=20 https://github.com/angular/angular-phonecat.git
--depth=20
选项的意思为:仅下载最近20次的代码提交版本;这么作能够减小下载的文件大小,加快下载。java
PhoneCat 是一个 Web 应用程序,所以最好在 Web 服务器中运行,以期得到最佳结果。官方推荐安装 Node.js 。node
PhoneCat 项目的运行与测试依赖一些别的工具,能够在安装 Node.js 后经过 npm 命令来安装这些依赖包。如下命令需在 angular-phonecat 项目路径下运行:jquery
$ npm install
运行该命令后,会在 angular-phonecat 项目路径下安装如下依赖包:git
Bower
包管理器Http-Server
轻量级Web服务器Karma
用于运行单元测试Protractor
用于运行端到端测试几乎全部的 AngularJS 学习教程,都会写到用这个命令来启动服务:angularjs
$ node scripts/web-server.js
但实际上 PhoneCat 项目已经放弃使用 web-server 了,git 上取下来的的项目里没有 scripts/web-server.js 文件了。
咱们能够用下面的方式来启动工程:
$ npm start
而后经过 http://localhost:8000/app/index.html 访问。
在克隆项目以后,目录以下:
➜ angular-phonecat git:(master) ✗ tree -L 2 . ├── LICENSE ├── README.md ├── app │ ├── bower_components │ ├── css │ ├── img │ ├── index.html │ ├── js │ ├── partials │ └── phones ├── bower.json ├── package.json ├── scripts │ ├── private │ └── update-repo.sh └── test ├── e2e ├── karma.conf.js ├── protractor-conf.js └── unit 20 directories, 8 files
这个目录下存在一个文件 package.json,该文件是作什么用的呢?
在 NodeJS 项目中,用 package.json 文件来声明项目中使用的模块,这样在新的环境部署时,只要在 package.json 文件所在的目录执行 npm install
命令便可安装所须要的模块。
关于 package.json 中可配置的选项请参考 package.json字段全解 。
从该文件能够看出 PhoneCat 的依赖:
"devDependencies": { "karma": "^0.12.16", "karma-chrome-launcher": "^0.1.4", "karma-jasmine": "^0.1.5", "protractor": "~1.0.0", "http-server": "^0.6.1", "tmp": "0.0.23", "bower": "^1.3.1", "shelljs": "^0.2.6" }
以及一些脚本:
"scripts": { "postinstall": "bower install", "prestart": "npm install", "start": "http-server -a 0.0.0.0 -p 8000", "pretest": "npm install", "test": "node node_modules/karma/bin/karma start test/karma.conf.js", "test-single-run": "node node_modules/karma/bin/karma start test/karma.conf.js --single-run", "preupdate-webdriver": "npm install", "update-webdriver": "webdriver-manager update", "preprotractor": "npm run update-webdriver", "protractor": "protractor test/protractor-conf.js", "update-index-async": "node -e \"require('shelljs/global'); sed('-i', /\\/\\/@@NG_LOADER_START@@[\\s\\S]*\\/\\/@@NG_LOADER_END@@/, '//@@NG_LOADER_START@@\\n' + cat('bower_components/angular-loader/angular-loader.min.js') + '\\n//@@NG_LOADER_END@@', 'app/index-async.html');\"" }
从上能够看出运行 npm start
以前会运行 npm install
,而后运行 http-server -a 0.0.0.0 -p 8000
启动一个 web 服务器,最后是运行 bower install
安装 bower 管理的包。
bower 管理的包由 bower.json 文件定义:
{ "name": "angular-phonecat", "description": "A starter project for AngularJS", "version": "0.0.0", "homepage": "https://github.com/angular/angular-phonecat", "license": "MIT", "private": true, "dependencies": { "angular": "1.3.x", "angular-mocks": "1.3.x", "jquery": "~2.1.1", "bootstrap": "~3.1.1", "angular-route": "1.3.x", "angular-resource": "1.3.x", "angular-animate": "1.3.x" } }
固然,package.json 文件中还定义了一些测试相关的命令。
关于 bower 的介绍,参考博客内文章:bower介绍。
在本项目中,bower 下载的包保存在 angular-phonecat/app/bower_components 目录下,依赖以下:
├── bower_components │ ├── angular │ ├── angular-animate │ ├── angular-mocks │ ├── angular-resource │ ├── angular-route │ ├── bootstrap │ └── jquery
Karma 是一个 Javascript 测试运行工具,能够帮助你关闭反馈循环。Karma 能够在特定的文件被修改时运行测试,它也能够在不一样的浏览器上并行测试。不一样的设备能够指向 Karma 服务器来覆盖实际场景。
关于 Karma 的使用,本文不作介绍。
http-server 是一个简单的零配置命令行 HTTP 服务器,基于 Node.js 。
在命令行中使用方式是:
$ node http-server
在package.json 中定义方式是:
"scripts": { "start": "http-server -a 0.0.0.0 -p 8000", }
支持的参数:
-p 端口号 (默认 8080) -a IP 地址 (默认 0.0.0.0) -d 显示目录列表 (默认 'True') -i 显示 autoIndex (默认 'True') -e or --ext 若是没有提供默认的文件扩展名(默认 'html') -s or --silent 禁止日志信息输出 --cors 启用 CORS -o 在开始服务后打开浏览器 -h or --help 打印列表并退出 -c 为 cache-control max-age header 设置Cache time(秒) ,禁用 caching, 则值设为 -1 .
Protractor 是一个端对端的测试运行工具,模拟用户交互,帮助你验证你的 Angular 应用的运行情况。
Protractor 使用 Jasmine 测试框架来定义测试。Protractor 为不一样的页面交互提供一套健壮的 API。
固然,也有其余的端对端工具,不过 Protractor 有着本身的优点,它知道怎么和 AngularJS 的代码一块儿运行,特别是面临 $digest 循环的时候。
关于 Protractor 的使用,本文不作介绍。
ShellJS 是 Node.js 扩展,用于实现 Unix shell 命令执行,支持 Windows。
一个示例代码:
require('shelljs/global'); if (!which('git')) { echo('Sorry, this script requires git'); exit(1); } // Copy files to release dir mkdir('-p', 'out/Release'); cp('-R', 'stuff/*', 'out/Release'); // Replace macros in each .js file cd('lib'); ls('*.js').forEach(function(file) { sed('-i', 'BUILD_VERSION', 'v0.1.2', file); sed('-i', /.*REMOVE_THIS_LINE.*\n/, '', file); sed('-i', /.*REPLACE_LINE_WITH_MACRO.*\n/, cat('macro.js'), file); }); cd('..'); // Run external tool synchronously if (exec('git commit -am "Auto-commit"').code !== 0) { echo('Error: Git commit failed'); exit(1); }
在 PhoneCat 中,主要是用在下面:
"update-index-async": "node -e \"require('shelljs/global'); sed('-i', /\\/\\/@@NG_LOADER_START@@[\\s\\S]*\\/\\/@@NG_LOADER_END@@/, '//@@NG_LOADER_START@@\\n' + cat('bower_components/angular-loader/angular-loader.min.js') + '\\n//@@NG_LOADER_END@@', 'app/index-async.html');\""
PhoneCat 项目中的单元测试是使用 Karma 来完成的,全部的单元测试用例都存放在 test/unit 目录下。能够经过执行如下命令来运行单元测试:
$ npm test
值得一提的是,在运行单元测试前,计算机上必须安装 Google Chrome 浏览器,由于这里用到了 karma-chrome-launcher 。
PhoneCat 项目使用端到端测试来保证 Web 应用的可操做性,而这个端到端测试是经过使用 Protractor 来实现的,全部的端到端测试用例都存放在test/e2e 目录下。能够经过执行如下步骤来运行端到端测试:
//更新webdriver,此命令只需运行一次
$ npm run update-webdriver //运行PhoneCat $ npm start
打开另外一个命令行窗口,在其中运行:
$ npm run protractor
在介绍了 PhoneCat 的运行和测试环境后,来看看 PhoneCat 的页面和 js 是怎么组织起来的。
路由
、 动画
、 资源
。ng-app
定义在 html 节点上,即做用于整个页面,其名称为phonecatApp
。ng-view
指定加载子视图的位置,这里主要包括 partials/phone-list.html
和 partials/phone-detail.html
两个视图。app.js 内容以下:
//JavaScript语法支持严格模式:若是在语法检测时发现语法问题,则整个代码块失效,并致使一个语法异常;若是在运行期出现了违反严格模式的代码,则抛出执行异常。 'use strict'; /* App Module */ //定义一个模块,模块名称和页面 ng-app 中内容一致 var phonecatApp = angular.module('phonecatApp', [ 'ngRoute', 'phonecatAnimations', 'phonecatControllers', 'phonecatFilters', 'phonecatServices' ]); //定义路由 phonecatApp.config(['$routeProvider', function($routeProvider) { $routeProvider. when('/phones', { templateUrl: 'partials/phone-list.html', controller: 'PhoneListCtrl' }). when('/phones/:phoneId', { templateUrl: 'partials/phone-detail.html', controller: 'PhoneDetailCtrl' }). otherwise({ redirectTo: '/phones' }); }]);
phonecatApp 模块依赖其余几个模块:ngRoute、phonecatAnimations、phonecatControllers、phonecatFilters、phonecatServices。
ngRoute 是内置的路由模块,定义路由规则:
/phones
,由 PhoneListCtrl
控制器处理,而且由 partials/phone-list.html
模板渲染显示内容。/phones/:phoneId
,由 PhoneDetailCtrl
控制器处理,而且由partials/phone-detail.html
模板渲染显示内容。/phones
phonecatAnimations 模块是定义动画效果,没有真个模块不影响程序的主要功能的运行,故不分析这部分代码。
phonecatControllers 模块定义在 controllers.js 文件中:
'use strict'; /* Controllers */ var phonecatControllers = angular.module('phonecatControllers', []); // 定义 PhoneListCtrl,并注入 Phone 对象 phonecatControllers.controller('PhoneListCtrl', ['$scope', 'Phone', function($scope, Phone) { $scope.phones = Phone.query(); $scope.orderProp = 'age'; }]); // 定义 PhoneDetailCtrl,并注入 Phone 对象和 $routeParams,$routeParams 封装了路由参数。 phonecatControllers.controller('PhoneDetailCtrl', ['$scope', '$routeParams', 'Phone', function($scope, $routeParams, Phone) { $scope.phone = Phone.get({phoneId: $routeParams.phoneId}, function(phone) { //回调方法 $scope.mainImageUrl = phone.images[0]; }); $scope.setImage = function(imageUrl) { $scope.mainImageUrl = imageUrl; } }]);
phonecatFilters 模块定义在 filter.js 文件中,主要是自定义了一个过滤器checkmark
:根据输入是否有内容判断返回 ✓
仍是 ✘
。
phonecatServices 模块定义在 services.js 文件中:
'use strict'; /* Services */ var phonecatServices = angular.module('phonecatServices', ['ngResource']); // 定义 Phone 服务,并提供了一个 query 方法,还包括一个内置的 get 方法。调用 get 方法实际上就是调用 query 方法,而且能够传递一个参数 phoneId phonecatServices.factory('Phone', ['$resource', function($resource){ return $resource('phones/:phoneId.json', {}, { query: {method:'GET', params:{phoneId:'phones'}, isArray:true} }); }]);