响应式表单使用ReactiveFormsModule,而非普通的FormModule,需要在app.module.ts里导入
import { ReactiveFormsModule} from '@angular/forms';
@NgModule({
declarations: [
...
],
exports:[AppComponent],
imports: [
BrowserModule,
...
ReactiveFormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
ReactiveFormsModule包含FormControlDirective、FormGroupDirective、FormControlName、FormArrayName和InternalFormsSharedModule模块里的指令。
- FormControlDirective 描述表单的一个字段
- FormGroupDirective 描述表单分组
- FormControlName 描述表单字段名
- FormArrayName 描述同类型的一组数据的名称,与表单分组无关
下面的例子与前文模板驱动表单类似,不过换了一种实现。自定义校验器也一样,代码不再贴。
模板:
<form [formGroup]="registerForm">
<div>
<label for="userName">用户名:</label>
<!--给input设置一个本地变量,可以读取errors显示错误信息-->
<input type="text" id="userName" name="userName" formControlName="userName" required>
<span *ngIf="formErrors['userName']" class="error">
{{ formErrors['userName'] }}
</span>
</div>
<fieldset aria-required="true" formGroupName="passwordGroup" required>
<label for="password1">密码:</label>
<input type="password" id="password1" name="password1" formControlName="password1" required (keyup)="password1ValChanged()" >
<span *ngIf="formErrors['passwordGroup.password1']" class="error">
{{ formErrors['passwordGroup.password1'] }}
</span>
<label for="password2">重复密码:</label>
<input type="password" id="password2" name="password2" formControlName="password2" required>
<span *ngIf="formErrors['passwordGroup.password2']" class="error">
{{ formErrors['passwordGroup.password2'] }}
</span>
</fieldset>
<div>
<label for="email">邮箱:</label>
<input type="text" id="email" name="email" formControlName="email" required>
<!-- 可以通过表单的onValueChanged事件,读到当前的错误信息,写到指定字段里 -->
<div *ngIf="formErrors.email" class="error">
{{ formErrors.email }}
</div>
</div>
<div>
<label>性别:</label>
<input type="radio" name="sex" value="male" formControlName="sex"> 男
<input type="radio" name="sex" value="female" formControlName="sex" > 女
</div>
<fieldset formGroupName="nameGroup">
<label>姓:</label>
<input type="text" name="firstName" formControlName="firstName" required checked="checked"><br />
<label>名:</label>
<input type="text" name="lastName" formControlName="lastName">
</fieldset>
<button type="button" class="btn btn-default"
[disabled]="!registerForm.valid" (click)="doSubmit(registerForm.value)">注册</button>
</form>
{{registerForm.value|json}}
组件:
import {Component, OnInit, AfterViewInit} from "@angular/core";
import {FormGroup, FormControl, FormBuilder, Validators} from "@angular/forms";
import {repeatPassword} from "../repeat-password.directive";
@Component({
selector: 'app-reactive-form',
templateUrl: './reactive-form.component.html',
styleUrls: ['./reactive-form.component.css']
})
export class ReactiveFormComponent implements OnInit,AfterViewInit {
registerForm: FormGroup;
ngAfterViewInit(): void {
}
constructor(private fb: FormBuilder) {
}
ngOnInit() {
this.registerForm = this.fb.group({
//每一个input都是一个FormControl,key是formControlName,value是构建FormControl的参数,
// 第一个参数是input的默认值,第二个参数是校验器数组
'userName': ['', [Validators.required,Validators.minLength(4)]],
//分组的FormControl,也需要分组构建,key是formGroupName
'passwordGroup':this.fb.group({
'password1': ['', [Validators.required,Validators.minLength(4)]],
}),
'nameGroup':this.fb.group({
'firstName': ['', [Validators.required]],
'lastName': ['', [Validators.required]],
}),
'email':['',[Validators.required,Validators.pattern("[\\w]+?@[\\w]+?\\.[a-z]+?")]],
//默认选中男性
'sex':['male',[Validators.required]],
});
let passwordFormGroup=this.registerForm.controls['passwordGroup'] as FormGroup;
let password1Control=passwordFormGroup.controls['password1'] as FormControl;
passwordFormGroup.addControl('password2',new FormControl('',[Validators.required,repeatPassword(password1Control)]));
this.registerForm.valueChanges.subscribe(data => this.onValueChanged(data));
}
//存储错误信息
formErrors = {
'email': '',
'userName': '',
'passwordGroup.password1':'',
'passwordGroup.password2':'',
'sex':''
};
//错误对应的提示
validationMessages = {
'email': {
'required': '邮箱必须填写.',
'pattern': '邮箱格式不对',
},
'userName': {
'required': '用户名必填.',
'minlength': '用户名太短',
},
'passwordGroup.password1':{
'required': '请输入密码',
'minlength': '密码太短',
},
'passwordGroup.password2':{
'required': '请重复输入密码',
'minlength': '密码太短',
'passwordNEQ':'两次输入密码不同',
'password1InValid':''
},
'sex':{
'required':'性别必填'
}
};
/**
* 第一个密码改变时,清空第二个密码框
*/
password1ValChanged() {
(this.registerForm.controls['passwordGroup'] as FormGroup).controls['password2'].reset();
}
/**
* 表单值改变时,重新校验
* @param data
*/
onValueChanged(data) {
for (const field in this.formErrors) {
this.formErrors[field] = '';
//取到表单字段
const control = this.registerForm.get(field);
//表单字段已修改或无效
if (control && control.dirty && !control.valid) {
//取出对应字段可能的错误信息
const messages = this.validationMessages[field];
//从errors里取出错误类型,再拼上该错误对应的信息
for (const key in control.errors) {
this.formErrors[field] += messages[key] + '';
}
}
}
}
doSubmit(obj: any) {
//表单提交
console.log(JSON.stringify(obj));
}
}