第2章
用Form表单做一个登录控件
从这一章起我们会打造一个待办事项列表的应用(Todo),这个Todo应用让人们可以输入新的待办事项,事项完成后,可以标记其为完成,也可以全部反转目前列表中事项的完成状态,以及清除所有已完成的事项。列表中还会有一个筛选器,用户可以通过筛选器筛选出所有活动的事项以及已完成的事项。这个应用在各个前端框架的比较中经常用到,因为它麻雀虽小五脏俱全。
当然我们会再给这个应用加点料,首先这个应用应该有Web API后台,而不仅仅是一个内存版本。再有,我们要打造一个支持多用户的待办事项列表,也就是说有用户的注册和登录,每个用户用自己的用户名和密码登录后都可以看到自己的待办事项列表。这样的话它的逻辑相对完整了,增删改查都有,HTTP请求、返回俱全。在一个平台折腾明白这个App就基本可以上手干活了,这货简直就是新时代的Hello World啊。
第2章我们会通过制作一个登录组件了解引用、双向数据绑定、依赖性注入以及表单的验证。
第3章我们开始在登录之外又建立了Todo组件,因为有多个组件,所以我们也会学习路由的概念。并且在这一章我们会建立一个虚拟Web服务,使得我们从一开始设计时就是按照HTTP的异步性质进行设计的。
第4章我们分拆了Todo组件,让每一个子组件负责的部分更加清楚。但分拆后就会出现组件间通信的问题,我们会一起学习父子组件的通信方式。随着功能变复杂,我们会想把相对独立的功能分离出来,这就需要我们为Todo单独构建一个模块。这一章我们会把欠缺的功能都完善掉,也就是说就单机版的Todo来说,功能已经比较完善了。
第5章我们会从实际操作的角度一起来思考和构建多用户版本的待办事项应该如何搭建。这一章我们会比较细致地讨论多种类型的路由。
第6章我们引入了第三方样式库,这也是前端工作中经常碰到的。我们一起用第三方样式库改造我们的应用。这一章我们还会一起按谷歌官方的最佳实践优化模块,并且会学习如何引入第三方Javascript类库以及如何发布到生产环境。当然我们还会接触到两个重要概念:管道和指令。
第7章,我们补全了注册功能,还利用Angular 2提供的动画功能添加了一些炫酷的动画效果。
第8章我们会介绍一个Angular的杀手锏Rx,Rx需要的学习曲线较陡,所以这一章我们举了很多小例子来帮你理解。并且我们也会一起把Todo改造成流式应用。
第9章我们更进一步,将业界最流行的状态管理模式Redux引入进来,Redux会以中心化的存储方式,让我们摆脱那些散在程序各个角落的难以维护的状态。
这一章我们会通过制作一个登录组件来了解引用、双向数据绑定、依赖性注入以及表单的验证。
引用是Angular 2提供的一种可以在模板内引用DOM元素或者指令的变量,我们经常会发现在实际工作中,我们需要在类似于普通JavaScript开发中需要的那样去引用一些DOM元素做一些小处理,这个引用的机制可以使我们很方便地达成这个目的。
双向数据绑定一向都是Angular引以为豪的优点,可以减轻很多编码的工作量,Angular 2底层通过Rx来实现这种双向绑定,使得双向绑定和Angular 1.x同样简单但性能更好。
依赖性注入(Dependency Injection)这个概念是Google一向在Android开发领域极力提倡的,这种机制可以让应用的组件、服务等松耦合,使得应用模块化,可维护性更佳。依赖注入是将所依赖的传递给将使用的从属对象(即客户端),而不是从客户端声明和构造,这种模式通常也叫控制反转(IoC – Inverse of Control)。
表单验证一直是客户端和前端开发中最常见的功能需求,Angular 2作为通用的开发平台,当然提供了极为强大的内建表单验证功能。这一章我们也会进行一个初体验。
2.1 对于login组件的小改造
在 hello-angular\src\app\login\login.component.ts 中更改其模板为下面的样子:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-login',
template: '
<div>
<input type="text">
<button>Login</button>
</div>
',
styles: []
})
export class LoginComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
我们增加了一个文本输入框和一个按钮,保存后返回浏览器可以看到结果,如图 2.1所示。你会发现一个有趣的特性,我们大多数情况下不需要重启服务,结果就会自动刷新了。这个特性极大地提升了我们的开发生产效率,当然如果我们做了较大改动,我还是建议大家重启服务的。
图2.1 为组件增加了一个输入框和按钮
接下来我们尝试给Login按钮添加一个处理方法 <button (click)="onClick()">Login</button>。(click)表示我们要处理这个button的click事件,圆括号是说发生此事件时,调用等号后面的表达式或函数。等号后面的onClick()是我们自己定义在LoginComponent中的函数,这个名称你可以随便定成什么,不一定叫onClick()。
下面我们就来定义这个函数,在LoginComponent中写一个叫onClick()的方法,内容很简单,就是把“button was clicked”输出到Console。
onClick() {
console.log('button was clicked');
}
返回浏览器,并按F12调出开发者工具。当你点击Login时,会发现Console窗口输出了我们期待的文字。如图 2.2所示,右边的部分就是Chrome开发者工具了。
图2.2 Chrome开发者工具
如果你感觉左右的布局不舒服,可以点击右上角的三个竖着排列的点的那个按钮,如图 2.3所示,可以选择单独窗口,窗口底部和窗口右方等。
图2.3 Chrome开发者工具的布局方式
那么如果要在onClick中传递一个参数,比如是上面的文本输入框输入的值,怎么处理呢?我们可以在文本输入框标签内加一个#usernameRef,这个叫引用(reference)。注意这里引用的是input对象,我们如果想传递input的值,可以用usernameRef.value,然后就可以把onClick()方法改成onClick(usernameRef.value):
<div>
<input #usernameRef type="text">
<button (click)="onClick(usernameRef.value)">Login</button>
</div>
在Component内部的onClick方法也要随之改写成一个接受username的方法
onClick(username) {
console.log(username);
}
现在我们再看看结果是什么样子,在文本输入框中键入“hello”,点击Login按钮,观察Console窗口:hello被输出了,如图 2.4所示。
图2.4 Console窗口
好了,现在我们再加一个密码输入框,然后改写onClick方法使其可以同时接收2个参数:用户名和密码。代码如下:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-login',
template: '
<div>
<input #usernameRef type="text">
<input #passwordRef type="password">
<button (click)="onClick(usernameRef.value, passwordRef.value)">Login</button>
</div>
',
styles: []
})
export class LoginComponent implements OnInit {
constructor() { }
ngOnInit() {
}
onClick(username, password) {
console.log('username:' + username + "\n\r" + "password:" + password);
}
}
看看结果吧,在浏览器中第一个输入框里输入“wang”,第二个输入框里输入“1234567”,观察Console窗口,如图2.5所示,Bingo!
图2.5 在Chrome开发者工具中观察元素引用的使用