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

                開發分頁方法有兩個方法,第一個是直接在當前的clazz列表組件開發,第二個是將分頁專門的剝離出一個組件。雖然第二個方法更正綜,但我們初期完成某個功能時,往往是先采用第一種,待使用第一種完成功能后,再使用第二種。 在此仍然采用step by step的敏捷開發模式,所謂的敏捷就是指把大的復雜的功能拆分成小的可衡量的功能模塊。然后一步步完成各個功能模塊,完成一點測試一點,直至最終功能完成。 ## 靜態分頁 我們先實現一個靜態分頁,即html中的頁數是固定的,但點擊頁碼卻真實的觸發C層的分頁方法,重新按頁數請求數據。 ```html +++ b/first-app/src/app/clazz/clazz.component.html @@ -32,10 +32,10 @@ <nav class="row justify-content-md-center"> <ul class="pagination col-md-auto"> - <li class="page-item disabled"><a class="page-link" href="#">上一頁</a></li> - <li class="page-item active"><a class="page-link" href="#">1</a></li> - <li class="page-item"><a class="page-link" href="#">2</a></li> - <li class="page-item"><a class="page-link" href="#">3</a></li> - <li class="page-item"><a class="page-link" href="#">下一頁</a></li> + <li class="page-item disabled"><span class="page-link">上一頁</span></li> + <li class="page-item active"><span class="page-link" (click)="onPage(1)">1</span></li> + <li class="page-item"><span class="page-link" (click)="onPage(2)">2</span></li> + <li class="page-item"><span class="page-link" (click)="onPage(3)">3</span></li> + <li class="page-item"><span class="page-link">下一頁</span></li> </ul> ``` 上述代碼使用了`span`標簽來替換`a`標簽,加入了`onPage()`方法并對應傳入了頁碼,在C層建立`onPage()`方法如下: ```typescript + onPage(page: number): void { + console.log(page); + } ``` 然后在V層點擊相應的分頁,查看控制臺變化: ![image-20210330163243332](https://img.kancloud.cn/fb/16/fb16e6bd2c533699fc7a6a3ac2e49070_1666x318.png) 觸發了C層的方法后, 我們便可以利用該分頁的值去請求對應的后臺數據了: ```typescript this.httpClient.get<Page<Clazz>>('/clazz/page?page=' + page.toString()) .subscribe(pageData => { this.pageData = pageData; console.log(pageData); }); ``` 在上述代碼中,我們接收了帶有`page`信息的請求地址,這樣以來后臺便可以接收到`page`的信息從而返回當前頁碼的數據了。但由于我們引用的`MockApi`天生存在一些缺陷的原因,導致MockApi無法匹配URL中的參數,所以此時點擊分頁時將得到一個如下錯誤: ![image-20210331082004280](https://img.kancloud.cn/29/0d/290dfa576d9accce87270ac5015b36b9_2108x130.png) > ? MockApi是團隊的一個開源項目,地址為:[https://github.com/yunzhiclub/ng](https://github.com/yunzhiclub/ng),希望能夠得到有能力的小伙伴的幫助,使MockApi越來越好。 其實`MockApi`之所以沒有匹配這種直接將參數放到URL的情況是因為:在使用HttpClient發起帶有參數的請求時,正確的打卡姿勢是使用`HttpParams`,比如我們想在請求中加入`page`參數,則需要如下使用: ```typescript +++ b/first-app/src/app/clazz/clazz.component.ts @@ -2,7 +2,7 @@ import {Component, OnInit} from '@angular/core'; import {Page} from '../entity/page'; import {Clazz} from '../entity/clazz'; import {Teacher} from '../entity/teacher'; -import {HttpClient} from '@angular/common/http'; +import {HttpClient, HttpParams} from '@angular/common/http'; @Component({ selector: 'app-clazz', @@ -33,7 +33,8 @@ export class ClazzComponent implements OnInit { } onPage(page: number): void { - this.httpClient.get<Page<Clazz>>('/clazz/page?page=' + page.toString()) + const httpParams = new HttpParams().append('page', page.toString()); + this.httpClient.get<Page<Clazz>>('/clazz/page', {params: httpParams}) ``` 此時當我們再次點擊分頁按扭時,錯誤消失并且可以在控制臺中發現打印的數據返回情況: ![image-20210331083022413](https://img.kancloud.cn/ac/17/ac17f628d29b07afc3beff9c32b1d20d_1454x134.png) 但組件中顯示的班級列表,卻沒有任何變化。這是由于我們在當前的單元測試中使用了`fixture.detectChanges();`來手動控制了組件的渲染。這導致了C層中的數據即使發生了變化,由于沒有啟用自動檢測變更機制,所以組件的V層也不會自動渲染。 所以我們得出的結論是:如果想借助`ng t`來查看一些功能,則需要在測試用例的最后一行啟用測試夾具的自動檢測變更機制: ```typescript +++ b/first-app/src/app/clazz/clazz.component.spec.ts @@ -36,5 +36,6 @@ describe('ClazzComponent', () => { expect(component).toBeTruthy(); getTestScheduler().flush(); fixture.detectChanges(); + fixture.autoDetectChanges(); }); }); ``` 此時,當我們點擊分頁按鈕時,組件的內容也會隨著發生變化,這與應用真實后臺的效果一模一樣。 **小BUG**:我們剛剛不小心寫了一個小BUG。上個小節中的接口規范中,每幾頁是`0基`而非`1基`的,也就是說如果我們獲取第1頁的信息,則應該將`page`設置為0。為此我們變更一下V層的代碼: ```html - <li class="page-item active"><span class="page-link" (click)="onPage(1)">1</span></li> - <li class="page-item"><span class="page-link" (click)="onPage(2)">2</span></li> - <li class="page-item"><span class="page-link" (click)="onPage(3)">3</span></li> + <li class="page-item active"><span class="page-link" (click)="onPage(0)">1</span></li> + <li class="page-item"><span class="page-link" (click)="onPage(1)">2</span></li> + <li class="page-item"><span class="page-link" (click)="onPage(2)">3</span></li> ``` 此時當我們當擊第1頁時,向C層傳遞的是0;點擊第2頁時,向C層傳遞的是1。 ## 加入每頁大小 前面我們在初始化組件時,初始化了每頁大小為3,而當前的模擬數據返回的每頁大小為默認的20。參考在請求時加入`page`的代碼,我們在請求中加入`size`: ```typescript onPage(page: number): void { - const httpParams = new HttpParams().append('page', page.toString()); + const httpParams = new HttpParams().append('page', page.toString()) + .append('size', this.size.toString()); this.httpClient.get<Page<Clazz>>('/clazz/page', {params: httpParams}) ``` 有了第幾頁、每頁大小后,在當下進行測試,仍然發現返回了20條數據。這是由于我們在ClazzMockApi中的相關方法,并沒有對`page`和`size`進行處理的原因。在ClazzMockApi中,我們是可以輕松的獲取到請求參數的: ```typescript +++ b/first-app/src/app/mock-api/clazz.mock.api.ts @@ -2,6 +2,7 @@ import {ApiInjector, MockApiInterface, randomNumber, RequestOptions} from '@yunz import {Clazz} from '../entity/clazz'; import {Teacher} from '../entity/teacher'; import {Page} from '../entity/page'; +import {HttpParams} from '@angular/common/http'; /** * 班級模擬API @@ -37,7 +38,9 @@ export class ClazzMockApi implements MockApiInterface { { method: 'GET', url: '/clazz/page', - result: () => { + result: (urlMatches: string[], options: RequestOptions) => { ?? + const httpParams = options.params as① HttpParams; + console.log(httpParams.get('page'), httpParams.get('size')); const size = 20; const clazzes = new Array<Clazz>(); for (let i = 0; i < size; i++) { ``` result屬性設置為回調函數的方法我們在前面已然接觸過,該方法支持0個,1個或2個參數。MockApi在模擬返回數據時,將使用相應的值做為參數來調用result屬性對應的方法。其中第一個參數`urlMatches`為請求URL應用正則表式的匹配結果,類型為字符串數組;第二個參數`options`為請求的具體信息,包括請求主體,請求`header`,以及我們本次使用的請求參數`params`。 ① `RequestOptions`中的`params`屬性有多個類型,其實包括了我們使用的`HttpParams` ,在此使用`as`指定其具體類型。`as`的使用情景為:我們確認數據的確切類型時。由于該`params`實際上為C層中我們使用`get`方法傳入的`HttpParams`類型,所以我們在此使用了`as HttpParams`來指定以規避`typescript`的一些語法提示。 此時再次點擊相應的分頁,則會在控制臺中打印具體的分頁信息: ![image-20210331091626114](https://img.kancloud.cn/e7/e0/e7e02a6de6c85358f7c39553853c2f1d_1446x128.png) 第一行打印了兩個`null`,就是由于在組件的`ngOnInit()`方法中同樣調用了后臺的分頁,在該方法中未指定`page`及`size`,所以在執行`httpParams.get('page')`方法時返回了`null`。 ## 完善模擬數據 完善的模擬數據能夠友好的支持組件開發,而且有些代碼可以只造一個輪子,在開發的時候是非常具有性價比的。在模擬Api返回數據前,是完全可以根據傳入的`page`、`size` 值來定制返回數據的: ```typescript +++ b/first-app/src/app/mock-api/clazz.mock.api.ts result: (urlMatches: string[], options: RequestOptions) => { + // 初始化兩個默認值 + let page = 0; + let size = 20; + const httpParams = options.params as HttpParams; - console.log(httpParams.get('page'), httpParams.get('size')); - const size = 20; + if (httpParams.has('page')) { + // 在這里我們使用了`has()`方法來判斷是否存在該字段。 + // 所以在此執行httpParams.get('page')必然返回一個非null的值 + // 結合httpParams.get('page')返回值類型規定為null | string + // null | string去了一個null,則返回值類型必然為string,所以在使用as指定 + // + 的目的是將string類型轉換為number + page = +(httpParams.get('page') as string); + } + + if (httpParams.get('size')) { + size = +(httpParams.get('size') as string); + } + @@ -55,9 +70,9 @@ export class ClazzMockApi implements MockApiInterface { } return new Page<Clazz>({ content: clazzes, - number: 2, + number: page, size, - numberOfElements: 20 + numberOfElements: size * 10 }); } } ``` 此時當未接收到`page`或`size`時將使用默認值設置`page`,`size`值,接收到`page`或`size`時將使用接收到的值。在返回值中,加入了當前頁、每頁大小信息,且當數據的總數量控制在10頁。此時,在組件初始化時將返回第1頁的數據,每頁大小20條;點擊分頁時,分頁大小為3條,對應返回當前頁碼的數據。比如點擊第2頁: ![image-20210331093356204](https://img.kancloud.cn/bc/17/bc172b1c418b3243a22789b55f42b80d_1868x572.png) 控制臺打印的返回數據信息,顯示當前頁為第2頁。 ![image-20210331093346647](https://img.kancloud.cn/05/42/0542a1db37622f1869742a7b76677789_804x238.png) 最后為了保持組件風格統一,我們在組件初始化時當每頁大小設置為3: ```typescript +++ b/first-app/src/app/clazz/clazz.component.ts @@ -28,7 +28,8 @@ export class ClazzComponent implements OnInit { } ngOnInit(): void { - this.httpClient.get<Page<Clazz>>('/clazz/page') + this.httpClient.get<Page<Clazz>>('/clazz/page', + {params: new HttpParams().append('size', this.size.toString())}) .subscribe(pageData => this.pageData = pageData); } ``` 對應的后臺請求完成后,接下來實現分頁的點亮效果:比如當前為第1頁,則第1頁是選中狀態: ![image-20210331093808677](https://img.kancloud.cn/f1/0b/f10b0ccfcd7b0a452848bbac1b6fba67_317x52.png) 如果是第2頁,則2是選中狀態,以此累推。 ## 點亮效果 觀察html我們得知點亮效果是由樣式控制的: ```typescript <li class="page-item active??"><span class="page-link" (click)="onPage(0)">1</span></li> ``` 則我們可以使用以下思路來完成該功能: - 在C層記錄當前是第幾頁 - 在V層的分頁按鈕上做判斷,如果其分頁值等于當前頁則加入`active`樣式 ### 記錄當前頁 記錄當前頁比較簡單,僅僅需要在`onPage()`方法中對`page`設置值即可: ```typescript +++ b/first-app/src/app/clazz/clazz.component.ts @@ -34,10 +34,14 @@ export class ClazzComponent implements OnInit { } onPage(page: number): void { + // 在請求數據之前設置當前頁 + this.page = page; ?? const httpParams = new HttpParams().append('page', page.toString()) .append('size', this.size.toString()); this.httpClient.get<Page<Clazz>>('/clazz/page', {params: httpParams}) .subscribe(pageData => { + // 在請求數據之后設置當前頁 + this.page = page; ?? this.pageData = pageData; console.log(pageData); }); ``` 在設置當前頁時有兩種選擇:在獲取到后臺返回數據之前或之后。我們在此使用第二種方案:在獲取到后臺返回數據之后,為此刪除在請求數據之前設置當前頁的代碼: ```typescript onPage(page: number): void { - // 在請求數據之前設置當前頁 - this.page = page; const httpParams = new HttpParams().append('page', page.toString()) .append('size', this.size.toString()); ``` ### ngClass 使用我們前面學習過的`ngIf`指令,可以非常輕松的完成:根據當前頁情況選擇是否添加`active`樣式功能: ```html <ul class="pagination col-md-auto"> <li class="page-item disabled"><span class="page-link">上一頁</span></li> <li *ngIf="page !== 0" class="page-item"><span class="page-link" (click)="onPage(0)">1</span></li> <li *ngIf="page === 0" class="page-item active"><span class="page-link" (click)="onPage(0)">1</span></li> <li *ngIf="page !== 1" class="page-item"><span class="page-link" (click)="onPage(1)">2</span></li> <li *ngIf="page === 1" class="page-item active"><span class="page-link" (click)="onPage(1)">2</span></li> <li *ngIf="page !== 2" class="page-item"><span class="page-link" (click)="onPage(2)">3</span></li> <li *ngIf="page === 2" class="page-item active"><span class="page-link" (click)="onPage(2)">3</span></li> <li class="page-item"><span class="page-link">下一頁</span></li> </ul> ``` ![image-20210331095454709](https://img.kancloud.cn/f4/ce/f4ce05432ce40144776a8f464c60d6fe_832x305.png) 上述方法雖然可行,但冗余的代碼有些過多。Angular當然可以更優雅的處理此種情況---- `[ngClass]`。`[ngClazz]`能夠實現根據某種條件來選擇是否添加某樣式的功能,比如當前情況可以使用`[ngClass]`改寫如下: ```html <ul class="pagination col-md-auto"> <li class="page-item disabled"><span class="page-link">上一頁</span></li> <li [ngClass]="{active: page === 0}"?? class="page-item"><span class="page-link" (click)="onPage(0)">1</span></li> <li [ngClass]="{active: page === 1}"?? class="page-item"><span class="page-link" (click)="onPage(1)">2</span></li> <li [ngClass]="{active: page === 2}"?? class="page-item"><span class="page-link" (click)="onPage(2)">3</span></li> <li class="page-item"><span class="page-link">下一頁</span></li> </ul> ``` `[ngClass]`接收一個對象,將對象上的某個屬性值為`true`時,將使用該屬性名做為class值,添加其所在的元素(宿主元素)的`class`屬性上。所以查看元素觀察生成的`html`代碼時,會看到如下代碼: ![image-20210331100102895](https://img.kancloud.cn/1c/3d/1c3dcf39bf8299dddbf87f74c96adc65_1466x258.png) 同樣,我們還可以使用`[ngClass]`來動態的為**上一頁**、**下一頁**添加相應的`disabled` 樣式。 在下個小節中,我們將共同學習如何生成一個動態的分頁,從而替換當前手寫的1,2,3頁。 ## 本節作業 1. `onPage()`方法中使用`this.page = page`更新了當前頁,請嘗試將代碼移至請求數據之前,然后再點擊分頁,觀察兩種設置方式的不同。 2. 使用`[ngClass]`為**上一頁**、**下一頁**添加相應的`disabled` 樣式。 | 名稱 | 鏈接 | | ------------------ | ------------------------------------------------------------ | | 配置 HTTP URL 參數 | [https://angular.cn/guide/http#configuring-http-url-parameters](https://angular.cn/guide/http#configuring-http-url-parameters) | | NgClass | [https://angular.cn/guide/built-in-directives#ngclass](https://angular.cn/guide/built-in-directives#ngclass) | | 本節源碼 | [https://github.com/mengyunzhi/angular11-guild/archive/step6.3.4.zip](https://github.com/mengyunzhi/angular11-guild/archive/step6.3.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>

                              哎呀哎呀视频在线观看