<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                本節我們為新增組件增加一些驗證。在正式編碼之前,為了使用組件與我們有個更好的交互,在單元測試的代碼中的最后一行啟動自動檢測變更: ```typescript +++ b/first-app/src/app/student/add/add.component.spec.ts @@ -32,5 +32,7 @@ describe('student -> AddComponent', () => { expect(component).toBeTruthy(); getTestScheduler().flush(); fixture.detectChanges(); + + fixture.autoDetectChanges(); }); }); ``` ## 加入驗證器 然后給名稱、學號、班級分別加一個`required`驗證器: ```typescript +++ b/first-app/src/app/student/add/add.component.ts @@ -1,5 +1,5 @@ import {Component, OnInit} from '@angular/core'; -import {FormControl, FormGroup} from '@angular/forms'; +import {FormControl, FormGroup, Validators} from '@angular/forms'; @Component({ selector: 'app-add', @@ -8,11 +8,11 @@ import {FormControl, FormGroup} from '@angular/forms'; }) export class AddComponent implements OnInit { formGroup = new FormGroup({ - name: new FormControl(), - number: new FormControl(), + name: new FormControl('', Validators.required), + number: new FormControl('', Validators.required), phone: new FormControl(), email: new FormControl(), - clazzId: new FormControl() + clazzId: new FormControl(null, Validators.required) }); constructor() { ``` 測試一校驗正常。 ## 自定義驗證器 一般情況下,只驗證是否為空是滿足不了實際的需求的,比如我們在這對手機號的驗證、郵箱的驗證。Angular支持自定義`FormControl`驗證器,來幫助我們實現Angular內置驗證器無法滿足的需求。比如當前需要一定驗證手機號是否正確的驗證器。 自定義驗證器與自定義`FormControl`有著相通之處:都需要符合某種規范。自定義`FormControl`使用了實現接口來達到符合規范的目的,自定義驗證器使用返回特定類型來達到符合規范的目的。 在自定義驗證器時,我們也像Angular一樣,把所有的驗證器放到一個類中。同時由于表單驗證器可供所有的項目使用,所以我們將其建立在項目的根目錄下,至于名字我們將其命名為`YzValidators`: ```bash panjie@panjies-iMac app % pwd /Users/panjie/github/mengyunzhi/angular11-guild/first-app/src/app panjie@panjies-iMac app % ng g class YzValidators CREATE src/app/yz-validators.spec.ts (179 bytes) CREATE src/app/yz-validators.ts (30 bytes) ``` 空的類文件`YzValidators`如下所示: ```typescript export class YzValidators { } ``` ### 驗證手機號 然后我們在該方法中建立一個用于手機號驗證的靜態方法`phone`: ```typescript import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms'; export class YzValidators { /** * 驗證手機號 */ ??static phone(control: AbstractControl①): ValidationErrors② | null { return null; ③ } } ``` - ??方法以`static`關鍵字標識 - 該方法將當前`FormControl`做為參數傳入,所以①處的類型必須是`AbstractControl` - 該方法的返回值類型必須是`ValidationErrors | null`,該返回值類型決定了其是一個表單驗證器 - ③當驗證通過時,將返回nulll;驗證不通過時,將返回`ValidationErrors`類型的數據 > 實際上`phone()`方法是`ValidatorFn`的實現。 ### 應用驗證器 自定義驗證器的用法與Angular內置驗證器的用法完全相同: ```typescript +++ b/first-app/src/app/student/add/add.component.ts @@ -11,7 +11,7 @@ export class AddComponent implements OnInit { formGroup = new FormGroup({ name: new FormControl('', Validators.required), number: new FormControl('', Validators.required), - phone: new FormControl(), + phone: new FormControl('', YzValidators.phone), email: new FormControl(), clazzId: new FormControl(null, Validators.required) }); ``` 在V層中加入測試的代碼: ```html <div class="col-sm-10"> <input type="text" class="form-control" formControlName="phone"> {{formGroup.get('phone').invalid}} <small class="text-danger" *ngIf="formGroup.get('phone').invalid"> 手機號格式不正確 </small> </div> ``` 此時由于驗證方法`phone()`的返回值為null,所以無論在手機號一欄中輸入什么內容,都不會報錯: ![image-20210412164708583](https://img.kancloud.cn/5d/dc/5ddc1a672d7474219ea23908b222253f_1514x172.png) 假設把返回值改成非null,則無論輸入的什么樣的值該驗證器都會報錯: ```typescript static phone(control: AbstractControl): ValidationErrors | null { return {phone: '手機號格式錯誤'}; } ``` ![image-20210412170000518](https://img.kancloud.cn/9c/de/9cdea9932a9fae462114f4e44040b489_914x122.png) ## 調用時機 每當`FormControl`有數據變更時,對應的驗證器都會被執行一次: ```typescript static phone(control: AbstractControl): ValidationErrors | null { console.log('phone is called'); return {phone: '手機號格式錯誤'}; } ``` 此時當我們變更表單中的手機號時,在控制臺中將反復打印相同的日志: ![image-20210412170419136](https://img.kancloud.cn/c3/56/c3560ef746d2ce0272a039f68b077e20_1102x90.png) ## 完成功能 既然每次數據改變都會調用一次`phone()`方法,那么在`phone()`方法中我們獲取當前的手機號,然后使用相關的方法驗證手機號是否符合要求即可: ```typescript static phone(control: AbstractControl): ValidationErrors | null { const phone = control.value as string; // 如果手機號是11位,并且以1打頭,則驗證成功 if (phone.length === 11 && phone.startsWith('1')) { return null; } return {phone: '手機號格式錯誤'}; } ``` 如此以來,當輸入11位以1打頭的手機號時,驗證成功; ![image-20210412170857044](https://img.kancloud.cn/63/d5/63d5be7eb3fee955bdd5a806c6932455_1076x208.png) 否則驗證失敗: ![image-20210412170848405](https://img.kancloud.cn/ca/9b/ca9bb7904749f4280e9a83fc5f19dc82_1052x202.png) ## 單元測試 我們剛剛制定了一個規則相關粗暴的驗證規則,相信在生產項目中你如上驗證手機號是否合規則一定離被噴不遠了。簡單粗暴的方案使得測試也比較簡單,這時候完成可用使用**手動**的方法。但如果測試的條件更多的話**手動**的方法便不切合實際了。 試想一下我們每完成一些驗證手機號的代碼,就需要從130-189號段一個個驗證一遍,那將是什么樣的痛苦。這時候就顯出單元測試的優勢了。 對于當前的驗證,在掌握了自定義驗證器的基本方法后,完全可以如下定制單元測試: ```typescript describe('YzValidators', () => { fit('should create an instance', () => { expect(new YzValidators()).toBeTruthy(); // 空手機號,返回非null const formControl = new FormControl(''); expect(YzValidators.phone(formControl)).toBeTruthy(); // 正常的手機號,返回null formControl.setValue('13900000000'); expect(YzValidators.phone(formControl)).toBeNull(); // 以2打頭,返回非null formControl.setValue('23900000000'); expect(YzValidators.phone(formControl)).toBeTruthy(); // 不足11位,返回非null formControl.setValue('1390000000'); expect(YzValidators.phone(formControl)).toBeTruthy(); }); }); ``` 或者,直接將驗證器引入`FormControl`: ```typescript +++ b/first-app/src/app/yz-validators.spec.ts @@ -2,7 +2,7 @@ import { YzValidators } from './yz-validators'; import {FormControl} from '@angular/forms'; describe('YzValidators', () => { - fit('should create an instance', () => { + it('should create an instance', () => { expect(new YzValidators()).toBeTruthy(); // 空手機號,返回非null const formControl = new FormControl(''); @@ -20,4 +20,22 @@ describe('YzValidators', () => { formControl.setValue('1390000000'); expect(YzValidators.phone(formControl)).toBeTruthy(); }); + + fit('將驗證器加入到FromControl', () => { + // 空手機號,校驗失敗 + const formControl = new FormControl('', YzValidators.phone); + expect(formControl.invalid).toBeTrue(); + + // 正常的手機號,校驗成功 + formControl.setValue('13900000000'); + expect(formControl.invalid).toBeFalse(); + + // 以2打頭,校驗失敗 + formControl.setValue('23900000000'); + expect(formControl.invalid).toBeTrue(); + + // 不足11位,校驗失敗 + formControl.setValue('1390000000'); + expect(formControl.invalid).toBeTrue(); + }); }); ``` 此外,我們還可以使用`valid`字段來替換`invalid`字段,這樣以來成功的時候斷言為`true`,失敗時斷言為`false`更易懂一些: ```typescript +++ b/first-app/src/app/yz-validators.spec.ts @@ -25,17 +25,21 @@ describe('YzValidators', () => { // 空手機號,校驗失敗 const formControl = new FormControl('', YzValidators.phone); expect(formControl.invalid).toBeTrue(); + expect(formControl.valid).toBeFalse(); // 正常的手機號,校驗成功 formControl.setValue('13900000000'); expect(formControl.invalid).toBeFalse(); + expect(formControl.valid).toBeTrue(); // 以2打頭,校驗失敗 formControl.setValue('23900000000'); expect(formControl.invalid).toBeTrue(); + expect(formControl.valid).toBeFalse(); // 不足11位,校驗失敗 formControl.setValue('1390000000'); expect(formControl.invalid).toBeTrue(); + expect(formControl.valid).toBeFalse(); }); }); ``` 如此以來,當我們開發更加適用的手機驗證器時,便可以維護一個更加符合現實的單元測試列表。然后在開發過程中,自動執行該測試文件,當所有的測試都通過時,就說明自己的功能成功了。相比于**手動**測試,這種單元測試的方法即安全、又高效。 ## 總結 自定義驗證器很簡單,只需要將方法聲明為`static`,并將方法的返回值類型聲明為` ValidationErrors | null`即可。 - 如果驗證內容通過,則返回`null`。 - 如果驗證內容沒有通過,則返回` ValidationErrors` - `FormControl`每變更一次,該驗證方法執行一次,所以在該方法中的代碼一定要是高效的。 ` ValidationErrors`類型實際上是以`字符串`類型為鍵值的對象,比如:`{hello: 123}`、`{a: 123}`等都是其合法的值。 ```typescript export declare type ValidationErrors = { [key: string]: any; }; ``` 返回的錯誤信息最終將匯總到`FormControl`的`errors`字段中: ```html +++ b/first-app/src/app/student/add/add.component.html @@ -22,6 +22,7 @@ <div class="col-sm-10"> <input type="text" class="form-control" formControlName="phone"> {{formGroup.get('phone').invalid}} + {{formGroup.get('phone').errors | json}} <small class="text-danger" *ngIf="formGroup.get('phone').invalid"> 手機號格式不正確 </small> ``` ![image-20210412173919740](https://img.kancloud.cn/f0/88/f08807375640c45db63dffa3564292d9_1076x212.png) ## 本節作業 嘗試完成用于驗證郵箱是否合法的郵箱驗證器,比如: ```typescript static email(control: AbstractControl): ValidationErrors | null { // 這里是邏輯實現 } ``` 然后將此驗證器應用到郵箱字段上,并進行充分的測試。 | 名稱 | 鏈接 | | ---------------- | ------------------------------------------------------------ | | 定義自定義驗證器 | [https://angular.cn/guide/form-validation#defining-custom-validators](https://angular.cn/guide/form-validation#defining-custom-validators) | | ValidatorFn | [https://angular.cn/api/forms/ValidatorFn#validatorfn](https://angular.cn/api/forms/ValidatorFn#validatorfn) | | ValidationErrors | [https://angular.cn/api/forms/ValidationErrors](https://angular.cn/api/forms/ValidationErrors) | | 本節源碼 | [https://github.com/mengyunzhi/angular11-guild/archive/step7.2.2.zip](https://github.com/mengyunzhi/angular11-guild/archive/step7.2.2.zip) |
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看