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

                本節我們完成班級管理模塊的路由定制、各個模塊間的跳轉關系以及前面遺留的`todo`: ## 路由定制 首先進行教師模塊路由的定制: ```typescript +++ b/first-app/src/app/clazz/clazz.module.ts @@ -12,6 +12,14 @@ const routes: Routes = [ { path: '', component: ClazzComponent + }, + { + path: 'add', + component: AddComponent + }, + { + path: 'edit/:id', + component: EditComponent } ]; ``` 新增組件不需要傳入參數,編輯組件需要傳入班級ID。 ### 教師列表 教師列表組件是當前模塊的著陸組件,在該組件中需要定制添加及編輯組件: ```html +++ b/first-app/src/app/clazz/clazz.component.html @@ -1,6 +1,6 @@ <div class="row"> <div class="col-12 text-right"> - <a class="btn btn-primary mr-2"><i class="fas fa-plus"></i>新增</a> + <a class="btn btn-primary mr-2" routerLink="./add"><i class="fas fa-plus"></i>新增</a> </div> </div> @@ -19,7 +19,7 @@ <td>{{clazz.name}}</td> <td>{{clazz.teacher.name}}</td> <td> - <a class="btn btn-outline-primary btn-sm"> + <a class="btn btn-outline-primary btn-sm" routerLink="edit/{{clazz.id}}"> <i class="fas fa-pen"></i>編輯 </a> ``` **新增**路由中使用了`./add`,以`./`打頭表示相對路徑,如果當前在URL為`http://localhost:4200/clazz`時,訪問`http://localhost:4200/clazz/add`;**編輯**路由中使用了`edit/`,該方法同樣為相對路徑;如果在此使用絕對路徑,則應該使用`/`打頭(不推薦),比如此處的**新增**路由使用`/clazz/add`。 此時,在教師列表組件中點擊新增按鈕,將跳轉到新增組件。 ## 新增 在新增組件中,填寫完名稱、選擇好班主任后,點擊保存后需要回跳至教師列表界面。 ```typescript +++ b/first-app/src/app/clazz/add/add.component.ts @@ -2,6 +2,7 @@ import {Component, OnInit} from '@angular/core'; import {HttpClient} from '@angular/common/http'; import {Teacher} from '../../entity/teacher'; import {Clazz} from '../../entity/clazz'; +import {Router} from '@angular/router'; @Component({ selector: 'app-add', @@ -17,7 +18,8 @@ export class AddComponent implements OnInit { teachers = new Array<Teacher>(); - constructor(private httpClient: HttpClient) { + constructor(private httpClient: HttpClient, + private router: Router) { } ngOnInit(): void { @@ -32,7 +34,7 @@ export class AddComponent implements OnInit { teacher: new Teacher({id: this.clazz.teacherId}) }); this.httpClient.post(this.url, newClazz) - .subscribe(clazz => console.log('保存成功', clazz), + .subscribe(clazz => this.router.navigateByUrl('/clazz'), error => console.log('保存失敗', error)); } ``` 在組件中進行頁面的跳轉除使用`Router.navigate()`外,還可以使用`Router.navigateByUrl()`,需要注意的是該方法的參數必須接收一個絕對地址,比如我們在此處不能使用`this.router.navigateByUrl('../')`。 此時當擊保存按鈕后,將跳轉至教師列表界面。保存后的班級顯示在了班級列表中,也驗證了班級列表功能的正常。 ![image-20210409091946088](https://img.kancloud.cn/49/4c/494c5a6df924f681091f9951d5a63a3f_1556x558.png) ## 編輯 在班級列表中點擊編輯按鈕,名稱與班主任信息并未自動填充: ![image-20210409092153078](https://img.kancloud.cn/0d/b9/0db9354c310f44394648ae45fd319233_2004x678.png) 這是由于在編輯組件中,尚存在一個todo: ```typescript +++ b/first-app/src/app/clazz/edit/edit.component.ts @@ -31,8 +31,7 @@ export class EditComponent implements OnInit { ngOnInit(): void { const id = this.activatedRoute.snapshot.params.id; - // todo: 調用loadById方法,獲取預編輯的班級 - console.log('ngOnInit方法獲取路由ID及調用loadById尚未測試,請在集成測試中補充代碼'); + this.loadById(+id); } /** ``` 修正todo后,繼續完成路由跳轉功能: ```typescript +++ b/first-app/src/app/clazz/edit/edit.component.ts @@ -1,5 +1,5 @@ import {Component, OnInit} from '@angular/core'; -import {ActivatedRoute} from '@angular/router'; +import {ActivatedRoute, Router} from '@angular/router'; import {HttpClient} from '@angular/common/http'; import {Clazz} from '../../entity/clazz'; import {FormControl, FormGroup, Validators} from '@angular/forms'; @@ -26,6 +26,7 @@ export class EditComponent implements OnInit { }); constructor(private activatedRoute: ActivatedRoute, + private router: Router, private httpClient: HttpClient) { } @@ -65,7 +66,7 @@ export class EditComponent implements OnInit { }); this.httpClient.put<Clazz>(`/clazz/${clazzId}`, clazz) .subscribe( - () => console.log('更新成功'), + () => this.router.navigate(['../../'], {relativeTo: this.activatedRoute}), error => console.log(error)); } } ``` `Router.navigate()`的第二個參數,支持傳入一個對象,當該對象的`relativeTo`屬性為`activatedRoute`時,則將其跳轉地址解析為**相對**地址。當前編輯對應的`url`為`http://localhost:4200/clazz/edit/1`,預由該`url`跳轉至`http://localhost:4200/clazz`,則需要向上翻兩層,所以相對地址應該是`../../`。 至此,模塊間便能夠順利的正進行跳轉了。最后進行**刪除**功能測試,發現一切正常。 集成測試結束。 ## 單元測試 每每修改一些功能的同時,都需要關注單元測試是否仍然正常。并需要要根據單元測試的報錯情況對應修正相關代碼。啟用`ng t`后,按提示分別進行修正: ![image-20210409095515807](https://img.kancloud.cn/7c/7a/7c7a8aeaa8380fbde7171474aea1958a_1592x168.png) 提示說找不到`Router`的提供者。通過IDE的查找功能,查找`clazz add with mockapi`關鍵字能夠快速的找到對應的測試文件: ```typescript +++ b/first-app/src/app/clazz/add/add.component.mock-api.spec.ts @@ -6,6 +6,7 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {ClazzMockApi} from '../../mock-api/clazz.mock.api'; import {TeacherMockApi} from '../../mock-api/teacher.mock.api'; import {KlassSelectComponent} from '../klass-select/klass-select.component'; +import {RouterTestingModule} from '@angular/router/testing'; describe('clazz add with mockapi', () => { let component: AddComponent; @@ -14,7 +15,7 @@ describe('clazz add with mockapi', () => { beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [AddComponent, KlassSelectComponent], - imports: [HttpClientModule, FormsModule, ReactiveFormsModule], + imports: [HttpClientModule, FormsModule, ReactiveFormsModule, RouterTestingModule], providers: [ { provide: HTTP_INTERCEPTORS, ``` 繼續排查錯誤: ![image-20210409095957319](https://img.kancloud.cn/a7/c7/a7c75e3f53d3d9707bf4041533846bc4_1518x154.png) 同樣的提示,同樣的問題,同樣的修正方法,但在IDE中使用的快速查找時,卻找到了兩個以`AddComponent`關鍵字命名的文件: ![image-20210409100125651](https://img.kancloud.cn/72/9d/729daefdfad188f20d23326c656df777_2074x376.png) 我們此時打開第二個也就是`clazz`文件中的測試文件,然后為這個測試改個名稱: ```typescript +++ b/first-app/src/app/clazz/add/add.component.spec.ts @@ -8,7 +8,7 @@ import {ApiInterceptor} from '../../api.interceptor'; import {CommonModule} from '@angular/common'; import {KlassSelectComponent} from '../klass-select/klass-select.component'; -describe('AddComponent', () => { +describe('clazz -> AddComponent', () => { let component: AddComponent; let fixture: ComponentFixture<AddComponent>; ``` 如此以來當再發生測試錯誤時,便能夠快速的定位到報錯的文件了: ![image-20210409100346150](https://img.kancloud.cn/f9/56/f9565ebddc21337d34610297b8f571a6_1516x160.png) 添加`RouterTestingModule`以提供`Router`: ```typescript declarations: [AddComponent, KlassSelectComponent], - imports: [FormsModule, HttpClientModule, CommonModule, ReactiveFormsModule], + imports: [FormsModule, HttpClientModule, CommonModule, ReactiveFormsModule, + RouterTestingModule], // 加入自定義的XAuthTokenInterceptor,讓其自動為我們處理認證的header ``` ### 控制臺錯誤 打開控制臺,發現報錯信息如下: ![image-20210409100641083](https://img.kancloud.cn/fc/05/fc053a4bed78c1890bcbd119a8dedd4c_1114x62.png) 點擊左側的小箭頭后展開錯誤信息: ![image-20210409100734441](https://img.kancloud.cn/52/cb/52cbc45e018bfd59c7c77aa5fec16abd_1178x226.png) 最示為clazz組件中的錯誤。提示`routerLink`并不是元素`a`的已知屬性,該錯誤是由于我們在clazz組件的V層中的a元素上使用了`routerLink`造成的: ```html <a class="btn btn-primary mr-2" routerLink="./add"><i class="fas fa-plus"></i>新增</a> ``` 消除該錯語的方法是為當前測試模塊提供`RouterTestingModule`,因為`RouterTestingModule`為`a`元素擴展了`routerLink`屬性: ```typescript +++ b/first-app/src/app/clazz/clazz.component.spec.ts @@ -6,6 +6,7 @@ import {ClazzMockApi} from '../mock-api/clazz.mock.api'; import {getTestScheduler} from 'jasmine-marbles'; import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http'; import {PageComponent} from './page/page.component'; +import {RouterTestingModule} from '@angular/router/testing'; describe('ClazzComponent', () => { let component: ClazzComponent; @@ -15,7 +16,8 @@ describe('ClazzComponent', () => { await TestBed.configureTestingModule({ declarations: [ClazzComponent, PageComponent], imports: [ - HttpClientModule + HttpClientModule, + RouterTestingModule ], providers: [ { ``` 繼續查看控制臺的錯誤提示: ![image-20210409101121023](https://img.kancloud.cn/27/75/2775ee6cb13c119fba4af39b9a7fbbc7_2214x260.png) 或者如下錯誤提示: ![image-20210409103426261](https://img.kancloud.cn/01/44/014474cb0ca2915e52372f9225b11cd0_1426x212.png) > 在使用`ng t`來測試整個項目時,由于測試用例執行順序的隨機性,上述兩個提示會隨機出現在控制臺中。 該提示說當執行`add.compoent.ts`的37行發生了一個錯誤:匹配不到`clazz`路由。`add.compoent.ts`的37行代碼如下: ```typescript 33 onSubmit(): void { 36 this.httpClient.post(this.url, newClazz) 37 .subscribe(clazz => this.router.navigateByUrl('/clazz'), ?? 38 error => console.log('保存失敗', error)); ``` 可見是當組件執行`onSubmit()`方法時發生的錯誤,由提示信息還可得該方法由`add.compoent.spec.ts`文件的第58行觸發: ```typescript 41 it('onSubmit', () => { 55 name: 'test', 56 teacherId: 1 57 }; 58 component.onSubmit(); ``` 由上述代碼可見:在單元測試中執行了`component.onSubmit()`方法,該方法使用了`this.router.navigateByUrl('/clazz'),`嘗試進行路由的跳轉,由于當前動態測試模塊并未設置路由,所以發生了跳轉錯誤。 如果你看到的是第二個錯誤,則是由于`add.component.mock-api.spec.ts`中同樣也觸發了`add.compoent.ts`中的上述代碼。 ### 修正錯誤 `RouterTestingModule`提供了`withRoutes([])`來為動態測試模塊設置路由,`withRoutes()`的使用可參考`RouterModule.forRoot()`方法: ```typescript +++ b/first-app/src/app/clazz/add/add.component.spec.ts @@ -8,16 +8,29 @@ import {ApiInterceptor} from '../../api.interceptor'; import {CommonModule} from '@angular/common'; import {KlassSelectComponent} from '../klass-select/klass-select.component'; import {RouterTestingModule} from '@angular/router/testing'; +import {Component} from '@angular/core'; +import {Route} from '@angular/router'; + +@Component({ ① + template: 'test' +}) +class TestComponent ① { +} describe('clazz -> AddComponent', () => { let component: AddComponent; let fixture: ComponentFixture<AddComponent>; - + const routes: Route[] = [ ② + { + path: 'clazz', ③ + component: TestComponent + } + ]; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [AddComponent, KlassSelectComponent], + declarations: [AddComponent, KlassSelectComponent, TestComponent④], imports: [FormsModule, HttpClientModule, CommonModule, ReactiveFormsModule, - RouterTestingModule], + RouterTestingModule.withRoutes(routes)⑤], // 加入自定義的XAuthTokenInterceptor,讓其自動為我們處理認證的header providers: [ {provide: HTTP_INTERCEPTORS, multi: true, useClass: XAuthTokenInterceptor}, ``` 如上代碼中做了以下三件事: - ① 定義了一只在本文件中供測試使用的Test組件 - ② 定義了一個路由數組,該數組中有一個路由,該路由配置的`path`為`clazz `③ - ④ 將Test組件聲明至動態測試模塊中的declarations中 - ⑤ 使用`RouterTestingModule.withRoutes()`來配置這個路由 如此,當前測試模塊便擁有了一個`clazz`路由。當組件嘗試跳轉至`clazz`時便可以可以匹配,從而解決了控制臺中顯示的匹配錯誤。 在`add.component.mock-api.spec.ts`同樣加入同樣的代碼,以解決`add.component.mock-api.spec.ts`調用`component.onSubmit()`時發生的路徑未匹配成功錯誤: ```typescript +++ b/first-app/src/app/clazz/add/add.component.mock-api.spec.ts @@ -7,6 +7,13 @@ import {ClazzMockApi} from '../../mock-api/clazz.mock.api'; import {TeacherMockApi} from '../../mock-api/teacher.mock.api'; import {KlassSelectComponent} from '../klass-select/klass-select.component'; import {RouterTestingModule} from '@angular/router/testing'; +import {Component} from '@angular/core'; + +@Component({ + template: 'test' +}) +class TestComponent { +} describe('clazz add with mockapi', () => { let component: AddComponent; @@ -15,7 +22,13 @@ describe('clazz add with mockapi', () => { beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [AddComponent, KlassSelectComponent], - imports: [HttpClientModule, FormsModule, ReactiveFormsModule, RouterTestingModule], + imports: [HttpClientModule, FormsModule, ReactiveFormsModule, + RouterTestingModule.withRoutes([ + { + path: 'clazz', + component: TestComponent + } + ])], providers: [ { provide: HTTP_INTERCEPTORS, ``` 至此,單元測試中的錯誤消失。 > 如果在學習單元測試時感覺有些力不從心,也可以跳過本節修正單元測試錯誤的環節。等整個教程完成后,對單元測試的認識進一步提升了,再回過頭來繼續學習。 ## 本節作業 請測試分頁功能是否正常。 | 名稱 | 鏈接 | | ------------------- | ------------------------------------------------------------ | | 指定相對路由 | [https://angular.cn/guide/router#specifying-a-relative-route](https://angular.cn/guide/router#specifying-a-relative-route) | | RouterTestingModule | [https://angular.cn/api/router/testing/RouterTestingModule](https://angular.cn/api/router/testing/RouterTestingModule) | | 本節源碼 | [https://github.com/mengyunzhi/angular11-guild/archive/step6.6.3.zip](https://github.com/mengyunzhi/angular11-guild/archive/step6.6.3.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>

                              哎呀哎呀视频在线观看