<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>

                本節我們展示如何在組件中獲取一個`YzAsyncValidators`實例。 ## 手動構建 `YzAsyncValidators`的構造函數中聲明了`HttpClient`,也就是構造一個`YzAsyncValidators`實例的前提是需要一個`HttpClient`實例。 ```typescript /** * 異步驗證器. */ export class YzAsyncValidators { constructor(private httpClient: HttpClient) { } ``` 在學生添加組件中,可以采取自動注入的方式,輕構的獲取到一個`HttpClient`實例: ```typescript +++ b/first-app/src/app/student/add/add.component.ts - constructor() { + constructor(private httpClient: HttpClient) { ``` 然后就可以使用這個`HttpClient`實例來構造一個`YzAsyncValidators`實例了: ```typescript constructor(private httpClient: HttpClient) { - const yzAsyncValidators = null as unknown as YzAsyncValidators; + const yzAsyncValidators = new YzAsyncValidators(httpClient); this.formGroup = new FormGroup({ ``` 執行學生新增組件的單元測試,錯誤信息消失,當輸入學號時,在控制臺中打印信息如下: ![image-20210414085521792](https://img.kancloud.cn/8a/ea/8aea5d10999e9ac8895f2f683f4b4283_1082x152.png) ## 完成功能 `YzAsyncValidators`有了`HttpClient`后,便可以向后臺發起請求,然后根據后臺的返回值情況來決定驗證是否通過。 ```typescript +++ b/first-app/src/app/yz-async-validators.ts numberNotExist(): (control: AbstractControl) => Observable<ValidationErrors | null> { return (control: AbstractControl): Observable<ValidationErrors | null> => { - console.log(this.httpClient); - console.log('異步驗證器被調用'); - return of(null) - .pipe(delay(1000), tap(data => console.log('驗證器返回數據', data))); + const httpParams = new HttpParams() + .append('number', control.value); ① + return this.httpClient.get<boolean>③('/student/numberIsExist', {params: httpParams}) ② + .pipe④(map⑤(exists => exists ? {numberExist: true} : null)⑥); }; } } ``` - ① 創建了請求參數,并將`number`加入請求參數中 - ② 發起了一個加入了請求參數的**預**請求,該**預**請求僅當被訂閱時才會發起真正的請求,而`FormControl`的異步驗證器會根據情況來決定是否進行訂閱,從而控制是否向后臺發起請求。 - ③ 該預請求的返回值的類型為`boolean`,這是由后面臺API返回類型決定的。所以預請求的返回值類型為`Observable<boolean>` - ④ `pipe`的作用是使數據依次通過各個管道(操作符)。 - ⑤ `map`操作符能起到數據轉換的作用。`boolean`類型的值由管道這頭輸入,然后它按自己的規則決定管道那頭的輸出。 - ⑥ 當前的轉換規則是:如果管道這頭接收的是`true`,則管道那頭輸出`{numberExist: true}`;如果管道這頭接收提`false`,則管道那頭輸出的是`null`。該規則將`boolean`類型的值成功的轉換為了`ValidationErrors | null`。便得最終的返回值類型由`Observable<boolean>`變更為` Observable<ValidationErrors | null>` 為了更好的理解`map`操作符,我們在當前的學生增加組件的測試文件中增加一個測試方法: ```typescript +++ b/first-app/src/app/student/add/add.component.spec.ts fit('理解map操作符', () => { // 數據源發送數據1 const a = of(1) as Observable<number>; a.subscribe(data => console.log(data)); // 數據源還是發送數據1 const b = of(1) as Observable<number>; // 使用pipe,但不加任何管道 const c = b.pipe() as Observable<number>; c.subscribe(data => console.log(data)); // 接著發送數據源1,通過map操作符完成number到string的轉換 const d = of(1) as Observable<number>; const e = d.pipe(map(data => { console.log('map操作符接收到的值的類型為:', typeof data); const result = data.toString(); console.log('map操作符轉換后的值的類型為:', typeof result); return result; })) as Observable<string>; e.subscribe(data => console.log(data)); // 下面的寫法與上面的效果一樣,當箭頭函數中僅有一行代碼時,可以省略該代碼的return關鍵字 const f = of(1) as Observable<number>; const g = f.pipe(map(data => data.toString())) as Observable<string>; g.subscribe(data => console.log(data)); // 除進行一般的類型轉換外,還可以轉換為任意類型 const h = of(1).pipe(map(data => { return {value: data}; })) as Observable<{ value: number }>; h.subscribe(data => console.log(data)); // 當然也可以轉換為null類型 const i = of(1).pipe(map(data => { if (data !== 1) { return {test: data}; } else { return null; } })) as Observable<{ value: number } | null>; i.subscribe(data => console.log(data)); }); ``` 測試結果如下: ![image-20210414092934339](https://img.kancloud.cn/9c/6e/9c6eeae122617b27936328b60efe62ed_1398x264.png) ## 測試 接著回到學號的異步驗證器的測試上來:當輸入032282時錯誤時顯示錯誤提示。 ![image-20210414093034719](https://img.kancloud.cn/47/ce/47ce3abe7d9f2d22115cb10ad1ebd0ce_1236x190.png) 當輸入其它學號時,錯誤提示消失: ![image-20210414093223995](https://img.kancloud.cn/4a/4d/4a4d0ce1e0014c8f0a0f9eff9d3e8d55_1180x146.png) 上述測試足以說明異步驗證是有效的。 ## 完善提示信息 輸入032282的學號時,異步驗證器返回了驗證失效的信息,但提示信息卻為:學號不能為空。正確的信息應該是:該學號已存在。 當一個`FormControl`存在多個驗證器時,當驗證不通過時可以通過`FormControl`的`errors`來定制驗證信息: ```html +++ b/first-app/src/app/student/add/add.component.html @@ -11,7 +11,7 @@ <div class="mb-3 row"> <label class="col-sm-2 col-form-label">學號</label> <div class="col-sm-10"> - {{formGroup.get('number').pending}} + {{formGroup.get('number').errors | json}} <input type="text" class="form-control" formControlName="number"> <small class="text-danger" *ngIf="formGroup.get('number').invalid"> 學號不能為空 ``` 此時,當學號為空時,顯示的提示信息如下: ![image-20210414093606246](https://img.kancloud.cn/2a/69/2a69b6e194576c3a169b213192e79dd9_1182x180.png) 當學號為032282時,顯示的提示信息為: ![image-20210414093628734](https://img.kancloud.cn/9f/95/9f95c3197bab8000ce294782bf4da3d0_1154x188.png) 如此以來,便可以利用`errors`如下定義提示信息了: ```html <small class="text-danger" *ngIf="formGroup.get('number').invalid"> - 學號不能為空 + <span *ngIf="formGroup.get('number').errors.required">學號不能為空</span> + <span *ngIf="formGroup.get('number').errors.numberExist">該學號已存在</span> </small> ``` 此時將輸入032282時,將提示**該學號已存在**。 ![image-20210414093947507](https://img.kancloud.cn/49/ea/49ea9e285161fe6de4c53e7f75514b5c_1132x180.png) 提示信息解決后,還需要解決異步驗證器在發起后臺請求時保存按鈕被點亮的BUG。解決該問題可以利用`FormControl`的`pending`屬性: ```html <div class="col-sm-10 offset-2"> - <button class="btn btn-primary" [disabled]="formGroup.invalid">保存 + <button class="btn btn-primary" [disabled]="formGroup.invalid || formGroup.pending">保存 </button> </div> ``` 如此以來,當異步驗證器發起異步驗證時`pending`的值為`true`,保存按鈕不可用;當異步驗證器完成異步驗證時`pending`的值為`false`,此時保存按鈕是否可以則取決于`invalid`屬性。 ## 自動構建 完善完異步驗證器后,讓我們把思緒拉回來在組件中構造`YzAsyncValidators`實例上來。剛剛我們通過在組件中注入`HttpClient`,然后再構建 `YzAsyncValidators`實例。 但隨著項目的更新,上述方法可能會引發比較嚴重的問題。 ![image-20210414094710308](https://img.kancloud.cn/e6/ab/e6abeb7aec21b3383cd16f8a2dd5b375_846x272.png) 1. 組件a、b、c、d、e全部依賴于`YzAsyncValidators`驗證器。所以在組件a、b、c、d、e全部注入`HttpClient`實例,進而使用`new `YzAsyncValidators(httpClient)`來獲取一個驗證器實例。 2. 有一天`YzAsyncValidators`除依賴于`HttpClient`外,還依賴于`ActivedRouter`實例,構造函數變更為:`constructor(private httpClient: HttpClient, private activedRouter: ActivedRouter) {`。 3. 這時候災難便發生了,我們需要依次修改組件a、b、c、d、e,每個組件中都需要再注入個`ActivedRouter`實例。 這明顯是不可接受的,我們的理想目標是當`YzAsyncValidators`變更時,依賴于該`YzAsyncValidators`組件可以在不進行任何變更的情況下繼續正常工作。 預解決這個問題,便需要將手動實例化`YzAsyncValidators`改為自動注入了: ```typescript +++ b/first-app/src/app/student/add/add.component.ts @@ -12,8 +12,7 @@ import {HttpClient} from '@angular/common/http'; export class AddComponent implements OnInit { formGroup: FormGroup; - constructor(private httpClient: HttpClient) { - const yzAsyncValidators = new YzAsyncValidators(httpClient); + constructor(private httpClient: HttpClient, private yzAsyncValidators: YzAsyncValidators) { this.formGroup = new FormGroup({ name: new FormControl('', Validators.required), number: new FormControl('', Validators.required, yzAsyncValidators.numberNotExist()), ``` 此時當前測試將發生一個錯誤: ![image-20210414095513624](https://img.kancloud.cn/40/e8/40e8b1831b6d2b208efcd66bc6c12a1a_1706x148.png) 該錯誤提示我們說:沒有找到`YzAsyncValidators`的提供者。沒錯,由于我們未對`YzAsyncValidators`類進行任何的配置,所以當前動態測試模塊中并沒有提供`YzAsyncValidators`的能力。這時候就需要復習一下6.7.2中的單例服務了: ![image-20210409180550615](https://img.kancloud.cn/ec/b1/ecb134829c80e95e8b65b862ec9e168c_1864x392.png) 上圖的`AuthService`只所以能夠被Angular注入到任意的模塊中,是由于`AuthService`并聲明在`root`模塊中。按上述思想,我們將`YzAsyncValidators`也聲明到`root`模塊中: ```typescript +++ b/first-app/src/app/yz-async-validators.ts @@ -2,10 +2,14 @@ import {AbstractControl, ValidationErrors} from '@angular/forms'; import {Observable, of} from 'rxjs'; import {delay, map, tap} from 'rxjs/operators'; import {HttpClient, HttpParams} from '@angular/common/http'; +import {Injectable} from '@angular/core'; /** * 異步驗證器. */ +@Injectable({ + providedIn: 'root' +}) export class YzAsyncValidators { constructor(private httpClient: HttpClient) { ``` 此時Anguar便有了提供`YzAsyncValidators`的能力,單元測試中的錯誤隨即消失,異步驗證器工作正常。 ![image-20210414093947507](https://img.kancloud.cn/49/ea/49ea9e285161fe6de4c53e7f75514b5c_1132x180.png) 此時,即使有一天我們變更`YzAsyncValidators`的依賴,依賴于該`YzAsyncValidators`組件也可以在不進行任何變更的情況下繼續正常工作。 | 名稱 | 鏈接 | | --------------- | ------------------------------------------------------------ | | Angular依賴注入 | [https://angular.cn/guide/dependency-injection](https://angular.cn/guide/dependency-injection) | | 本節源碼 | [https://github.com/mengyunzhi/angular11-guild/archive/step7.2.4.zip](https://github.com/mengyunzhi/angular11-guild/archive/step7.2.4.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>

                              哎呀哎呀视频在线观看