在Angular中存在两种表单处理方式:

模版驱动式表单

表单的数据模型是通过组件模版中的相关指令来定义的。由于使用这种方式定义表单的数据模型时,我们会受限于HTML的语法,所以,模版驱动方式只适用于一些简单的场景。

响应式表单

使用响应式表单时,通过编写TypeScript代码而不是Html代码来创建一个底层的数据模型,在定义好这个模型以后,使用一些特定的指令,将模版上的HTML元素与底层的数据模型连接在一起。

Angular表单API

不管是哪种表单,都有一个对应的数据模型来存储表单的数据。在模版式表单中,数据模型是由angular基于组件模版中的指令隐式创建的。而在响应式表单中,你通过编码明确的创建数据模型然后将模版上的html元素与底层的数据模型连接起来。

数据模型并不是一个任意的对象,它是一个由angular/forms模块中的一些特定的类,如FormControl,FormGroup,FormArray等组成的。在模版式表单中,是不能直接访问这些类的。

响应式表单并不会替你生成HTML,模版仍然需要你自己来编写。

模版式表单

使用模版式表单时,需要引入FormsModule,只能使用指令定义数据模型。这些指令都来自于FormsModule模块。

1
2
3
4
5
6
7
import {FormsModule} from '@angular/forms';
@NgModule({
imports:[
FormsModule
]
})

##模版式表单的指令:

NgForm

ngForm指令代表整个表单,在Angular应用里ngForm指令会自动的添加到每一个<form>表单上。ngForm会隐式的创建一个FormGroup类的实例,这个类用来代表表单的数据模型,并且存储表单的数据。

1
2
3
4
<form>
<div>昵称:<input type="text"></div>
<button type="submit">提交</button>
</form>
  • Angular应用会自动为<form>元素添加ngForm指令。

  • ngForm会自动拦截表单的提交事件,阻止表单的提交。Angular用一个自定义的ngSubmit事件来代替他。

    1
    2
    3
    4
    <form (ngSubmit)="test()">
    <div>昵称:<input type="text"></div>
    <button type="submit">提交</button>
    </form>
  • ngForm会自动发现添加了ngModel的子元素,并将这些子元素的值会被添加到表单数据模型中。

  • ngForm指令可以也可以在其他元素上使用,比如<div ngForm>,这和写一个<form>效果是一样的。

  • 如果你不希望Angular来自动处理你的表单,你可以在<form>元素上明确的添加ngNoForm指令:

    1
    <form ngNoForm>...</form>

    添加了ngNoForm指令,angular将不再接管<form>表单的处理。

  • ngForm指令创建的对象可以被一个模版本地变量引用,以便在模版中访问ngForm对象的实例。

    value是一个JS对象,保存着form表单中所有字段的值。

    1
    2
    3
    4
    5
    6
    7
    <form #myForm="ngForm">
    <div>昵称:<input ngModel name="nickName" type="text"></div>
    </form>
    <div>{{myForm.value}}</div>
    //昵称:Tom
    //{nickName: Tom}

###NgModel

ngModel指令代表表单中的一个字段,ngModel指令会隐式的创建一个FormControl类的实例,来代表字段的数据模型,并用这个FormControl的对象来存储字段的值。

1
2
3
4
<form #myForm="ngForm">
<div>昵称:<input ngModel name="nickName" type="text"></div>
</form>
<div>{{myForm.value}}</div>
  • <form>标签或者标记了ngForm指令的HTML元素内,使用ngModel指令时,不需要像双向绑定那样用[()]扩起来,应为这里不是双向绑定,而是表单字段的指令;也不需要绑定到组件的属性上。

    但是ngModel指令需要为添加它的HTML元素指定name的值。如果不指定name属性的值,angular不知道以什么名字把这个字段的值添加到数据模型中。

  • ngForm类似,ngModel指令创建的对象也可以被模版本地变量引用,并通过模版本地变量来访问这个对象的值。

    1
    2
    3
    4
    5
    <form #myForm="ngForm">
    <div>昵称:<input #myNickName="ngModel" ngModel name="nickName" type="text"></div>
    </form>
    <div>{{myForm.value}}</div>
    <div>昵称的值是:{{myNickName.value}}</div>

###NgModelGroup

ngModelGroup指令代表表单的一部分,他将表单的一部分组织在一起,形成更清晰的层级关系。

ngForm类似,ngModelGroup也会创建一个FormGroup类型的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<form #myForm="ngForm">
<div>昵称:<input ngModel name="nickName" type="text"></div>
<div ngModelGroup="passwordInfo">
<div>密码:<input type="password"></div>
<div>确认密码:<input type="password"></div>
</div>
</form>
<div>{{myForm.value}}</div>
//
{
"nickName":"",
"password":{}
}

#响应式表单

创建响应式表单与模版式表单不同,需要两步:

  • 首先通过编码创建一个数据模型。
  • 然后使用指令将模版中的HTML元素连接到数据模型上。

使用响应式表单时,需要引入ReactiveFormsModule模块,响应式表单的指令都来自ReactiveFormsModule模块。

1
2
3
4
5
6
7
import {ReactiveFormsModule} from '@angular/forms';
@NgModule({
imports:[
ReactiveFormsModule
]
})

数据模型

数据模型是指一个用来保存表单数据的数据结构,他由定义在FormsModule模块中的三个类组成:

  • FormControl:

    他是构成表单的基本单位,通常情况下用来表示一个<input>元素,也可以用来表示更复杂的UI组件。

    FormControl类的对象保存着与其关联的HTML元素当前的值,元素是否被修改过,以及校验状态等信息。

    1
    2
    // ReactiveForm.component.ts
    private nickname = new FormControl('tom');

    FormControl接收的参数用来制定初始值。

    模版式表单中,ngModel指令默认会为其附着的元素创建一个FormControl类的对象。

  • FormGroup:

    FormGroup类代表表单的一部分,也可以表示整个表单,他是多个FormControl的集合。

    FormGroup将多个FormControl的状态和值聚合在一起。如果其中一个FormControl的值是无效的,也就是不符合校验规则,那么整个FormGroup都是无效的。

    1
    2
    3
    4
    5
    // ReactiveForm.component.ts
    private passwordInfo = new FormGroup({
    password: new FormControl(),
    passwordConfirm: new FormControl()
    })

    FormGroup的构造函数需要传入一个对象{},这个对象里是FormControl

  • FormArray:

    FormArrayFormGroup是类似的,但是有一个额外的属性,长度,因为FormArray是一个数组。

    FormGroup用来代表整个表单,或者一个表单中固定的子集。

    FormArray用来代表一个可以增长的集合。比如,一个表单中的邮箱字段,一个用户可以拥有多个邮箱。

    1
    2
    3
    4
    5
    6
    // ReactiveForm.component.ts
    private emails = new FormArray([
    new FormControl(),
    new FormControl(),
    new FormControl()
    ])

    FormArray中的FormControl没有key,而FormGroup中的FormControl是要有一个key。

编写一个完整的表单数据模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ReactiveForm.component.ts
export class {
private formModel: FormGroup;
constructor(){
this.formModel = new FormGroup({
nickname: new FormControl(),
mobile: new FormControl(),
emails: new FormArray([
new FormControl()
]),
passwordInfo: new FormGroup({
password: new FormControl(),
passwordConfirm: new FormControl()
})
})
}
}

##响应式表单的指令:

在控制器中已经构建好了数据模型,在模版中通过指令,将表单元素和数据模型连接起来。

响应式表单使用和模版式表单完全不同的指令,这些指令来自于ReactiveFormsModule模块。

响应式表单的指令分成两种,一种是需要使用属性绑定语法[xxx]="xxx"的指令,另一种是不需要属性绑定语法的指令。

响应式表单的所有指令,都是以form开头的;而模版式表单的所有指令,都是以ng开头的。

###使用属性绑定的指令:

formGroupformControl

这种指令使用时必须使用属性绑定语法,就是说使用这些指令时,需要使用属性绑定的语法,用[]扩起来。

属性绑定的指令,必须绑定到组件中的某个属性上,比如:

1
2
3
4
5
<form [formGroup]="formModel"></form>
export class ReactiveForm {
private formModel: FormGroup;
}

指令的名字很简单,和类名一样,首字母小写。

FormArray类没有属性绑定方式的指令,因为它是数组,没有key来对应。

###不使用属性绑定的指令:

formGroupNameformControlNameformArrayName

这些以Name结尾的指令可以使用属性的名字来连接DOM元素和数据模型,它们不需要使用属性绑定语法。

formGroup通过属性绑定将表单绑定到组件中的formModel属性上,nickname不是组件的属性,只是数据模型中的一个字段的key,因此不能用属性绑定[formControl]="nickname",否则会报错。

1
2
3
4
5
6
7
8
9
10
11
12
<form [formGroup]="formModel">
<input formControlName="nickname">
</form>
export class ReactiveForm {
private formModel: FormGroup;
constructor(){
this.formModel = new FormGroup({
nickname: new FormControl()
})
}
}

与模版式表单不同,响应式表单的指令都是不可引用的,不能像模版式表单那样用模版本地变量来引用。

而在模版式表单中,不能在控制器中直接访问FormGroupFormControl这些类。

Angular是故意这么做,目的就是区分模版式表单和响应式表单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<form [formGroup]="formModel" (submit)="createUser()">
<div>昵称:<input formControlName="nickname" type="text"></div>
<div>邮箱:
<ul formArrayName="emails">
<li *ngFor="let email of formModel.get('emails').controls; let i = index">
<input [formControlName]="i">
</li>
</ul>
</div>
<大专栏  Angular4——7.表单处理span class="name">div>手机号:<input formControlName="mobile" type="text"></div>
<div formGroupName="passwordInfo">
<div>密码:<input formControlName="password" type="password"></div>
<div>确认密码:<input formControlName="passwordConfirm" type="password"></div>
</div>
<button type="submit">注册</button>
</form>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export class ReactiveForm {
private formModel: FormGroup;
constructor(){
this.formModel = new FormGroup({
nickname: new FormControl(),
mobile: new FormControl(),
emails: new FormArray([
new FormControl()
]),
passwordInfo: new FormGroup({
password: new FormControl(),
passwordConfirm: new FormControl()
})
})
}
createUser(){
}
}

注意

  • 处理submit事件:
    • 模版式表单中提供了ngSubmit事件来处理表单提交:<form #myForm="ngForm"(ngSubmit)="createUser(myForm)"。在模版式表单中,通过把保存表单数据的模版本地变量传给控制器来获取表单数据。
    • 响应式表单中直接处理原生的submit事件:<form (submit)="createUser()">。并且,不需要传模版本地变量,因为响应式表单中本身就不能引用模版本地变量,表单的数据模型是在组件控制器中定义的,在控制器中就能直接拿到数据。

FormBuilder

FormBuilder是Angular提供的一个工具类,它并没有提供新的功能,只是简化构建响应式表单数据结构的语法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
export class ReactiveForm{
private formModel: FormGroup;
private fb: FormBuilder = new FormBuilder();
constructor(){
this.formModel = this.fb.group({
nickname: [''],
mobile: [''],
emails: this.fb.array([
[''],
]),
passwordInfo: this.fb.group({
password: [''],
passwordConfirm: [''],
})
})
}
}
  • new FormGroup({}) = new FormBuilder().group({});
  • new FormControl() = new FormBuilder().[''];
  • new FormArray([]) = new FormBuilder().array([]);

此外,FormBuilder还可以对表单模型进行其他的配置,比如校验表单。

表单校验

Angular校验器

angular校验器有两种,一个是自己定义的校验器,另一个是angular预定义好的校验器:

Angular预定义的校验器

angular提供的校验器都在一个叫Validators的类中,这个类有很多方法,比如:

  • Validators.required必填的校验器
  • Validators.minLength()最小长度的校验器
  • Validators.maxLength()最到长度的校验器

等等。

当有了定义好的校验器,包括angular提供的或者自己定义的,我们就可以配置数据模型来使用校验器。

将校验器作为参数传到表单模型的构造函数中就能进行校验:

1
2
3
this.formModel = this.fb.group({
nickname:['',Validators.required]
})

这个意思是nickname这个字段是必填的。

也可以同时传入多个校验器,那么这个参数就是一个数组:

1
2
3
this.formModel = this.fb.group({
nickname: ['', [Validators.required, Validators.minLength(5)]
})

nickname这个字段是必填的,同时最小长度是5。

###自定义校验器

自定义的校验器实际上就是一个自定义的方法,该方法要求需要传入一个AbstractControl类的参数,并且该方法的返回值必须是keystring类型的对象:

1
2
3
xxx(param: AbstractControl):{[key:string]: any}{
return null;
}
  • AbstractControl类是FormGroupFormControlFormArray的父类,因此,自定义校验器的参数可以是这三个类中的任意一种。

当需要更复杂的校验规则时,angular提供的校验器可能无法满足需求,我们可以通过自定义的检验器来实现。

响应式表单校验

1
2
3
4
5
6
7
class FormBuilder {
group(controlsConfig: {[key: string]: any}, extra: {[key: string]: any}|null = null): FormGroup
control(formState: Object, validator?: ValidatorFn|ValidatorFn[]|null, asyncValidator?: AsyncValidatorFn|AsyncValidatorFn[]|null): FormControl
array(controlsConfig: any[], validator?: ValidatorFn|null, asyncValidator?: AsyncValidatorFn|null): FormArray
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import {FormControl, FormGroup} from "@angular/forms";
import {Observable} from "rxjs";
export function mobileValidator(mobile: FormControl):any {
let value = (mobile.value || '') + '';
var myreg = /^(((13[0-9]{1})|(15[0-9]{1})|(18[0-9]{1}))+d{8})$/;
let valid = myreg.test(value);
console.log('mobile是否校验通过:'+valid);
return valid ? null : {mobile:true};
}
export function mobileAsyncValidator(mobile: FormControl):any {
let value = (mobile.value || '') + '';
var myreg = /^(((13[0-9]{1})|(15[0-9]{1})|(18[0-9]{1}))+d{8})$/;
let valid = myreg.test(value);
console.log('mobile是否校验通过:'+valid);
return Observable.of(valid ? null : {mobile:true}).delay(5000);
}
export function passwordValidator(info: FormGroup):any {
let password:FormControl = info.get('password') as FormControl;
let pConfirm:FormControl = info.get('passwordConfirm') as FormControl;
if(password != null && pConfirm != null) {
let valid:boolean = password.value === pConfirm.value;
console.log('password是否校验通过:'+valid);
return valid ? null : {password: {description:'密码和确认密码不匹配'}};
}
return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
export class ReactivedFormComponent implements OnInit {
private formModel:FormGroup;
private fb:FormBuilder = new FormBuilder();
constructor() {
this.formModel = this.fb.group({
nickname: ['xxxx', [Validators.required, Validators.minLength(6)]],
emails: this.fb.array([
['']
]),
mobile: ['', mobileValidator, mobileAsyncValidator],
passwordInfo: this.fb.group({
password: ['', Validators.required],
passwordConfirm: ['']
}, {validator: passwordValidator})
})
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<form [formGroup]="formModel" (submit)="createUser()" >
<div>昵称:<input [class.inputHasError]="formModel.get('nickname').invalid" formControlName="nickname" type="text" pattern="[a-zA-A0-9]+"></div>
<div [hidden]="!formModel.hasError('required','nickname')">
昵称是必填项
</div>
<div [hidden]="!formModel.hasError('minlength','nickname')">
昵称的最小长度是6
</div>
<div>邮箱:
<ul formArrayName="emails">
<li *ngFor="let email of formModel.get('emails').controls;let i = index">
<input [formControlName]="i">
</li>
</ul>
<button type="button" (click)="addEmail()">增加Email</button>
</div>
<div>手机号:<input formControlName="mobile" type="number"></div>
<div [hidden]="formModel.get('mobile').valid || formModel.get('mobile').pristine">
<div [hidden]="!formModel.hasError('mobile','mobile')">
手机号不合法
</div>
</div>
<div [hidden]="!formModel.get('mobile').pending">
正在校验手机号合法性
</div>
<div formGroupName="passwordInfo">
<div>密码:<input formControlName="password" type="password"></div>
<div [hidden]="formModel.get('passwordInfo.password').valid || formModel.get('passwordInfo.password').untouched">
<div [hidden]="!formModel.hasError('required','passwordInfo.password')">
密码是必填项
</div>
</div>
<div>确认密码:<input formControlName="passwordConfirm" type="password"></div>
</div>
<div [hidden]="!formModel.hasError('password','passwordInfo')">
{{formModel.getError('password', 'passwordInfo')?.description}}
</div>
<button type="submit">注册</button>
</form>
<div>
{{formModel.status}}
</div>
  • FormGroup.hasError('校验器的key','被校验字段的key')
  • FormGroup.getError('校验器的key','被校验字段的key')
  • FormGroup.get('').

Angular4——7.表单处理的更多相关文章

  1. Angular4笔记——表单状态相关的属性

    表单状态字段(FromControl)touched和untouched用来判断用户是否访问过一个字段(也就是这个字段是否获取过焦点,如果获取过焦点,touched是true,untouched是fa ...

  2. angular4 form表单验证

    <!-- novalidate 清除浏览器默认的校验行为 --> <form [formGroup]="formModel" (ngSubmit)="o ...

  3. angular4 自定义表单验证Validator

    表单的验证条件有时候满足不了需求就可以自定义验证 唯一要求返回是ValidatorFn export interface ValidatorFn{ (c:AbstractControl):Valida ...

  4. angular4 自定义表单组件

    自定义表单组件分为单值组件和多值组件. 单值组件:input/select/radio/textarea 多值组件:checkbox/tree组件 条件: 1.必须实现ControlValueAcce ...

  5. angular4 Form表单相关

    ng4中,有两种方式去声明一个表单 一:Template-Driven Forms - 模板驱动式表单   [引入FormsModule] 1.ngForm赋值 [可以方便的获取表单的值] <f ...

  6. Angular4.x 创建组件|绑定数据|绑定属性|数据循环|条件判断|事件|表单处理|双向数据绑定

    Angular4.x 创建组件|绑定数据|绑定属性|数据循环|条件判断|事件|表单处理|双向数据绑定 创建 angular 组件 https://github.com/angular/angular- ...

  7. Angular2 表单验证相关

    angular4响应式表单与校验http://blog.csdn.net/xiagh/article/details/78360845?locationNum=10&fps=1 How to ...

  8. angularcli 第五篇(输入框、表单处理)

    本文参考:Angular4 表单快速入门 注:涉及input表单时要在AppComponent中引入 FormsModule模块:     import{ FormsModule } from '@a ...

  9. ASP.NET Aries 入门开发教程9:业务表单的开发

    前言: 经过前面那么多篇的列表的介绍,终于到了大伙期待的表单开发了. 也是本系列的最后一篇文章了! 1:表单页面的权限设置与继承 对于表单页面,权限的设置有两种: 1:你可以选择添加菜单(设置为不显示 ...

随机推荐

  1. a标签的一些特殊使用

    <a href="tel:10086">10086</a> //点击后直接拨打10086  <a href="mailto:c1586@qq ...

  2. 20190221 beautiful soup 入门

    beautiful soup 入门 Beautiful Soup 是 python 的一个库,最主要的功能是从网页抓取数据. Beautiful Soup 自动将输入文档转换为 Unicode 编码, ...

  3. 表查询语句及使用-连表(inner join-left join)-子查询

    一.表的基本查询语句及方法 from. where. group by(分组).having(分组后的筛选).distinct(去重).order by(排序).  limit(限制) 1.单表查询: ...

  4. ubuntu linux下解决“no java virtual machine was found after searching the following locations:”的方法

    现象:删除旧的jdk,安装新的jdk之后,打开eclipse报错: A Java Runtime Environment (JRE) or Java Development Kit (JDK)must ...

  5. python中的变量引用小结

    python的变量都可以看成是内存中某个对象的引用.(变量指向该内存地址存储的值) 1.python中的可更改对象和不可更改对象 python中的对象可以分为可更改(mutable)对象与不可更改(i ...

  6. IOS之Core Foundation框架和Cocoa Foundation框架的区别(转)

    Core Foundation框架 (CoreFoundation.framework) 是一组C语言接口,它们为iOS应用程序提供基本数据管理和服务功能.下面列举该框架支持进行管理的数据以及可提供的 ...

  7. A component required a bean named xxx that could not be found. Action: Consider defining

    0 环境 系统:win10 1 正文 https://stackoverflow.com/questions/44474367/field-in-com-xxx-required-a-bean-of- ...

  8. javaScript 面向对象 触发夫级构造函数

    class Person{ constructor(name,age){ //直接写属性 this.name=name; this.age=age; console.log('a'); } showN ...

  9. 路由器协议----IGP、EGP、RIP、OSPF、BGP、MPLS

    1.路由控制的定义 <br>1.1.IP地址与路由控制   file:///var/folders/pz/cy11_lpd5rqfs66s778032580000gn/T/51.html ...

  10. 堆排Heap Sort

    1. #define LeftChild(i) (2*(i)+1) void PercDown(vector<int>&num, int i, int n) { int child ...