Angular 4.x 中有两种表单:
Template-Driven Forms - 模板驱动式表单 (类似于 AngularJS 1.x 中的表单 )? 官方文档:https://v2.angular.cn/docs/ts/latest/guide/forms.html
Reactive Forms (Model-Driven Forms) - 响应式表单? 官方文档: https://v2.angular.cn/docs/ts/latest/guide/reactive-forms.html
Template-Driven Forms vs Reactive Forms
Template-Driven Forms (模板驱动表单) 的特点
使用方便
适用于简单的场景
通过 [(ngModel)] 实现数据双向绑定
最小化组件类的代码
不易于单元测试
异步的,模板驱动表单会委托指令来创建它们的表单控件。 为了消除“检查完后又变化了”的错误,这些指令需要消耗一个以上的变更检测周期来构建整个控件树。 这意味着在从组件类中操纵任何控件之前,我们都必须先等待一个节拍。Reactive Forms (响应式表单) 的特点
比较灵活
适用于复杂的场景
简化了HTML模板的代码,把验证逻辑抽离到组件类中
方便的跟踪表单控件值的变化
易于单元测试
同步的 ,使用响应式表单,我们会在代码中创建整个表单控件树。 我们可以立即更新一个值或者深入到表单中的任意节点,因为所有的控件都始终是可用的。使用?reactive forms 表单前,我们必须在? @NgModule ?中导入? @angular/forms ?库中的? ReactiveFormsModule :
import { ReactiveFormsModule } from ' @angular/forms ' ;
@NgModule({
imports: [
...,
ReactiveFormsModule
],
declarations: [...],
bootstrap: [...]
})
export class AppModule {}
友情提示:若使用 reactive forms,则导入 ReactiveFormsModule;若使用 template-driven 表单,则导入 FormsModule。
FormControl 和 FormGroup
FormControl - 它是一个为单个表单控件提供支持的类,可用于跟踪控件的值和验证状态,此外还提供了一系列公共API。
使用示例:
ngOnInit() {
this .myControl = new FormControl( ' Semlinker ' );
}
FormGroup - 包含是一组 FormControl 实例,可用于跟踪 FormControl 组的值和验证状态,此外也提供了一系列公共API。
使用示例:
ngOnInit() {
this .myGroup = new FormGroup({
name: new FormControl( ' Semlinker ' ),
location: new FormControl( ' China, CN ' )
});
}
现在我们已经创建了? FormControl ?和? FormGroup ?实例,接下来我们来看一下如何使用:
<form novalidate [formGroup]= " myGroup " > Name: <input type= " text " formControlName= " name " > Location: <input type= " text " formControlName= " location " > </form>
注意事项:Template-Driven Forms 中介绍的? ngModel ?和? name="" ?属性,已经被移除了。这是一件好事,让我们的模板更简洁。
上面示例中,我们必须使用? [formGroup] ?绑定我们创建的? myGroup ?对象,除此之外还要使用? formControlName ?指令,绑定我们创建的? FormControl ?控件。此时的表单结构如下:
FormGroup -> ' myGroup '
FormControl -> ' name '
FormControl -> ' location '
User interface
signup.interface.ts
export interface User {
name: string ;
account: {
email: string ;
confirm: string ;
}
}
与之对应的表单结构如下:
FormGroup -> ' user '
FormControl -> ' name '
FormGroup -> ' account '
FormControl -> ' email '
FormControl -> ' confirm '
是的,我们可以创建嵌套的 FormGroup 集合!让我们更新一下组件 (不包含初始数据):
import { Component, OnInit } from ' @angular/core ' ;
import { FormControl, FormGroup } from ' @angular/forms ' ;
@Component({...})
export class SignupFormComponent implements OnInit {
user: FormGroup;
ngOnInit() {
this .user = new FormGroup({
name: new FormControl( '' ),
account: new FormGroup({
email: new FormControl( '' ),
confirm: new FormControl( '' )
})
});
}
}
如果我们想要设置初始数据,我们可以按照上述示例进行设置。通常情况下,我们通过服务端提供的 API 接口来获取表单的初始信息。
绑定FormGroup模型
<form novalidate [formGroup]= " user " >
<label>
<span>Full name</span>
< input
type = " text "
placeholder = " Your full name "
formControlName = " name " >
</label>
<div formGroupName= " account " >
<label>
<span>Email address</span>
< input
type = " email "
placeholder = " Your email address "
formControlName = " email " >
</label>
<label>
<span>Confirm address</span>
< input
type = " email "
placeholder = " Confirm your email address "
formControlName = " confirm " >
</label>
</div>
<button type= " submit " >Sign up</button>
</form>
现在? FormGroup ?与? FormControl ?对象与 DOM 结构的关联信息如下:
// JavaScript APIs
FormGroup -> ' user '
FormControl -> ' name '
FormGroup -> ' account '
FormControl -> ' email '
FormControl -> ' confirm '
// DOM bindings
formGroup -> ' user '
formControlName -> ' name '
formGroupName -> ' account '
formControlName -> ' email '
formControlName -> ' confirm '
当使用模板驱动的表单时,为了获取? f.value ?表单的值,我们需要先执行? #f="ngForm" ?的操作。而对于使用响应式的表单,我们可以通过以下方式,方便的获取表单的值:
{{ user.value | json }} // { name: '', account: { email: '', confirm: '' }}
Reactive submit
跟模板驱动的表单一样,我们可以通过? ngSubmit ?输出属性,处理表单的提交逻辑:
<form novalidate (ngSubmit)= " onSubmit(user) " [formGroup]= " user " > ... </form>
需要注意的是,我们使用? user ?对象作为? onSubmit() ?方法的参数,这使得我们可以获取表单对象的相关信息,具体处理逻辑如下:
export class SignupFormComponent {
user: FormGroup;
onSubmit({ value, valid }: { value: User, valid: boolean }) {
console.log(value, valid);
}
}
上面代码中,我们使用? Object destructuring ?(对象解构) 的方式,从 user ?对象中获取? value ?和? valid ?属性的值。其中? value 的值,就是? user.value ?的值。在实际应用中,我们是不需要传递? user ?参数的:
export class SignupFormComponent {
user: FormGroup;
onSubmit() {
console.log( this .user.value, this .user.valid);
}
}
表单的数据绑定方式和提交逻辑已经介绍完了,是该介绍表单实际应用中,一个重要的环节 — 表单验证。
Reactive error validation
接下来我们来为表单添加验证规则,首先我们需要从? @angular/forms ?中导入? Validators 。具体使用示例如下:
ngOnInit() {
this .user = new FormGroup({
name: new FormControl( '' , [Validators.required, Validators.minLength( 2 )]),
account: new FormGroup({
email: new FormControl( '' , Validators.required),
confirm: new FormControl( '' , Validators.required)
})
});
}
通过以上示例,我们可以看出,如果表单控制包含多种验证规则,可以使用数组声明多种验证规则。若只包含一种验证规则,直接声明就好。通过这种方式,我们就不需要在模板的输入控件中添加? required ?属性。接下来我们来添加表单验证失败时,不允许进行表单提交功能:
<form novalidate (ngSubmit)= " onSubmit(user) " [formGroup]= " user " > ... <button type= " submit " [disabled]= " user.invalid " >Sign up</button> </form>
那么问题来了,我们要如何获取表单控件的验证信息?我们可以使用模板驱动表单中介绍的方式,具体如下:
<form novalidate [formGroup]= " user " >
{{ user.controls.name ?.errors | json }}
</form>
友情提示:?.prop 称为安全导航操作符,用于告诉 Angular prop 的值可能不存在。
此外我们也可以使用? FormGroup ?对象提供的 API,来获取表单控件验证的错误信息:
<form novalidate [formGroup]= " user " >
{{ user. get ( ' name ' ).errors | json }}
</form>
完整代码:
import { Component, OnInit } from ' @angular/core ' ;
import { FormControl, FormGroup, Validators } from ' @angular/forms ' ;
import { User } from ' ./signup.interface ' ;
@Component({
selector: ' signup-form ' ,
template: `
<form novalidate (ngSubmit)= " onSubmit(user) " [formGroup]= " user " >
<label>
<span>Full name</span>
<input type= " text " placeholder= " Your full name " formControlName= " name " >
</label>
<div class = " error " *ngIf= " user.get('name').hasError('required') &&
user. get ( ' name ' ).touched " >
Name is required
</div>
<div class = " error " *ngIf= " user.get('name').hasError('minlength') &&
user. get ( ' name ' ).touched " >
Minimum of 2 characters
</div>
<div formGroupName= " account " >
<label>
<span>Email address</span>
<input type= " email " placeholder= " Your email address " formControlName= " email " >
</label>
< div
class = " error "
*ngIf= " user.get('account').get('email').hasError('required') &&
user. get ( ' account ' ). get ( ' email ' ).touched " >
Email is required
</div>
<label>
<span>Confirm address</span>
<input type= " email " placeholder= " Confirm your email address "
formControlName = " confirm " >
</label>
< div
class = " error "
*ngIf= " user.get('account').get('confirm').hasError('required') &&
user. get ( ' account ' ). get ( ' confirm ' ).touched " >
Confirming email is required
</div>
</div>
<button type= " submit " [disabled]= " user.invalid " >Sign up</button>
</form>
`
})
export class SignupFormComponent implements OnInit {
user: FormGroup;
constructor() {}
ngOnInit() {
this .user = new FormGroup({
name: new FormControl( '' , [Validators.required, Validators.minLength( 2 )]),
account: new FormGroup({
email: new FormControl( '' , Validators.required),
confirm: new FormControl( '' , Validators.required)
})
});
}
onSubmit({ value, valid }: { value: User, valid: boolean }) {
console.log(value, valid);
}
}
功能是实现了,但创建? FormGroup ?对象的方式有点繁琐,Angular 团队也意识到这点,因此为我们提供? FormBuilder ?,来简化上面的操作。
Simplifying with FormBuilder
首先我们需要从? @angular/forms ?中导入? FormBuilder :
import { FormBuilder, FormGroup, Validators } from ' @angular/forms ' ;
export class SignupFormComponent implements OnInit {
user: FormGroup;
constructor( private fb: FormBuilder) {}
...
}
使用FormBuilder:
@Component({...})
export class SignupFormComponent implements OnInit {
user: FormGroup;
constructor( private fb: FormBuilder) {}
ngOnInit() {
this .user = this .fb.group({
name: [ '' , [Validators.required, Validators.minLength( 2 )]],
account: this .fb.group({
email: [ '' , Validators.required],
confirm: [ '' , Validators.required]
})
});
}
onSubmit({ value, valid }: { value: User, valid: boolean }) {
console.log(value, valid);
}
}
?
转自:https://segmentfault测试数据/a/1190000009041192
查看更多关于Angular Reactive Forms -- Model-Driven Forms响应式表单的详细内容...