ng2的表单有三个重要概念
1 FormControl
它封装了表单的inputs项,对外暴露为一个可以操作的FormControl对象。
FormControl 和 FormGroup
1.1 FormControl 代表单一的 input 输入域,它是 angular2/forms 的最小单元,在 FormControl中我们可以访问 input的值,当前的状态,例如是否 valid,是否 dirty,是否有errors。 在组件中创建一个FormControl:
let name = nameControl.value; //kate
nameControl.errors //StringMap(string, any) of errors
nameControl.dirty //false
nameControl.valid //true
于是创建表单的过程就是创建一系列FormControl并添加相应的元数据和逻辑代码的过程。在模板中这样使用一个FormControl:
//form 表单中某一行
这时我们就相当于在表单form的上下文环境中创建了一个新的FormControl对象。由于大部分表单都不止一个输入域,因此,我们需要有一种方式来管理多个 FormControl,同时还需要验证我们的表单,我们可以使用循环来遍历所有的FormControl来实现,但是这样会相当笨重而且复杂,没事,ng2为我们提供了 FormGroup来解决这个问题,下面我们创建一个FormGroup:
name: new FormControl("issliu"),
job: new FormControl('web front end develpoer'),
zip: new FormControl("200000")})
在实现上,FormGroup和FormControl都继承自AbstractControl抽象类,这意味着只要是在AbstraclControl中拥有的属性和函数在这两者之间都可以使用。因此,我们同样可以查看FormGroup的状态,值,变化检测等等。
下面我们来创建一个表单:
在创建表单之前,我们需要引入相应的依赖:
import {FORM_DIRECTIVES} from '@angular/forms';
//FORM_DIRECTIVES 模块中包含了一系列常用的表单指令,只要我们在component的directives中引用了它,就可以在对应的template中使用这些指令
@Component({
selector: "test-form",
directives: [FORM_DIRECTIVES],
template: ``
})
export class DemoForm {
onSubmit(form:any):void{
console.log('form data:', form);
}
}
这样,我们就实现了一个简单的表单。
详细解释:
这里可能会比较迷惑,因为我们并没有写什么指令在form上面,唯一的变化点在我们写了一个#f="ngForm"在form表单上面而已。
其实NgForm已经帮我们完成了这一切,在初始化Angular2表单时,只要我们引入了FORM_DIRECTIVES指令,它会自动检测form标签,如果form不包含
ngNoForm指令或者没有指定formGroup属性,则会自动为form表单应用NgForm指令,具体到细节就是绑定了一个名为ngForm的FormGroup到表单上。
这是一个非常有用的特性。当NgForm指令应用到form表单上后,它使得表单具有了以下两个功能:
(1) 一个名为 ngForm 的 FormGroup
(2) 表单具有一个 ngSubmit 的 @Output 属性
这里的#f="ngForm"表示我们使用了一个名为f的变量引用了这个表单对象,这样我们可以在模板的其他地方访问到它。
我们使用(ngSubmit)="onSubmit(f.value)"语句为表单绑定了一个提交行为,这个 onSubmit 方法是需要我们在对应的组件里面实现的。
这句话的意思是,当点击提交表单时,请调用组件的onSubmit方法。于是我们实现了一个具有一个input标签的可以提交的表单。
2 使用FormBuilder
ng2 为我们隐式构造FormControl 和 FormGroup 是非常简单快捷的。但是,如果我们希望自定义表单的行为怎么办?FormBuilder为我们很好的
解决了这个问题。
从命名上我们就可以看出,FormBuilder是用于帮助我们build表单的。前面我们提到表单是由FormControl和FormGroup组成的,这里我们可以把
FormBuilder理解为生产FormControl和FormGroup的工厂。
下面我们举例如何使用FormBuilder
我们需要引入REACTIVE_FORM_DIRECTIVE,这样才能在表单中使用formGroup和formControl指令。
import {FORM_DIRECTIVE, REACTIVE_FORM_DIRECTIVES,FormBuilder,FormGroup} from '@angular/form';
@Component({
selector: "demo-form-builder",
templateUrl:'demo.html',
directives: [FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES]
})
export class DemoFormBuilder{
myForm: FormGroup;
constructor(private fb: FormBuilder) {
this.myForm = fb.group({
'sku': ['ABC123']
})
}
onSubmit(value){
console.log(value);
}
}
这里我们在构造函数中注入了一个FormBuilder实例,这个FormBuilder实例具有两个主要的方法:
(1) fb.control() //构造一个FormControl实例
(2) fb.group() //构造一个FormGroup实例
上面的例子中,我们构造了一个名为myForm的FormGroup实例,这个group包含一个名为sku的control,默认值为'ABC123'.
现在我们把这个组件引用到模板中:
这里,我们显示的指明了表单的formGroup属性,因此ng2不会为表单自动应用NgForm指令了。
NgForm自动应用的form表单需要符合以下规则:
form:not([ngNoForm]):not([formGroup]), ngForm, [ngForm]
这意味着使用了ngNoForm属性的表单也不会应用NgForm
总结:
如果我们需要隐式的创建FormGroup和FormControl,请使用 ngFrom和ngModel //FORM_DIRECTIVES
如果需要绑定到一个已经存在的FormGroup和FormControl对象,请使用formGroup和formControl //REACTIVE_FORM_DIRECTIVES
3 添加验证:
在上面的DemoFormBuilder 类构造函数中,我们为this.myForm初始化值,现在改为:
this.myForm = fb.group({
'sku': ['', Validators.required]
})
我们已经可以在模板中通过myForm.controls['sku']来访问表单的sku值了,但是这样会在模板中写很多代码,最方便快捷的,是在组件内定义
一个实例变量用来引用名为sku的FormControl,例如
sku:AbstractControl;
this.sku = this.myForm.controls['sku'];
于是在模板中我们可以直接使用sku变量。这样做的好处,是在模板的任何一个地方我们都可以快速访问sku变量,但是如果是一个非常大的表单,
我们则需要在组件中初始化很多这样的变量,使得代码体验比较糟糕。
FormControl和FormGroup都具有hasError方法,
如果我们需要验证一个FormControl,可以这么写
如果需要验证FormGroup的某一项,那就这么写
现在,让我们来自定义一个Validator吧!
function skuValidator(control:FormControl): {[s:string]: boolean} {
if(control.value.test(/^123/)) {
return {invalidSku: true}
}
}
定义Validator有三个关键点:
它接受一个FormControl作为参数,同时返回一个StringMap
现在我们把这个验证项加入myForm:
this.myForm = fb.Group({
'sku': ['', Validator.compose([
validators.required, skuValidator
])]
})
ok,大功告成,现在我们在模板中可以使用:
来检测是否通过Validator了
以上基本是angular表单中的主要功能,下面,我们还可以监听表单的变化:
同样,对于FormControl和FormGroup,都具有一个EventEmitter用来监听变动事件的。
我们通过调用control.valueChanges来获得他们的EventEmitter:
this.sku.valueChanges.subscribe(
(value: string) => {
console.log("sku value changed", value)
}
)
this.myForm.valueChanges.subscribe(
(form: any) => {
console.log("form chnged to:", form)
}
)