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

                本節我們展示學生編輯組件的開發在步驟及每步的詳細代碼。 ## 初始化 初始化包括組件初始化以及MockApi初始化: ### 組件初始化 來到student模塊,并使用angularCli進行完成的快速的初始化: ```bash panjie@panjies-iMac student % pwd /Users/panjie/github/mengyunzhi/angular11-guild/first-app/src/app/student panjie@panjies-iMac student % ng g c edit Your global Angular CLI version (11.2.13) is greater than your local version (11.0.7). The local Angular CLI version is used. To disable this warning use "ng config -g cli.warnings.versionMismatch false". CREATE src/app/student/edit/edit.component.css (0 bytes) CREATE src/app/student/edit/edit.component.html (19 bytes) CREATE src/app/student/edit/edit.component.spec.ts (612 bytes) CREATE src/app/student/edit/edit.component.ts (267 bytes) UPDATE src/app/student/student.module.ts (794 bytes) ``` ### MockApi初始化 根據API信息,初始化MockApi信息: ```typescript +++ b/first-app/src/app/mock-api/student.mock.api.ts @@ -78,6 +78,49 @@ export class StudentMockApi implements MockApiInterface { const ids = httpParams.getAll('ids'); Assert.isArray(ids, '未接收到ids'); }) + }, { + method: 'GET', + url: '/student/(\\d+)', + result: (urlMatches: string[]) => { + const id = +urlMatches[1]; + return { + id, + name: randomString('姓名'), + number: randomString('學號'), + phone: (139000000000 + randomNumber(99999999)).toString(), + email: randomString('前綴') + '@yunzhi.club', + clazz: { + id: randomNumber(), + name: randomString('班級名稱'), + teacher: { + id: randomNumber(), + name: randomString('教師名稱') + } as Teacher + } as Clazz + } as Student; + } + }, { + method: 'PUT', + url: '/student/(\\d+)', + result: (urlMatches: string[], options: RequestOptions) => { + const id = +urlMatches[1]; + const student = options.body as Student; + return { + id, + name: student.name, + number: randomString('學號'), + phone: student.phone, + email: student.email, + clazz: { + id: student.clazz.id, + name: randomString('班級名稱'), + teacher: { + id: randomNumber(), + name: randomString('教師名稱') + } as Teacher + } as Clazz + } as Student; + } } ]; } ``` ### M層 根據Api信息建立補充M層方法 ---- `getById`: ```bash +++ b/first-app/src/app/service/student.service.ts @@ -37,6 +37,15 @@ export class StudentService { return this.httpClient.delete<void>(url); } + + /** + * 獲取學生 + * @param id 學生ID + */ + getById(id: number): Observable<Student> { + return this.httpClient.get<Student>('/student/' + id.toString()); + } + /** ``` `update`: ```typescript +++ b/first-app/src/app/service/student.service.ts /** * 更新學生 * @param id 學生ID * @param student 學生信息 */ update(id: number, student: { name: string, phone: string, email: string, clazz: { id: number } }): Observable<Student> { return this.httpClient.put<Student>(`/student/${id}`, student); } ``` ### 單元測試 對`getById()`方法的測試: ```typescript +++ b/first-app/src/app/service/student.service.spec.ts @@ -4,6 +4,7 @@ import {StudentService} from './student.service'; import {MockApiTestingModule} from '../mock-api/mock-api-testing.module'; import {HttpClient} from '@angular/common/http'; import {getTestScheduler} from 'jasmine-marbles'; +import {randomNumber} from '@yunzhi/ng-mock-api'; describe('StudentService', () => { let service: StudentService; @@ -93,5 +94,21 @@ describe('StudentService', () => { console.log(jsonTest.name); // jsonTest.sayHello(); }); + + fit('getById', () => { + // 返回前面已經請求的數據(如有),避免產生數據污染。 + getTestScheduler().flush(); + + const id = randomNumber(); + let called = false; + service.getById(id).subscribe(student => { + called = true; + expect(student).toBeTruthy(); + }); + expect(called).toBeFalse(); + + getTestScheduler().flush(); + expect(called).toBeTrue(); + }); }); ``` 測試通過: ![image-20210609142349392](https://img.kancloud.cn/6d/4f/6d4f6a02634d77b7d5c7903c34450882_1050x290.png) 對`update`方法的測試: ```typescript +++ b/first-app/src/app/service/student.service.spec.ts @@ -5,6 +5,7 @@ import {MockApiTestingModule} from '../mock-api/mock-api-testing.module'; import {HttpClient} from '@angular/common/http'; import {getTestScheduler} from 'jasmine-marbles'; import {randomNumber} from '@yunzhi/ng-mock-api'; +import {randomString} from '@yunzhi/ng-mock-api/testing'; describe('StudentService', () => { let service: StudentService; @@ -110,5 +111,25 @@ describe('StudentService', () => { getTestScheduler().flush(); expect(called).toBeTrue(); }); + + fit('update', () => { + // 返回前面已經請求的數據(如有),避免產生數據污染。 + getTestScheduler().flush(); + + const id = randomNumber(); + let called = false; + service.update(id, { + name: randomString(), + email: randomString(), + phone: randomString(), + clazz: {id: randomNumber()}}).subscribe(student => { + called = true; + expect(student).toBeTruthy(); + }); + expect(called).toBeFalse(); + + getTestScheduler().flush(); + expect(called).toBeTrue(); + }); }); ``` ![image-20210609142817630](https://img.kancloud.cn/d6/13/d6138980324e789f916cca882eddeb0b_1230x310.png) ## 原型開發 更新學生與新增學生大同小異,不同的是更新學生時不能夠對學生的學號進行更新,原型代碼如下: ```html <form class="container-sm" (ngSubmit)="onSubmit()" [formGroup]="formGroup"> <div class="mb-3 row"> <label class="col-sm-2 col-form-label">名稱</label> <div class="col-sm-10"> <input type="text" class="form-control" formControlName="name"> <small class="text-danger" *ngIf="formGroup.get('name')!.invalid"> 名稱不能為空 </small> </div> </div> <div class="mb-3 row"> <label class="col-sm-2 col-form-label">學號</label> <div class="col-sm-10"> <input type="text" readonly class="form-control-plaintext" formControlName="number"> </div> </div> <div class="mb-3 row"> <label class="col-sm-2 col-form-label">手機號</label> <div class="col-sm-10"> <input type="text" class="form-control" formControlName="phone"> <small class="text-danger" *ngIf="formGroup.get('phone')!.invalid"> 手機號格式不正確 </small> </div> </div> <div class="mb-3 row"> <label class="col-sm-2 col-form-label">郵箱</label> <div class="col-sm-10"> <input type="text" class="form-control" formControlName="email"> </div> </div> <div class="mb-3 row"> <label class="col-sm-2 col-form-label">班級</label> <div class="col-sm-10"> <app-clazz-select formControlName="clazzId"></app-clazz-select> <small class="text-danger" *ngIf="formGroup.get('clazzId')!.invalid"> 必須選擇班級 </small> </div> </div> <div class="mb-3 row"> <div class="col-sm-10 offset-2"> <button appLoading class="btn btn-primary" [disabled]="formGroup.invalid || formGroup.pending"> <i class="fa fa-save"></i>保存 </button> </div> </div> </form> ``` 根據原型中使用到的組件屬性及方法,初始化組件如下: ```typescript +++ b/first-app/src/app/student/edit/edit.component.ts @@ -1,4 +1,7 @@ -import { Component, OnInit } from '@angular/core'; +import {Component, OnInit} from '@angular/core'; +import {FormControl, FormGroup, Validators} from '@angular/forms'; +import {YzValidators} from '../../yz-validators'; +import {YzAsyncValidators} from '../../yz-async-validators'; @Component({ selector: 'app-edit', @@ -6,10 +9,22 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./edit.component.css'] }) export class EditComponent implements OnInit { + formGroup: FormGroup; - constructor() { } + constructor(private yzAsyncValidators: YzAsyncValidators) { + this.formGroup = new FormGroup({ + name: new FormControl('', Validators.required), + number: new FormControl('', Validators.required, yzAsyncValidators.numberNotExist()), + phone: new FormControl('', YzValidators.phone), + email: new FormControl(), + clazzId: new FormControl(null, Validators.required) + }); + } ngOnInit(): void { } + onSubmit(): void { + + } } ``` ### 單元測試 在組件中注入的`YzAsyncValidators`異步驗證器依賴于后臺API,在測試過程中,我們使用一個假的后臺來提供后臺API: ```typescript +++ b/first-app/src/app/student/edit/edit.component.spec.ts @@ -1,6 +1,8 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {EditComponent} from './edit.component'; +import {MockApiTestingModule} from '../../mock-api/mock-api-testing.module'; +import {getTestScheduler} from 'jasmine-marbles'; describe('EditComponent', () => { let component: EditComponent; @@ -8,6 +10,7 @@ describe('EditComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ + imports: [MockApiTestingModule], declarations: [EditComponent] }) .compileComponents(); @@ -22,4 +25,15 @@ describe('EditComponent', () => { fit('should create', () => { expect(component).toBeTruthy(); }); + + /** + * 每個測試用例執行結束后,都執行一次此方法 + */ + afterEach(() => { + // 發送尚未發送的數據,可以避免兩次相近執行的單元測試不互相影響 + getTestScheduler().flush(); + + // 統一調用自動檢測功能 + fixture.autoDetectChanges(); + }); }); ``` ![image-20210609144053128](https://img.kancloud.cn/cf/4a/cf4a08fe72b7f28ffea5f87a49c96ed0_2430x844.png) 至此,整體的初始化工作宣告結束,下一節開始完成功能實現。 | 鏈接 | 名稱 | | ------------------------------------------------------------ | -------- | | [https://github.com/mengyunzhi/angular11-guild/archive/step7.7.1.zip](https://github.com/mengyunzhi/angular11-guild/archive/step7.7.1.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>

                              哎呀哎呀视频在线观看