注:学习使用,禁止转载
一个简单的reddit应用程序
在这一章,我们编写一个能够提交一篇文章(包含URL以及标题)并且可以对帖子进行投票的应用程序。
你可以认为这个应用程序是Reddit.com或者Product Hunt的原型。
在这个简单的应用程序中,我们将会学习到angular2包含的一个必须的部分:
- 编写一个自定义组件
- 从表单接受用户输入
- 在视图上渲染一个对象列表
- 拦截用户点击事件并响应它们
学完这章,你可以掌握怎么去搭建一个基本的angular 2应用程序。
下面是我们App的截图:
首先,用户可以提交一个新的链接,之后可以对该文章进行添加投票或者减少投票。每一个链接都有一个分数并且我们可以找到我们感兴趣的链接进行投票。
在这个项目乃至整本书中,我们使用Typescript。Typescript是ES6的超集,它增加了类型信息。在本章,我们不会深入讨论Typescript,但是如果你了解ES5或者ES6,你不会感觉到任何问题。我们将会在下一章深入学习Typescript,所以,如果你有任务语法方面的问题,不用担心。
开始
Typescript
为了使用 Typescript,你需要安装node.js,这里有很多种方式去安装node.js,所以请参阅Node.js的网站的详细信息。
:fa-info-circle:难道我必须使用Typescript吗?在angular2中,你可以使用Typescript,但是不是必须的。但是ng2使用Typescript进行编写,所以每一个人都必须了解。在这本书中,我们使用Typescript,因为他跟ng2结合起来更加简单。意思就是说,这个不是必须的。
一旦你安装了node.js,接下来就是安装Typescript。保证你安装的版本在1.7或者以上。为了安装他,请运行下面的命令:
$ npm install -g typescript
npm是作为node.js的一部分安装的,如果在你系统中没有npm,请确认你安装node.js的时候已经安装了npm。
Windows用户:在本书中,我们使用linux/mac系统里面的命令行工具。在windows中,我们强烈建议你安装Cygwin作为运行命令行的工具。
示例应用程序
既然环境已经准备好了,让我们开始编写第一个应用程序。打开本书的代码并且解压缩它,在你的终端里面,使用cd进入first_app/angular2-reddit-base目录。
$ cd first_app/angular2-reddit-base
如果你熟悉cd,它代表的是change directory(改变目录),如果你使用的是mac,可以试一下下面的步骤:
- 打开/Applications/Utilities/Terminal.app
- 键入cd
- 在finder中,拖动first_app/angular2-reddit-base目录到你的终端;
- 键入Enter键,你就可以进入指定的目录了。
让我们使用下面的命令安装所有的依赖:
$ npm install
在根目录下面,创建一个新的index.html并且增加下面的基本结构:
<!doctype html>
<html>
<head>
<title>Angular 2 - Simple Reddit</title> </head>
<body>
</body>
</html>
你的angular2-reddit-base目录看起来像下面这样:
ng2本身就是一个js文件。所以你需要添加一个script标签到index.html,但是ng2有一些自己的依赖。
Ng2的依赖
为了使用ng2,你可以不需要理解这些依赖,但是你必须包含它们,如果你对它们没有兴趣,你可以跳过这一部分,但是记住,必须包含它们。
为了运行ng2,需要依赖下面的四个库:
- es6-shim
- zone.js
- reflect-metadata
- SystemJS
为了包含它们,将下面的内容包含在你的head标签里面。
<script src="node_modules/es6-shim/es6-shim.js"> </script>
<script src="node_modules/zone.js/dist/zone.js"> </script>
<script src="node_modules/reflect-metadata/Reflect.js"> </script>
<script src="node_modules/systemjs/dist/system.src.js"> </script>
:fa-info-circle:注意,我们是直接从node_modules目录加载这些.js文件的。当你运行npm install的时候,该目录就会被创建。如果没有该目录,确保你运行npm install是在angular2-reddit-base目录下面运行的。
ES6 Shim
ES6 shim 提供垫片使传统的JavaScript引擎表现得尽可能的ECMAScript 6。不是新版本的Safari、Chrome等严格要求,但它是旧版本的IE的要求.
Zones
在ng2的开发过程中,Angular团队为我们带来了一个新的库 – zone.js。zone.js的设计灵感来源于Dart语言,它描述JavaScript执行过程的上下文,可以在异步任务之间进行持久性传递,它类似于Java中的TLS(thread-local storage: 线程本地存储)技术,zone.js则是将TLS引入到JavaScript语言中的实现框架。
在本文开篇提到zone.js为JavaScript提供了执行上下文,可以在异步任务之间进行持久性传递。该是zone.js上场的时候了。zone.js采用猴子补丁(Monkey-patched)的暴力方式将JavaScript中的异步任务都包裹了一层,使得这些异步任务都将运行在zone的上下文中。每一个异步的任务在zone.js都被当做为一个Task,并在Task的基础上zone.js为开发者提供了执行前后的钩子函数(hook)。这些钩子函数包括:
- onZoneCreated:产生一个新的zone对象时的钩子函数。
- zone.fork也会产生一个继承至基类zone的新zone,形成一个独立的zone上下文;
- beforeTask:zone Task执行前的钩子函数;
- afterTask:zone Task执行完成后的钩子函数;
- onError:zone运行Task时候的异常钩子函数;
并且zone.js对JavaScript中的大多数异步事件都做了包裹封装,它们包括:
- zone.alert;
- zone.prompt;
- zone.requestAnimationFrame、
- zone.webkitRequestAnimationFrame、
- zone.mozRequestAnimationFrame;
- zone.addEventListener;
- zone.addEventListener、zone.removeEventListener;
- zone.setTimeout、zone.clearTimeout、zone.setImmediate;
- zone.setInterval、zone.clearInterval
以及对promise、geolocation定位信息、websocket等也进行了包裹封装,你可以在这里找到它们https://github.com/angular/zone.js/tree/master/lib/patch。
Reflect Metadata
angular2是使用Typescript写的,而Typescript又提供了annotation,使得可以向代码中添加元数据。严格意义上说,反射元数据包是一个让我们可以使用注解的polyfill。
SystemJS
systemjs 是一个最小系统加载工具,用来创建插件来处理可替代的场景加载过程,包括加载 CSS 场景和图片,主要运行在浏览器和 NodeJS 中。它是 ES6 浏览器加载程序的的扩展,将应用在本地浏览器中。通常创建的插件名称是模块本身,要是没有特意指定用途,则默认插件名是模块的扩展名称。
加载所有的依赖
现在我们加载了所有的依赖,我们的index.html看起来应该是下面这样。
<!doctype html>
<html>
<head>
<title>Angular 2 - Simple Reddit</title>
<!-- Libraries -->
<script src="node_modules/es6-shim/es6-shim.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script> <script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
</head>
<body>
</body>
</html>
添加CSS
我们也需要添加一写CSS样式去美化我们的应用,让我们包含两个CSS样式。
<!doctype html>
<html>
<head>
<title>Angular 2 - Simple Reddit</title>
<!-- Libraries -->
<script src="node_modules/es6-shim/es6-shim.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<!-- Stylesheet -->
<link rel="stylesheet" type="text/css"
href="resources/vendor/semantic.min.css">
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
</body>
</html>
我们的第一个Typescript文件
让我们创建我们的第一个Typescript文件,创建一个app.ts的文件在相同的目录。并且添加下面的代码:
// your code goes here
import { bootstrap } from "@angular/platform-browser-dynamic";
import { Component } from "@angular/core";
@Component({
selector: 'hello-world',
template: `
<div>
Hello world
</div>
`
})
class HelloWorld {
}
bootstrap(HelloWorld);
这个代码第一眼看起来可能有点怪异,但是别担心。我们将会一步一步解释它。
Typescript是一个具有类型的Javascript,为了在浏览器中使用angular,我们需要告诉Typescript编译器我们发现了哪些类型文件。那个reference标明了一些类型文件的路径。
import语句定义了我们代码中需要使用的模块,这里我们import了两个模块:Component和bootstrap.
我们从”@angular/core”导入Component,那个”@angular/core”告诉我们应用程序去哪里寻找模块。
我们从”@angular/platform-browser-dynamic”导入bootstrap。
注意,import的格式为 import {thing} from wherever,在{thing}部分,我们叫着解构。解构是es6提供的功能,我们在下一章会讲到。import特别像java中的import或者Ruby中的require.我们从特定模块中拉取这些依赖使得它在本文件中可用。
构造一个组件
angular2最伟大的创意背后就是组件。
在我们的angular应用程序中,我们写的HTML标签使得我们的程序编程交互式的程序。但是浏览器中的标签是很少的,比如:select,form,video等,如果我们希望教给浏览器一个新的标签?比如我们希望一个标签去显示天气或者我们希望一个标签代表去创建一个登陆面板?
这个注意的背后就是组件,它教给浏览器一个新的功能,对应一个新的标签。
:fa-info-circle:如果你有ng1的背景,那么组件就是directive的新版本。
让我们创建第一个组件。当我们创建完这个组件,我们可以在html中像下面这样使用它:
<hello-world></hello-world>
那么我们怎么去定义一个组件呢?一个简单的组件包含两部分:
1.一个Component注解
2. 定义组件的类
让我们花点时间研究下。
如果你之前使用javascript编写过程序,那么下面的代码看起来有点怪异:
@Component({
//...
})
这是什么?如果你有java的背景,那么你对这个东西比较熟悉,它是一个注解。
注解就是将元数据加入你的代码中。当我们使用@Component在一个HelloWorld类上面时,我们正在装饰那个HelloWorld类使其成为一个组件。
我们想通过在Html中用标签代表我们的组件怎么弄?为了实现这个功能,我们用hello-world配置@Component中的selector。
@Component({
selector: 'hello-world'
})
如果你熟悉css/xpath/jquery选择器,那么你会知道有很多种方式去配置一个选择器。
angular增加了它自己独特的混合选择器,我们将在后面的章节中讲到。现在,我们只需要知道,这个就是定义了一个新的标签。
selector属性标志了这个组件在html中怎么使用。如果你有任何的标签在html中,它将会