先說"響應式表單"的用法
新建的文件,用來寫自定義驗證器
import { AbstractControl } from "@angular/forms";
//control是我們要驗證的表單控件, export function beginWith(control: AbstractControl) { const result = /^13/.test(control.value); return result ? null : {'beginWith': {value: control.value}};
}
上面返回語句中的"beginWith"是我們自定義的一個錯誤類型名,如果被驗證的控件不滿足我們自定義的這個驗證規則, 控件元素的實例對象
在組件類文件中
//要引用自定義驗證器的文件,並且引入表單相關類
import { beginWith } from '....../上面定義驗證器函數的文件名';
import { FormGroup, FormControl, Validators } from '@angular/forms'; //Validators是ng自帶的內置驗證器類,里面有一些對html5驗證規則的封裝方法.
...
在模板中
<form [formGroup]="heroForm" novalidate> <input id="phone" name="phone" formControlName="phone" autocomplete="off"> <div *ngIf="phone.invalid && (phone.dirty || phone.touched)" class="alert alert-danger"> <div *ngIf="phone.errors.required"> 必填 </div> <div *ngIf="phone.errors.minlength"> 至少4個字母 </div> <div *ngIf="phone.errors.beginWith"> 必須是13 </div> </div> </form>
上面定義的驗證器中的正則表達式是寫死了, 可以改造一下, (這也是官方文檔的例子https://angular.cn/guide/form-validation#adding-to-reactive-forms):
export function beginWith(regExp: RegExp): ValidatorFn { return (control: AbstractControl): {[key: string]: any} | null => { const isMatch = regExp.test(control.value); return isMatch ? null : {'beginWith': {value: control.value}}; }; }
之前的寫法其實就是直接export(導出)一個驗證器函數(ValidatorFn),這個修改的寫法就是個工廠函數,即調用它時要傳進來一個正則表達式,它會返回一個使用這個正則表達式創建的驗證器函數(ValidatorFn),ValidatorFn是一個函數接口,參數為一個表單控件對象,返回值是一個錯誤信息對象或null(如果沒有錯誤).
相應的組件類中的創建表單對象的代碼也要改下:
this.heroForm = new FormGroup({ name: new FormControl('蜘蛛俠', [Validators.required, Validators.minLength(4), beginWith(/^13/)]) });
"響應式表單"的實現方式需要在表單組件類所在模塊( 可能是根模塊 )引入ReactiveFormsModule,
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
接下來看下模板驅動表單的實現方式:
用自定義指令的方式將上面寫自定義驗證器進行包裝 : 官方的原文:必須創建一個指令,它會包裝這個驗證器函數。我們使用 NG_VALIDATORS 令牌來把它作為驗證器提供出來. https://angular.cn/guide/form-validation#adding-to-reactive-forms
// 自定義驗證器 import { ValidatorFn, AbstractControl, NG_VALIDATORS, Validator } from "@angular/forms"; import { Directive, Input } from "@angular/core"; export function beginWith(nameRe: RegExp): ValidatorFn { return (control: AbstractControl): { [key: string]: any } | null => { const result = nameRe.test(control.value); return result ? null : { 'beginWith': { value: control.value } }; }; } @Directive({ selector: '[phone]', //在模板中的屬性指令就要寫成 phone="..." providers: [{ provide: NG_VALIDATORS, //這里我還沒太搞弄,參考下官方文檔吧 useExisting: ForbiddenValidatorDirective, //這個屬性的意思官方文檔上寫的我也沒太懂,總之呢要和下面類名一致 multi: true //官方文檔是說要想讓一個控件同時支持多個驗證器就要寫 multi: true }] }) export class ForbiddenValidatorDirective implements Validator { @Input('phone') regExp_str: string; //通過模板上的屬性指令取到的字符串值 validate(control: AbstractControl): { [key: string]: any } | null { const regExp = new RegExp(this.regExp_str, 'i'); //創建正則表達式對象 const validatFn = beginWith(regExp); //直接調用上面定義的工廠函數, 它返回的是一個驗證器函數 return this.regExp_str ? validatFn(control) : null; //然后調用這個驗證器, control就是當前指令所屬的表單控件 } }
修改組件類的代碼:
... export class MyApp { ... //"模板驅動式表單"的數據就直接用類屬性定義 hero = { phone: '13333333333' }; constructor() { // this.heroForm = new FormGroup({ // name: new FormControl('王寧', [Validators.required, Validators.minLength(4), beginWith(/^123/)]) // }); } //get name() { return this.heroForm.get('name'); } }
然后 修改一下模板
<form> <input id="phone" name="phone" required minlength="11" phone="^13" [(ngModel)]="hero.phone" #phone="ngModel" autocomplete="off"> <div *ngIf="phone.invalid && (phone.dirty || phone.touched)" > <div *ngIf="phone.errors.required"> 必填 </div> <div *ngIf="phone.errors.minlength"> 至少11位數 </div> <div *ngIf="phone.errors.beginWith"> 手機號必須是13開頭 </div> </div> </form>
"模板驅動式表單"中, 需要定義模板變量 #xxx="ngModel" (就是上面的#phone), 下面的錯誤提示DIV中用的phone.invalid 和 phone.dirty 等,其中的phone就是這個模板變量xxx,即指向被綁定的數據模型,這里是hero.phone