1. Angular的两种表单API
1.1. 概述
Angular中有两种表单API,分别是模板式表单和响应式表单。模板式表单是通过Angular提供的指令在模板中对表单进行操作的,由于受限于HTML的语法功能,模板式表单只适合一些简单的表单使用场景。而响应式表单是通过组件中自定义数据模型实现对表单的操控。所以更适合复杂表单功能。
Angular表单都需要使用一个数据模型来接收存储表单的数据,而这个数据模型并不是我们自定义的类型,而是需要使用Angular提供的特定类型FormControl
、FormGroup
、FormArray
等来组成。
在模板式表单中上述的数据模型是在HTML中使用Angular指令,由Angular帮我们生成的;响应式表单相反,这些都需要我们在组件中自定义,所以会更加灵活一些。
模板式表单存在于FormModule
模块中,响应式表单存在于ReactiveFormModule
模块中,在使用的时候一定要将对应的模块import到app.module.ts
根模块中。
2. 响应式表单
2.1. 如何建响应式表单数据模型
创建响应式表首先需要创建表单需要用到的数据模型,然后将数据模型使用指令“映射”到模板上。
要使用响应式表单需要将ReactiveFormsModule
模块导入到app.module.ts
模块中:
|
|
定义表单的数据模型需要用到Angular提供的三个类型FormControl
、FormGroup
、FormArray
。
2.1.1. FormControl
FormControl
是表单模型的最小单位,也就相当于数据模型对象中的一个属性,具体到表单中可以保存的是一个input
元素的值和其他元数据。
|
|
FormControl
的构造函数可以传入一个参数,表示默认值,例如绑定在input
元素上时,就是该元素的默认输入。
2.1.2. FormGroup
FormGroup
从语义上看就是代表整个表单,但是也可以保存表单的一部分,它里面可以包含多个FormControl
,可以包含多个FormGroup
。
|
|
FormGroup
的构造函数需要传入一个对象,对象内部可以是FormControl
和FormGroup
。- 在上面的代码中定义了一个演示的注册信息表单,包含了账号和密码两个主要数据,但是密码分成了两个
FormControl
,分别是密码和重复密码,这两个从业务上是同一个数据,所以在这里包含在一个FormGroup
中了。
2.1.3. FormArray
FormArray
本质上和FormGroup
是一样的,只不过FormGroup
中的FormControl
数量是固定的,初始化多少个就是多少个;FormArray
中的FormControl
的数量是可变的,可以动态增减。
|
|
FormArray
的构造函数需要传入一个数组类型,数组中包含一个或多个FormControl
对象。
FormGroup
和FormArray
的另一个不同点就是访问内部FormControl
的方式:
FormGroup
可以通过对象的属性名来访问具体的FormControl
。FormArray
只能通过下标(索引)来访问FormControl
。
2.2. 响应式表单用到的指令
类 | 指令(1) | 指令(2) |
---|---|---|
FormGroup |
formGroup |
formGroupName |
FormControl |
formControl |
formControlName |
FormArray |
formArrayName |
- 指令(1)列的指令都是需要用属性绑定语法使用的,指令(2)列的指令都是不需要属性绑定语法直接使用的。
2.3. 响应式表单的特点
- 响应式表单的指令都是以
form
开头的,模板式表单都是用ng
开头的。 - 响应式表单中不能用模板本地变量。
2.4. 响应式表单的例子
2.4.1. 使用FormGroup
编写表单并且获取提交
|
|
|
|
[FormGroup]
将组件中定义的FormGroup
数据模型绑定到表单上。formControlName
与fromGroupName
分别将数据模型内的FormControl
和FormGroup
对象绑定到相应的元素上面。- 上面有使用属性绑定方式(
[xxx]=ooo
),也有没有使用的,这是因为使用属性绑定的regist
是组件中的一个属性,而表单内部的account
、password
、pwd
、repwd
都是一个字符串(属性名),所以直接赋值了。
页面的输出:
- 可以看到
FormGroup
这个对象中包含很多的属性和方法,如果只关注其表单值的话,可以直接使用其value
属性。
2.4.2. FormArray
绑定动态表单
|
|
- 我们在
regist
数据模型中增加了一个address
的FormArray
的动态表单组合,默认有两个FormControl
对象,也就是默认会有两个空的输入框。 addAddress()
函数是响应页面上的一个按钮,用来向动态表单增加FormControl
,表现在页面上就是每点一次增加一个输入框。- 可以看到在
addAddress()
函数中获取address
的过程有点奇怪,这是因为regist
是一个FormGroup
对象,而我们定义的模型都是从构造函数传入的,所以在FormGroup
对象中必须用它暴露的get()
函数传入属性名来获取。并且需要使用类型转换后才能使用。
|
|
- 在收货地址的input元素上绑定
formControlName
可以看到使用了属性绑定的方式,这里可能会有疑问:不是说包含Name
结尾的指令都是不需要属性绑定的吗? 这个需要看使用的情况了,而这也是FormArray
的特殊的地方,它不能通过属性名去访问成员,只能用下标,所以这里我们传递给formControlName
的值i
并不是一个字符串(属性名),而是一个实实在在的变量,所以需要使用属性绑定的方式传递。
输入页面效果:
- 我们增加两个收货地址输入框,然后在其中三个输入了内容,填写其他的内容后,点击提交可以看到控制台输出了结果:
address
的值是一个数组形式,最后一个没有填的值为null。
2.5. 可单独使用的formControl
指令
fromControl
这个指令比较特殊,它不能出现在formGroup
的内部,否则就会直接报错:
|
|
可以看到如果[formControl]
出现在[formGroup]
的内部就会引发错误:
将[formControl]
拿出去放在[formGroup]
的外面就不会出现错误了。 之前曾在数据绑定一篇说过,插值表达式绑定的是单向绑定的,那么[formControl]
的作用就显而易见了,可以创建一个需要双向绑定的数据和组件关联。
2.6. FormBuilder
Angualr还提供了一个FormBuilder
对象来简化数据模型的创建,可以将之前的代码修改成FormBuilder
创建的:
|
|
页面输出同样的内容:
FormBuilder
对象只有三个方法:- 这三个方法分别对应着三个表单模型的对象。
- 使用
FormBuilder
会减少一些代码,并且方便接下来的表单验证的使用。
3. 模板式表单
3.1. 概述
模板式表单前面说过不用在组件中定义数据模型,Angular会隐式的帮助我们创建底层数据模型,其实对应的就是FormControl
和FormGroup
:
模板表单指令 | 表单模型对象 |
---|---|
NgForm |
FormGroup |
NgModel |
FormControl |
NgModelGroup |
FormGroup |
模板式表单依赖FormsModule
模块的功能,所以首先需要将该模块import到app.module.ts
主模块中:
|
|