看了两天官网文档表单和表单验证后,才发现以前本身仅仅经过特定的 Angular CSS 类去控制去反馈用户的操做,如ng-touched等等是多么的入门,虽说这已经够用了,可是以前写的都是在视图层上写逻辑,各类判断,致使后期维护有点困难。趁有时间记录一下这两天看了资料的一些心得。javascript
二者均可以经过表单绑定获取整个表单的值和是否合法eg: myForm.value, myForm.valid等等html
使用方便java
适用于简单的场景react
经过 [(ngModel)] 实现数据双向绑定json
最小化组件类的代码segmentfault
不易于单元测试数组
导入FormModule函数
表单绑定经过 #myForm="ngForm"单元测试
比较灵活测试
适用于复杂的场景
简化了HTML模板的代码,把验证逻辑抽离到组件类中
方便的跟踪表单控件值的变化
易于单元测试
导入ReactiveFormsModule
表单绑定经过 [formGroup]="myForm"
import { Component, AfterViewChecked, ViewChild } from '@angular/core'; import { NgForm } from '@angular/forms'; import { Hero } from '../shared/hero'; @Component({ selector: 'hero-form-template2', templateUrl: './hero-form-template2.component.html' }) export class HeroFormTemplate2Component implements AfterViewChecked { powers = ['Really Smart', 'Super Flexible', 'Weather Changer']; hero = new Hero(18, 'Dr. WhatIsHisWayTooLongName', this.powers[0], 'Dr. What'); submitted = false; onSubmit() { this.submitted = true; } // Reset the form with a new hero AND restore 'pristine' class state // by toggling 'active' flag which causes the form // to be removed/re-added in a tick via NgIf // TODO: Workaround until NgForm has a reset method (#6822) active = true; addHero() { this.hero = new Hero(42, '', ''); this.active = false; setTimeout(() => this.active = true, 0); } /*组件手动记录表单对象*/ heroForm: NgForm; /*heroFrom变量是Angular从模板衍生出来的控制模型的引用。 咱们利用@ViewChild来告诉Angular注入这个模型到组件类的currentForm*/ @ViewChild('heroForm') currentForm: NgForm; /*监听视图中表格变化,第一次渲染表格对象有多少个值就跑多少次*/ ngAfterViewChecked() { this.formChanged(); } formChanged() { /*检测是否发生变化*/ if (this.currentForm === this.heroForm) { return; } this.heroForm = this.currentForm; if (this.heroForm) { /*heroForm获得的是FormModule对象,该对象继承于AbstractControl抽象类,里面包含touched等*/ /*valueChanges返回一个Observal,用于获取视图返回的可观察对象*/ this.heroForm.valueChanges .subscribe(data => this.onValueChanged(data)); } } onValueChanged(data?: any) { if (!this.heroForm) { return; } const form = this.heroForm.form; for (const field in this.formErrors) { // clear previous error message (if any) this.formErrors[field] = ''; /*heroForm.form.get('name')获取对应表单里的键值*/ const control = form.get(field); if (control && control.dirty && !control.valid) { const messages = this.validationMessages[field]; for (const key in control.errors) { this.formErrors[field] += messages[key] + ' '; } } } } formErrors = { 'name': '', 'power': '' }; validationMessages = { 'name': { 'required': 'Name is required.', 'minlength': 'Name must be at least 4 characters long.', 'maxlength': 'Name cannot be more than 24 characters long.', 'forbiddenName': 'Someone named "Bob" cannot be a hero.' }, 'power': { 'required': 'Power is required.' } }; }
import { Component, OnInit } from '@angular/core'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { Hero } from '../shared/hero'; import { forbiddenNameValidator } from '../shared/forbidden-name.directive'; @Component({ ... }) export class HeroFormReactiveComponent implements OnInit { ... /**/ heroForm: FormGroup; constructor(private fb: FormBuilder) { } ngOnInit(): void { /*初始化表单对象*/ this.buildForm(); } buildForm(): void { /*FormBuilder.group是一个用来建立FormGroup的工厂方法*/ this.heroForm = this.fb.group({ /*这里的值接受两个参数,第一个是值,第二个是检验器,若是有多个检验器必须用数组表示*/ 'name': [this.hero.name, [ Validators.required, Validators.minLength(4), Validators.maxLength(24), forbiddenNameValidator(/bob/i) ] ], 'alterEgo': [this.hero.alterEgo], 'power': [this.hero.power, Validators.required] }); this.heroForm.valueChanges .subscribe(data => this.onValueChanged(data)); this.onValueChanged(); // (re)set validation messages now } /*与驱动表单上面代码相似*/ ... }
表组经过ngModelGroup去绑定
<form #templateForm="ngForm" novalidate (ngSubmit)="onSubmit(templateForm)"> <span>Name</span> <label class="user_name"> <input #uName="ngModel" type="text" name="user_name" [(ngModel)]="user.name" required> </label> <div ngModelGroup="account"> <span>QQ</span> <label class="user_name"> <input type="text" name="user_qq" [(ngModel)]="user.account.qq" required> </label> <span>Password</span> <label class="user_name"> <input type="password" name="user_password" [(ngModel)]="user.account.password" required> </label> </div> <button style="width: 200px; height: 36px; border: 1px solid #ccc; margin-top: 20px;" [disabled]="templateForm.invalid">提交</button> </form>
表组经过
formGroupName="address"
-------------------------------------------------------------------
html:
这里注意要经过表单.get的方法获取FormModel的东西,reactiveForm.get('age').touched 或者reactive.controls.age?.touched
<form [formGroup]="reactiveForm" (ngSubmit)="onSubmit()"> <div class="form-group"> <label for="name">Name</label> <input type="text" id="name" class="form-control" formControlName="name"> </div> <div class="form-group" [ngClass]="{'has-error': (reactiveForm.get('age').touched || reactiveForm.get('age').dirty) && !reactiveForm.get('age').valid }"> <label class="col-md-2 control-label" for="ageId">年龄</label> <div class="col-md-8"> <input class="form-control" id="ageId" type="number" placeholder="请输入年龄" formControlName="age" /> <span class="help-block" *ngIf="(reactiveForm.get('age').touched || reactiveForm.get('age').dirty) && reactiveForm.get('age').errors"> <span *ngIf="reactiveForm.get('age').errors.range"> 输入年龄不合法 </span> </span> </div> </div> <p>{{reactiveForm.value | json}}</p> </form>
ts:
... @Component({ ... }) export class ReactiveFormComponent implements OnInit { user: any = { name: '', age: 0 }; reactiveForm: FormGroup; constructor(public fb: FormBuilder) { } ngOnInit() { this.buildForm(); } buildForm(): void { this.reactiveForm = this.fb.group({ 'name': [this.user.name, Validators.required], 'age': [this.user.age, this.ageValidator] //自定义验证方法使用 }); // this.reactiveForm.valueChanges // .subscribe(data => { console.log(data) }); } /*有参数的验证器工厂函数写法,这里FormControl和下面的AbstractControl都是指向抽象类的属性和方法都是为了获取表单属性*/ private ageValidatorParams(min: number, max: number) { return (c: FormControl): { [key: string]: any } | null => { let age = c.value; if (age && (isNaN(age) || age < min || age > max)) { return { 'range': true, min: min, max: max }; } return null; } } /*必须返回一个对象,该对象为错误信息的值*/ private ageValidator(c: AbstractControl): { [key: string]: any } | null { let age = c.value; if (age && (isNaN(age) || age < 20 || age > 120)) { return { 'range': true, min: 20, max: 120 }; } return null; } }
参考文献:
二、模板驱动式表单
三、响应式表单