Angular在模板驅(qū)動表單中自定義校驗(yàn)器的方法
引言
模板驅(qū)動表單相比較響應(yīng)式表單可以少更少的代碼做同樣的事情,可也損失了自由度與更易測試,當(dāng)然很多人并不在乎啦。
所以我相信很多人在編寫Angular不自由自主去更傾向于模板驅(qū)動表單的寫法。
表單最核心的是校驗(yàn)體驗(yàn),在Angular中簡直就是發(fā)揮到了極致,比如:required、min、max、pattern 等,這些原本是HTML DOM元素中的表述,而Angular默認(rèn)實(shí)現(xiàn)了一整套的校驗(yàn)指令,比如:required 對應(yīng) RequiredValidator。
然后很多時候我們需要一些特殊的校驗(yàn),比如:數(shù)據(jù)比較、遠(yuǎn)程校驗(yàn)等。那在模板驅(qū)動表單風(fēng)格中我們要如何優(yōu)雅的實(shí)現(xiàn)這樣一個校驗(yàn)器呢?
一、Angular是如何校驗(yàn)?
一般在編寫一個手機(jī)文本框可能是這樣:
<input [(ngModel)]="user.mobile" #mobile="ngModel" autocomplete="off" type="tel" class="form-control" name="mobile" required maxlength="11"> <div *ngIf="mobile.errors"> <p *ngIf="mobile.errors.required">手機(jī)號必填</p> <p *ngIf="mobile.errors.pattern">手機(jī)號格式不正確</p> </div>
以上幾行很友好的實(shí)現(xiàn)從必填項(xiàng)、格式進(jìn)行校驗(yàn),而這一切都是依靠 [(ngModel)] 統(tǒng)一采集,得以只需要利用一個模板引用變量訪問到每個校驗(yàn)指令的錯誤信息。
1、[(ngModel)] 到底做了什么?
在解析這個問題前需要先了解一下 RequiredValidator 是如何定義的。
@Directive({ providers: [{ provide: NG_VALIDATORS, useExisting: forwardRef(() => RequiredValidator), multi: true }] }) export class RequiredValidator {}
只看最核心向 NG_VALIDATORS 標(biāo)識符注冊一個 RequiredValidator 指令。這樣就可以使 ngModel 指令中注入 NG_VALIDATORS 后就能得到這個指令對象。
ngModel 我把它簡化了一下:
export class NgModel extends NgControl { constructor(@Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>) {} get validator(): ValidatorFn|null { // 各種校驗(yàn)并返回結(jié)果 } }
有關(guān)更多ng_model.ts可以深入閱讀源代碼。
Angular會在每一次表單值變更時,對所有的表單中已經(jīng)安裝的校驗(yàn)器進(jìn)行一次遍歷。
二、編寫一個校驗(yàn)器
誠如 required 校驗(yàn)器一樣,依然是把自定義校驗(yàn)器掛到 NG_VALIDATORS 當(dāng)中。假如我們希望手機(jī)文本框只能輸入 159 開頭的一個校驗(yàn)器。
定義Directive
@Directive({ selector: '[user-mobile]', exportAs: 'userMobile', providers: [{ provide: NG_VALIDATORS, useExisting: forwardRef(() => UserMobileDirective), multi: true }] }) export class UserMobileDirective {}
一個非常普通的指令定義方法,只是多了一個將 UserMobileDirective 注冊到 NG_VALIDATORS 標(biāo)識符當(dāng)中而已。別問我為什么,一種約定。
類
export class UserMobileDirective implements Validator { validate(c: AbstractControl): { [key: string]: any; } { let value: string = c.value || ''; if (!value.startsWith('159')) { return { mobile: { msg: '手機(jī)號必須是159開頭', actualValue: value } }; } return null; } }
只需要實(shí)現(xiàn) Validator 接口的 validate 方法即可。
從 c 中獲取DOM值,當(dāng)遇到非 159 開頭時,返回一個用于表述消息的對象即可,否則返回一個 null。這個對象會被統(tǒng)一采集在 ngModel.errors 對象下面。故而,只需要在DOM元素加上 user-mobile 指令即可。
<input user-mobile [(ngModel)]="user.mobile" #mobile="ngModel" autocomplete="off" type="tel" class="form-control" name="mobile" id="mobile" required maxlength="11"> <div *ngIf="mobile.errors"> <p *ngIf="mobile.errors.required">手機(jī)號必填</p> <p *ngIf="mobile.errors.mobile">{{mobile.errors.mobile.msg}}</p> </div>
接口還包括一個 registerOnValidatorChange 可選方法,當(dāng)某些其它外部屬性的變更時,允許重新手動觸發(fā)校驗(yàn)。
三、異步校驗(yàn)器
如果說用戶手機(jī)校驗(yàn)器需要檢查手機(jī)是否為黑名單的情況下,正常黑名單數(shù)據(jù)都存在遠(yuǎn)程當(dāng)中。這樣情況下需要發(fā)送HTTP請求,而這一過程就是異步。
Angular針對這類異步校驗(yàn)有獨(dú)立的另一個標(biāo)識符,即:NG_ASYNC_VALIDATORS,而其它代碼都是相通的。
@Directive({ selector: '[user-async]', exportAs: 'userAsync', providers: [{ provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => UserAsyncDirective), multi: true }] }) export class UserAsyncDirective implements Validator { validate(c: AbstractControl): Observable<any> { return c.valueChanges // 去抖 .debounceTime(300) // 抑制重復(fù)值 .distinctUntilChanged() // 1、可以使用flatMap進(jìn)行遠(yuǎn)程校驗(yàn) // .flatMap(value => value) // 2、本地模擬判斷 .map((value: string) => { if ([ '15900000001', '15900000002' ].includes(value)) { return { mobile: { msg: '手機(jī)號為黑名', actualValue: value } } } return null; }) .first(); } }
除了 NG_ASYNC_VALIDATORS 核心的結(jié)構(gòu)完全沒有變動。
而對于 validate 方法返回的是一個 Observable 類型,利用對 valueChanges 的訂閱可以制作一些像去抖動作。
而最后必須使用 first() 做為結(jié)尾,原因每一次校驗(yàn),對于結(jié)果而言只允許一個。
結(jié)論
本章介紹的是如何對模板驅(qū)動表單創(chuàng)建自定義校驗(yàn)器,它相比較響應(yīng)式表單自定義校驗(yàn)器略為復(fù)雜一些。但是實(shí)際運(yùn)用中,我們不應(yīng)該只為某個構(gòu)建表單風(fēng)格做一種自定義校驗(yàn)器,應(yīng)該二者是共存的。
比如上面 159 開頭的示例。更合理的編寫方式應(yīng)該是將校驗(yàn)邏輯獨(dú)立:
export class MyValidators { static checkMobile(value: string): ValidationErrors|null { return !value.startsWith('159') ? { mobile: { msg: '手機(jī)號必須是159開頭' } } : null; } } // 校驗(yàn)器類 export class UserMobileDirective implements Validator { validate(c: AbstractControl): { [key: string]: any; } { let value: string = c.value || ''; return MyValidators.checkMobile(value); } }
這樣,同一個校驗(yàn)器,不管是模板驅(qū)動表單還是響應(yīng)式表單,都能是通用的。
總結(jié)
以上所述是小編給大家介紹的Angular在模板驅(qū)動表單中自定義校驗(yàn)器的方法,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
深入解析AngularJS框架中$scope的作用與生命周期
這篇文章主要介紹了AngularJS中$scope的作用與生命周期,包括在DOM中添加controller對象的相關(guān)用法,需要的朋友可以參考下2016-03-03淺談angularjs $http提交數(shù)據(jù)探索
這篇文章主要介紹了淺談angularjs $http提交數(shù)據(jù)探索,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-01-01AngularJS通過ng-route實(shí)現(xiàn)基本的路由功能實(shí)例詳解
這篇文章主要介紹了AngularJS通過ng-route實(shí)現(xiàn)基本的路由功能,結(jié)合實(shí)例形式詳細(xì)分析了AngularJS使用ng-route實(shí)現(xiàn)路由功能的操作步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-12-12angularjs 指令實(shí)現(xiàn)自定義滾動條效果
橫向商品欄,把原有的滾動條改成自定義的樣式,并且給兩邊加上箭頭可以調(diào)整,可以拖動商品和滾輪實(shí)現(xiàn)滾動條效果,這篇文章主要介紹了angularjs 指令實(shí)現(xiàn)自定義滾動條效果,需要的朋友可以參考下2024-03-03angular+ionic 的app上拉加載更新數(shù)據(jù)實(shí)現(xiàn)方法
這篇文章主要介紹了angular+ionic 的app上拉加載更新數(shù)據(jù)實(shí)現(xiàn)方法,需要的的朋友參考下2017-01-01Angular獲取手機(jī)驗(yàn)證碼實(shí)現(xiàn)移動端登錄注冊功能
最近在使用angular來做項(xiàng)目,功能要求實(shí)現(xiàn)一是點(diǎn)擊按鈕獲取驗(yàn)證碼,二是點(diǎn)擊登錄驗(yàn)證表單。之前用jquery來做項(xiàng)目很好做,使用angular怎么實(shí)現(xiàn)呢?其實(shí)實(shí)現(xiàn)代碼也很簡單的,下面通過實(shí)例代碼給大家介紹下,需要的朋友參考下吧2017-05-05