这是Angular2教程的第二部分,主要介绍Angular2的form的使用。相关博客如下:

  1. Angular2 初探
  2. Angular2 dependency injection
  3. Angular2之rxjs以及http的世界
  4. Angular2 cheatsheet

参考资料

还是小G上个博客里面介绍的例子,链接在这:Angular 2 quick start。文件结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
├──angular2/                                    
│   │
│   ├──app/                               
│   │   ├──boot.ts                          
│   │   ├──app.component.ts   
│   │   ├──hero/                  //这篇博客用 
│   │   |   |──hero.component.ts
│   │   ├──hero-form/             //form blog 用
│   │   |   ├──hero.ts                         
│   │   |   |──hero-form.component.ts
│   │   |   |──hero-form.component.html
│   │   ├──hero-list/             //dependency injection 用
│   │   |   ├──...
│   ├──index.html
│   ├──package.json   //用来安装我们需要的库, 以及node的一些命令
│   ├──tsconfig.json  //用来配置typescript
│   ├──style.css      //这个playground的css

Form Component

在Angular2 初探中介绍了app.component.ts里面的第一个component,下面要介绍的是关于表单的component。上篇博客介绍过了,angular2是一个完全模块化的框架,所以在这我也把整个form component模块化成一个,然后把它加入到app.component中。我定义的form component的selector是hero-form,所以app.component的代码如下:

1
2
3
4
5
6
7
8
9
@Component({
    selector: 'my-app',
    template:`
    < hero >< /hero >
    < hero-form ></ hero-form >   //加入 form component的模块
    < heroes-list >< /heroes-list >
    `,
    directives:[HeroComponent, HeroFormComponent,HeroesListComponent]
})

因为这是一个模块化的世界,所以如果我们想定义一个对象,比如一个英雄,我们要给他一个id,给他一个名字。在Angular2 里面,我们可以给他定义个一个interface,比如:

1
2
3
4
interface Hero {
 id: number;
 name: string;
}

因为是typescript,所以在使用的时候可以给变量定义type。比如我们要定义一群英雄,可以这么写:

1
2
3
4
var HEROES: Hero[] = [
    { "id": 11, "name": "Mr. Nice" },
    { "id": 12, "name": "Narco" }
];

在这里,HEROS就被加上了Hero的type。这是interface的一个用例。然而,当我们的英雄里面不仅仅是要定义type的时候,interface就不够用了,比如我们想定义一些属于英雄的方法,或者逻辑,熟悉ood的童鞋肯定想到了类。没错,在angular2里面你可以像在其他语言里面一样方便的写类了:

1
2
3
4
5
6
7
8
export class Hero {
  constructor(
 public id: number,
 public name: string,
 public power: string,
 public alterEgo?: string
 ) {  }
}

这个类可以被angular2用来新建一个英雄的对象。和其他语言的类一样,这个类可以有一个constructor,来初始化一些参数。比如:

1
let myHero =  new Hero(42, 'SkyDog', 'Fetch any object at any distance', 'Leslie Rollover');

有一点要说明的是“alterEgo”后面有一个问号,这代表这个参数是可选的,在我们新建对象的时候这个参数可有可无。如果没有也不影响整个类的初始化。我们也可以加一些类的函数在里面完成一些功能。
对于这个form component,我会有一个有一个表单的html的模板以及form component来处理表单的验证。上一个博客说到我们可以在component的template里面直接加入html来渲染页面。但当要嵌入的html太长的时候,另外一个解决办法是把要渲染的html写入一个html文件中,然后用templateUrl来加入这个模板。具体如下:

1
2
3
4
@Component({
  selector: 'hero-form',
  templateUrl: 'app/hero-form.component.html'
})

然后这里是我们加入的模板:hero-form.component.html,我会逐个开始介绍。

首先,硬生生的form是不好看的,我们可以先加入bootstrap来让整个form变得好看一些:

1
npm install bootstrap --save

然后在index.html里面把css加入到head里面

1
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">

由于这一章还没说到http的post request,所以就小hack一下用div的[ hidden ]属性来显示这个submit的过程。在这个模块里面定义个一个submitted的变量,初始值是true, 然后提交之前的表单信息在submitted为true的时候显示,提交之后的信息在submitted为false的时候显示。

1
2
< div [hidden]="submitted" >(include form information)< / div >
< div [hidden]="submitted" >(include information after submit)< / div >

在上一章已经说过什么是双向绑定了,而这里也用到了[ ( ngModel ) ]来双向绑定form里面的input。同时我们也可以用*ngFor 来表示下拉框里面可以选择的选项,然后在模块中定义powers就可以有下拉框的选项了。

1
2
3
< select class="form-control" required >
 < option *ngFor="#p of powers" [value]="p" >{{p}}< /option >
< /select >

这样一来,这个表单就做好了,但是在表单中我们想要有verification的信息。比如某个input box必须有内容,或者email的inputbox必须是email的格式,或者下拉框必须选择一个内容等等等等。 在没有angular2的时候,所有的信息会在你按提交按钮的时候进行验证看输入是否正确,然后会给提示哪个输入不正确。但是有了angular2之后,这一切都可以很简单的完成了。
在angular2中,我们使用ngControl来验证输入是否正确。ngControl 根据不同的状态来更新不同的状态,具体如表:
| 状态 | 如果为真 | 如果为假 |
| control被访问了 | ng-touched | ng-untouched |
| control的内容被改变了 | ng-dirty | ng-pristine |
| control的内容是有效的 | ng-valid | ng-invalid |
当我们要使用ngControl的时候只用给他定义个名字即可:

1
2
3
< input type="text" class="form-control" required
  [(ngModel)]="model.name"
  ngControl="name" >

然后具体ngControl的状态如图所示:
Aaron Swartz

然后我们就可以给这些class定义css来给用户页面上的反馈:

1
2
3
4
5
6
7
8
style.css
.ng-valid[required] {
 border-left: 5px solid #42A948; /* green */
}

.ng-invalid {
 border-left: 5px solid #a94442; /* red */
}

然而,如果我们想要再加一个错误信息到表单里面,我们不仅需要ng-valid这个class还需要一个本地模板来操作。具体如下:

1
2
3
4
5
6
< input type="text" class="form-control" required
  [(ngModel)]="model.name"
    ngControl="name"  #name="ngForm" >
< div [hidden]="name.valid" class="alert alert-danger" >
  Name is required
< /div>

在这里,input通过ngModel双向绑定到model的name上,然后和ngFor里面的本地模板一样,对于ngForm我们也要有个本地模板,当我们设定 #name=”ngForm” 的时候,angular2识别了这个语法然后把这个name作为ngControl的一个实例,所以在另外一个没有ngControl的div中我们可以使用name.valid来观察这个状态是否有效,然后选择相应的css。
做完这些input之后就需要做提交功能了。在提交的时候由于上面每个输入框或者选择框都会有是否有效的信息,然后在最后提交的时候需要综合考虑整个表单是否有效。所以要这么做:

1
< form (ngSubmit)="onSubmit()" #heroForm="ngForm" >

首先在建立表单的时候定义一个 #heroForm作为ngForm这个directive的钥匙,当我们设置了这个本地模板之后,这个heroForm就可以接触整个form,然后看是否每个在表单里面的元素都有效。

1
2
< button type="submit" class="btn btn-default"
        [disabled]="!heroForm.form.valid">Submit< /button >

这样做了之后通过 (heroForm.form.valid)来看是否表单里面所有的元素都有效,如果都有效则让提交按钮可以提交,否则就让提交键变暗不能提交。

小结

Angular2 让表单验证变得实时有效。用户可以在输入表单信息的时候就验证输入的信息是否有效,通过css来给用户以反馈,同时也可以通过整张表单是否有效来决定用户是否能够提交。这样一来一方面表单验证可以完全在前端实现,减轻了后端的负担,另一方面在前端给用户实时的输入反馈,增加了表单的能动性。