本节我们快速完善分页的一个小问题。 当前分页在分数数据为空时,将如下显示: ![image-20210408151413344](https://img.kancloud.cn/cb/72/cb721a8e06fa0ea57cf8e6e988c30601_2092x594.png) 如果仅有一页数据,将如下显示: ![image-20210409105601799](https://img.kancloud.cn/45/90/459069d12336d55318d686d734a5b21b_602x168.png) 其实当页数为0或1时,大可隐藏分页。我们使用`ngStyle`来快速的实现该功能: ## ngStyle 使用单元测试能够快速的模拟出当前页数为0或1页的情景: ```typescript +++ b/first-app/src/app/clazz/page/page.component.spec.ts @@ -31,4 +31,15 @@ describe('PageComponent', () => { fixture.autoDetectChanges(); }); + fit('0页', () => { + component.page = new Page<any>({ + content: [], + number: 0, + size: 20, + numberOfElements: 0 + }); + fixture.detectChanges(); + }); + + }); ``` 效果如下: ![image-20210409105917134](https://img.kancloud.cn/7a/41/7a41bbcea174697de5e3f4048f0bfa7d_520x116.png) Angular提供的`ngStyle`可根据表达式来配置宿主元素的样式,比如当总页数大于1时,分页组件可见;页数小于等于1时,分页组件不可见: ```html +++ b/first-app/src/app/clazz/page/page.component.html @@ -1,4 +1,4 @@ -<nav class="row justify-content-md-center"> +<nav class="row justify-content-md-center" [ngStyle]="{'visibility': inputPage.totalPages > 1 ? 'visible' : 'hidden'}"> <ul class="pagination col-md-auto"> ``` 如上所示,`ngStyle`与`ngClass`使用方法基本相同。此时,当`inputPage.totalPages`大于1时,宿主元素将添加`visibility: visible`属性,否则将添加`visibility: hidden`属性: ![image-20210409111611094](https://img.kancloud.cn/ad/49/ad49e4f9754feca8fb72966e69da160e_1006x112.png) ### 共1页 ```typescript +++ b/first-app/src/app/clazz/page/page.component.spec.ts @@ -41,5 +41,14 @@ describe('PageComponent', () => { fixture.detectChanges(); }); + fit('1页', () => { + component.page = new Page<any>({ + content: [], + number: 0, + size: 20, + numberOfElements: 10 + }); + fixture.detectChanges(); + }); }); ``` ![image-20210409111611094](https://img.kancloud.cn/ad/49/ad49e4f9754feca8fb72966e69da160e_1006x112.png) ### 共2页 ```typescript +++ b/first-app/src/app/clazz/page/page.component.spec.ts @@ -50,6 +50,15 @@ describe('PageComponent', () => { }); fixture.detectChanges(); }); - + + fit('2页', () => { + component.page = new Page<any>({ + content: [], + number: 0, + size: 20, + numberOfElements: 25 + }); + fixture.detectChanges(); + }); }); ``` ![image-20210409111917416](https://img.kancloud.cn/2c/30/2c30dd19481909c3bc10bb4f6d9b0eb5_616x124.png) ![image-20210409111930418](https://img.kancloud.cn/42/93/429379057bf8f466b535071da82bc3ae_1012x102.png) ## 代码测试代码 肉眼毕竟不可靠,使用代码来测试会更可靠。重要的是有了单元测试代码测试的支持,我们再也不用怕其它的团队伙伴在更新其它功能时破坏当前已有功能了。 ```typescript +++ b/first-app/src/app/clazz/page/page.component.spec.ts @@ -2,6 +2,7 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {PageComponent} from './page.component'; import {Page} from '../../entity/page'; +import {By} from '@angular/platform-browser'; describe('PageComponent', () => { let component: PageComponent; @@ -39,6 +40,8 @@ describe('PageComponent', () => { numberOfElements: 0 }); fixture.detectChanges(); + const navHtml = fixture.debugElement.query(By.css('nav')).nativeElement as HTMLElement; + expect(navHtml.style.visibility).toEqual('hidden'); }); it('1页', () => { @@ -49,6 +52,8 @@ describe('PageComponent', () => { numberOfElements: 10 }); fixture.detectChanges(); + const navHtml = fixture.debugElement.query(By.css('nav')).nativeElement as HTMLElement; + expect(navHtml.style.visibility).toEqual('hidden'); }); it('2页', () => { @@ -59,6 +64,8 @@ describe('PageComponent', () => { numberOfElements: 25 }); fixture.detectChanges(); + const navHtml = fixture.debugElement.query(By.css('nav')①).nativeElement as HTMLElement; + expect(navHtml.style.visibility②).toEqual('visible'); }); }); ``` - ① 如上代码使用了`css`选择器选择了`nav`元素。 - ② 按分页情况对`nav`元素样式的`visibility`属性进行了断言。 ## 向下兼容 由于前面进行重构时的疏忽,我们制造了一个Bug。此时点击注销按钮时控制台将发生一个404错误。 ![image-20210409141900220](https://img.kancloud.cn/ed/21/ed21d2e3f37bafb93cdfd086f3826755_2534x106.png) 这是由于在前面的小节消除重复的轮子时,由于轮子过多,产生的**不可避免**的问题。 ```typescript +++ b/first-app/src/app/nav/nav.component.ts onSubmit(): void { const url = 'http://angular.api.codedemo.club/teacher/logout'; 👈 this.httpClient.get(url) .subscribe(() => this.beLogout.emit(undefined), error => console.log('logout error', error)); } ``` - 此处有个漏网之鱼。 - 该鱼尚有些历史问题,你发现了吗? 虽然我们可以将`http://angular.api.codedemo.club:81/teacher/logout`变更为`/teacher/logout`来快速的解决该问题。但更深入的问题是:我们如何在以后的项目来规避此类问题? 在生产项目中,我们在代码重构过程中,也会遇到类似的问题。且我们认为这个问题是在重构过程中不可规避的。生产项目中的重构往往不是替换一两行代码,如果需要重构的轮子比较多,则需要使用步进式重构,今天做一点,明天做一点,而在这个过程我们保证新老两种模式共同运行。我们把这种需要考虑老的模式运行的做法称为**向下兼容性**。 比如我们本节中引入了`ApiIntercepter`后,消除请求前缀轮子的同时,还需要保证没有消除请求前缀的轮子也是正常运转的。这时候就需要在`ApiIntercepter`上做一些文章,对于当前功能我们需要保证:1. 有前缀的正常请求;2. 没有前缀的添加前缀。即无论url是`http://angular.api.codedemo.club:81/teacher/logout`还是`/teacher/logout`,都能够请求正确的后台地址:`http://angular.api.codedemo.club:81/teacher/logout`。 请发挥下自己的小才能,解决这个问题吧。 | 名称 | 链接 | | -------- | ------------------------------------------------------------ | | NgStyle | [https://angular.cn/api/common/NgStyle](https://angular.cn/api/common/NgStyle) | | 本节源码 | [https://github.com/mengyunzhi/angular11-guild/archive/step6.6.4.zip](https://github.com/mengyunzhi/angular11-guild/archive/step6.6.4.zip) |