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

                當所有的盲點都被消除后,最后的功能完成已然成為了最很簡單的一環。 ## 服務與實體 在生產項目中,往往會使用服務來完成與后臺的交互工作,這在組件需要處理一些邏輯功能,或是需要與其它的服務進行交互時特別重要。 為此首先來到`src/app/service`文件夾創建StudentService: ```bash panjie@panjies-iMac service % pwd /Users/panjie/github/mengyunzhi/angular11-guild/first-app/src/app/service panjie@panjies-iMac service % ng g s student CREATE src/app/service/student.service.spec.ts (362 bytes) CREATE src/app/service/student.service.ts (136 bytes) ``` 然后該服務器添加新增學生方法: ```typescript import {Injectable} from '@angular/core'; import {Observable, of} from 'rxjs'; @Injectable({ providedIn: 'root' }) export class StudentService { constructor() { } /** * 新增學生. */ add(): Observable<any> { return of(); } } ``` 為了在后續的其它學生相關組件中更好的處理學生這個實體,在繼續進行之前,還需要來到`src/app/entity`文件夾,新建一個學生實體: ```bash panjie@panjies-iMac entity % pwd /Users/panjie/github/mengyunzhi/angular11-guild/first-app/src/app/entity panjie@panjies-iMac entity % ng g class student CREATE src/app/entity/student.spec.ts (158 bytes) CREATE src/app/entity/student.ts (25 bytes) ``` 然后在學生實體中定義屬性、加入構造函數以及特別重要的注釋: ```typescript import {Clazz} from './clazz'; /** * 學生. */ export class Student { id: number; /** * 姓名. */ name: string; /** * 學號. */ number: string; /** * 手機號. */ phone: string; /** * email. */ email: string; /** * 班級. */ clazz: Clazz; constructor(data = {} as { id?: number, name?: string, number?: string, phone?: string, email?: string, clazz?: Clazz }) { this.id = data.id as number; this.name = data.name as string; this.number = data.number as string; this.phone = data.phone as string; this.email = data.email as string; this.clazz = data.clazz as Clazz; } } ``` 有了Student實體后,開始完成StudentService中的`add`方法。 ## Add方法 添加學生時,需要接收姓名、學號、手機號、email、班級信息,故對參數初始化如下: ```typescript +++ b/first-app/src/app/service/student.service.ts @@ -1,5 +1,7 @@ import {Injectable} from '@angular/core'; import {Observable, of} from 'rxjs'; +import {Student} from '../entity/student'; +import {Clazz} from '../entity/clazz'; @Injectable({ providedIn: 'root' @@ -12,7 +14,14 @@ export class StudentService { /** * 新增學生. */ - add(): Observable<any> { - return of(); + add(data: ①{name: string, number: string, phone: string, email: string, clazzId: number}): Observable<Student> ②{ + const student = new Student({ + name: data.name, + number: data.number, + phone: data.phone, + email: data.email, + clazz: new Clazz({id: data.clazzId}) + })③; + return of(student)④; } } ``` - ① 當參數類型設置為`{}`,以后擴展增加新字段時更方便。 - ② 新增成功后臺將返回新增后的學生信息。 - ③ 使用`new Student()`的方法讓編譯器來對語法進行校驗,防止不小心出現的拼寫錯誤。 - ④ 在沒有MockApi以前,暫時返回student。 ## MockApi 添加學生的API與添加教師、添加班級的Api一致,在此即使不給出后臺API的具體說明,相信我們也能夠書寫出正確的請求: ```typescript +++ b/first-app/src/app/mock-api/student.mock.api.ts @@ -1,5 +1,6 @@ -import {ApiInjector, MockApiInterface, RequestOptions} from '@yunzhi/ng-mock-api'; +import {ApiInjector, MockApiInterface, randomNumber, RequestOptions} from '@yunzhi/ng-mock-api'; import {HttpParams} from '@angular/common/http'; +import {Student} from '../entity/student'; /** * 學生模擬API. @@ -21,6 +22,15 @@ export class StudentMockApi implements MockApiInterface { return false; } } - }]; + }, { + method: 'POST', + url: '/student', + result: ((urlMatches: string[], options: RequestOptions) => { + const student = options.body as Student; + // 模擬保存成功后生成ID + student.id = randomNumber(); + return student; + }) + } + ]; } } ``` 如果你想使自己的MockApi能夠像真實的Api一樣可以校驗信息,則還可以適當的加入一些斷言,比如在新增學生時,要求必須傳入預新增學生的基本字段: ```typescript result: ((urlMatches: string[], options: RequestOptions) => { const student = options.body as Student; + Assert.isString(student.phone, student.email, student.number, student.name, '學生的基本信息未傳全'); + Assert.isNumber(student.clazz.id, '班級id校驗失敗'); student.id = randomNumber(); return student; }) ``` 此時將對該模擬后臺發起請求時,如果未傳入相應的信息,`HttpClient`則會接收到了一個`error`。我們借用StudentService的測試文件來測試下發起請求時如果沒有傳入特定的字段,怎樣來獲取這個`error`: ```typescript +++ b/first-app/src/app/service/student.service.spec.ts @@ -1,16 +1,26 @@ -import { TestBed } from '@angular/core/testing'; +import {TestBed} from '@angular/core/testing'; -import { StudentService } from './student.service'; +import {StudentService} from './student.service'; +import {MockApiTestingModule} from '../mock-api/mock-api-testing.module'; +import {HttpClient} from '@angular/common/http'; describe('StudentService', () => { let service: StudentService; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ + imports: [ + MockApiTestingModule + ] + }); service = TestBed.inject(StudentService); }); - it('should be created', () => { + fit('should be created', () => { expect(service).toBeTruthy(); // TestBed.inject()可獲取到當前動態測試模塊的所有服務 + const httpClient = TestBed.inject(HttpClient); + httpClient.post('/student', {}) + .subscribe(success => console.log('success', success), + error => console.log('error', error)); }); }); ``` 當MockApi發生異常時,將會觸發`subscribe`中的`error`方法,這與正常的后臺請求報錯的方式一致: ![image-20210414151827586](https://img.kancloud.cn/60/58/6058f8e3ca38a9f28aedc6e967d098ef_884x278.png) ## 返回預請求 有了MockApi后,我們在StudentService中發起這個請求: ```typescript +++ b/first-app/src/app/service/student.service.ts @@ -2,13 +2,14 @@ import {Injectable} from '@angular/core'; import {Observable, of} from 'rxjs'; import {Student} from '../entity/student'; import {Clazz} from '../entity/clazz'; +import {HttpClient} from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class StudentService { - constructor() { + constructor(private httpClient: HttpClient) { } /** @@ -22,6 +23,6 @@ export class StudentService { email: data.email, clazz: new Clazz({id: data.clazzId}) }); - return of(student); + // 將預請求信息返回 + return this.httpClient.post<Student>('/student', student); } ``` ## 組件調用 其它工作準備完畢后,組件調用便成了最簡單的一環: ```typescript +++ b/first-app/src/app/student/add/add.component.ts @@ -2,7 +2,7 @@ import {Component, OnInit} from '@angular/core'; import {FormControl, FormGroup, Validators} from '@angular/forms'; import {YzValidators} from '../../yz-validators'; import {YzAsyncValidators} from '../../yz-async-validators'; -import {HttpClient} from '@angular/common/http'; +import {StudentService} from '../../service/student.service'; @Component({ selector: 'app-add', @@ -12,7 +12,7 @@ import {HttpClient} from '@angular/common/http'; export class AddComponent implements OnInit { formGroup: FormGroup; - constructor(private httpClient: HttpClient, private yzAsyncValidators: YzAsyncValidators) { + constructor(private studentService: StudentService, private yzAsyncValidators: YzAsyncValidators) { this.formGroup = new FormGroup({ name: new FormControl('', Validators.required), number: new FormControl('', Validators.required, yzAsyncValidators.numberNotExist()), @@ -26,6 +26,15 @@ export class AddComponent implements OnInit { } onSubmit(): void { - console.log('submit'); + const student = this.formGroup.value① as② { + name: string, + number: string, + phone: string, + email: string, + clazzId: number + }; + this.studentService.add(student) + .subscribe(success => console.log('保存成功', success), + error => console.log('保存失敗', error)); } } ``` - ① 可以使用`FormGroup.value`來獲取整個`FormGroup`中所有`FormControl`的值 - ② 需要注意的是,這個雖然可能使用`as`將其轉換為任意值,但這種轉換也帶來了一定的風險,比如我們在初始化`FormGroup`時,誤把`email`寫成了`emial`。 填寫完所有的字段后,保存成功。 ![image-20210414152940550](https://img.kancloud.cn/77/08/7708033943d1e8dff785cb9c8b917cfe_1558x358.png) 其實有時候我們很難將`onSubmit()`一次性的書寫成功,比如我們以后需要加入保存成功后路由的跳轉信息。所以在開發過程中往往需要屢次點擊保存按鈕,而點擊該按鈕前卻需要將表單的所有字段輸全,這明顯是個重復的勞動,做為**懶人**的我們怎么能允許些類事情的發生。 如果在點擊保存按鈕前這些信息全部都為我們自動填寫好,那該多好呀。?? 還不快用單元測試? ```typescript +++ b/first-app/src/app/student/add/add.component.spec.ts @@ -8,6 +8,8 @@ import {getTestScheduler} from 'jasmine-marbles'; import {Observable, of} from 'rxjs'; import {map} from 'rxjs/operators'; import {LoadingModule} from '../../directive/loading/loading.module'; +import {randomString} from '@yunzhi/ng-mock-api/testing'; +import {randomNumber} from '@yunzhi/ng-mock-api'; describe('student -> AddComponent', () => { let component: AddComponent; @@ -40,6 +42,24 @@ describe('student -> AddComponent', () => { fixture.autoDetectChanges(); }); + fit('自動填充要新建的學生數據', () => { + // 固定寫法 + getTestScheduler().flush(); + fixture.detectChanges(); + + component.formGroup.setValue({ + name: randomString('姓名'), + number: randomNumber().toString(), + phone: '13900000000', + email: '123@yunzhi.club', + clazzId: randomNumber(10) + }); + + // 固定寫法 + getTestScheduler().flush(); + fixture.autoDetectChanges(); + }); + it('理解map操作符', () => { // 數據源發送數據1 const a = of(1) as Observable<number>; ``` 在此單元測試代碼的支持上,我們再也不必手動地填寫這些數據了: ![image-20210414154202408](https://img.kancloud.cn/11/3b/113b3b3cdc5962ea48cf467b48cbdd08_2276x308.png) 這絕對是個提升生產力的好方法。好了,就到這里,休息一會。 | 名稱 | 鏈接 | | ----------------- | ------------------------------------------------------------ | | HTMLElement | [https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement) | | HTMLButtonElement | [https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement) | | 本節源碼 | [https://github.com/mengyunzhi/angular11-guild/archive/step7.2.6.zip](https://github.com/mengyunzhi/angular11-guild/archive/step7.2.6.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>

                              哎呀哎呀视频在线观看