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

                學生管理為學生模塊的第一個組件。所以在生成新增學生組件前,我們先在app根路徑使用`ng g m student`中生成了一個學生模塊。再使用`ng g c add`來生成添加學生組件。 ``` panjiedeMac-Pro:app panjie$ ng g m student CREATE src/app/student/student.module.ts (193 bytes) panjiedeMac-Pro:app panjie$ cd student/ panjiedeMac-Pro:student panjie$ ng g c add CREATE src/app/student/add/add.component.sass (0 bytes) CREATE src/app/student/add/add.component.html (18 bytes) CREATE src/app/student/add/add.component.spec.ts (607 bytes) CREATE src/app/student/add/add.component.ts (258 bytes) UPDATE src/app/student/student.module.ts (257 bytes) ``` 其實做到這里便可以繼續開發了。但為了更加的貼近于`更佳實踐`,我們首先做些重構的工作。 ## 剝離路由 正式開始本節內容以前,先給上一章班級管理補個刀。仔細觀察下 app模塊和klass模塊模塊我們會發現,這兩個模塊在路由的配置上有所不同: 在app模塊中,有一個專門來定義路由的app-routing.module.ts ![](https://img.kancloud.cn/ea/99/ea9956715f1d34f7cecfe55cff3c3706_326x74.png) 而在klass模塊中,我們并沒有專門的路由文件: ![](https://img.kancloud.cn/b9/ee/b9eec9b7a8fcdddd295995ac4235eed0_236x63.png) angular的[官方文檔](https://www.angular.cn/guide/router#milestone-2-routing-module)對這兩種方式分別進行描述,并指出并不強制使用哪種模式,但同時也指出在一個項目我們應該統一風格。要么將路由配置直接寫到模塊中,要么將路由配置統一寫到路由模塊中。在團隊的實際生產環境中我們更愿意將路由模塊寫到單獨的路由模塊中,這樣做最少有2個好處:① 減少模塊類的代碼量,更易讀;② 是否在某個模塊中配置了路由一目了解。 ### 重構klass模塊 首先,我們對歷史的klass模塊進行重構,進入klass文件夾并新建`klass-routing.module.ts`,然后將路由設置的信息由`klass/klass.module.ts`轉移到`klass-routing.module.ts`中。 klass/klass-routing.module.ts ``` import {RouterModule, Routes} from '@angular/router'; import {IndexComponent} from './index/index.component'; import {AddComponent} from './add/add.component'; import {EditComponent} from './edit/edit.component'; import {NgModule} from '@angular/core'; /*定義路由*/ const routes: Routes = [ { path: '', component: IndexComponent }, { path: 'add', component: AddComponent }, { path: 'edit/:id', component: EditComponent } ]; @NgModule({ imports: [RouterModule.forChild(routes)] }) export class KlassRoutingModule {} ``` 我們在此聲明的路由對應的組件均屬于`KlassModule`,所以想讓此路由信息生效則需要將其添加到對應的`KlassModule`中。在angular中,想讓其它模塊使用本模塊內部的東西,則需要將其添加到`export`中: klass/klass-routing.module.ts ``` @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule] ① }) export class KlassRoutingModule {} ``` * ① 將使用本模塊routes變量配置過的RouterModule拋出。 此操作的作用是:在`KlassRoutingModule`上捆綁`RouteModule`,其它模塊在`import KlassRoutingModule`時,將自動的引入`KlassRoutingModule`身上捆綁的`RouteModule`。 最后我們在KlassModule中引入該路由模塊,重構完畢。 klass/klass.module.ts ``` @NgModule({ declarations: [IndexComponent, AddComponent, EditComponent, TeacherSelectComponent], imports: [ CommonModule, FormsModule, ReactiveFormsModule, KlassRoutingModule ① ] }) export class KlassModule { } ``` * ① 引入KlassRoutingModule的同時,引用了其捆綁的`RouteModule`。該`RouteModule`已經配置了路由信息,進而使得路由信息在本模塊中生效。 補刀結束,回歸主題。 ### 新增student路由 參考剛剛補刀的過程為student模塊來建立單獨的路由模塊。 student/student-routing.module.ts ```javascript import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @NgModule({ exports: [RouterModule] }) export class StudentRoutingModule { } ``` 然后在student模塊中引入該路由模塊 student/student.module.ts ```javascript @NgModule({ declarations: [AddComponent], imports: [ CommonModule, StudentRoutingModule ] }) export class StudentModule { } ``` ## V層初始化 student/add/add.component.html ```html <h2>編輯教師①</h2> <form (ngSubmit)="onSubmit()"> <label>姓名:<input name="name"/></label> <label>學號:<input name="sno"/></label> <label>班級:todo?</label> <button>保存</button> </form> ``` * ① 此處筆者在復制內容時發生了錯誤,正常的標題為:添加學生 * ? 此處應該用班級列表組件,由于還不存在,所以我們用TODO來標記一下。 > 此處有錯誤 在進行初始化時不要怕錯,也不要怕界面難看。界面錯了我們后面會結合C層及單元測試進行修正,而界面的好看則應該是**集成測試**的任務而非當前初始化的任務。 ## 建立實體類 angular的cli除了可以幫助我們快速的建立模塊、組件以外還可以做很多我們想到的或是想不到的事實,比如創建實體。來到norm/entity文件夾,并執行`ng g class student`,則會自動生成實體及實體的測試文件。 norm/entity/student.ts (angular為我們生成的是student而不是Student,看來我們以前對Klass及Teacher的命名都錯了。。) ```javascript import {NgModule} from '@angular/core'; @NgModule({}) export class Student { id: number; name: string; sno: string; constructor(data???: { id?: number; name?: string; sno?: string }) { if (!data) { ? return; } this.id = data.id ? data.id : null; ? this.name = data.name ? data.name : ''; this.sno = data.sno ? data.sno : ''; } } ``` 在此,我們在構造函數中使用了一種更優的實踐。該方法將使得實例化該類具有高度的靈活性。 * ? 構造函數直接接收對象,而非某個字段。當實體屬性發生變動時整體項目的改動最小 * ? `?`表示此參數為可選參數。可以傳、也可以不傳 * ? 規避未傳data時可以造成的錯誤 * ? 按傳入的參數值賦初值或設置默認值 ### 單元測試 norm/entity/student.spec.ts ```javascript // @ts-ignore ? import {Student} from './student'; describe('Student', () => { fit('should create an instance', () => { expect(new Student()).toBeTruthy(); ? expect(new Student({})).toBeTruthy(); ? expect(new Student({id: 1, name: 'test', sno: '100021'})); ? expect(new Student({id: 1})).toBeTruthy(); ? expect(new Student({name: 'hello', id: 2, sno: '123'})).toBeTruthy(); ? expect(new Student({sno: '456'})).toBeTruthy(); ? }); }); ``` * ? 忽略IDE報的TS語法錯誤(實際上并沒有問題)。 * ? 支持空參數初始化 * ? 支持空object初始化 * ? 支持傳入所有的字段初始化 * ? 支持傳入個別字段初始化 * ? 支持調換字段的書寫順序初始化 * ? 支持只傳入個別非首參數初始化 ``` Chrome 78.0.3904 (Mac OS X 10.13.6): Executed 1 of 17 (skipped 16) SUCCESS (0.04 secs / 0.003 secs) TOTAL: 1 SUCCESS TOTAL: 1 SUCCESS ``` 如我們上面的單元測試所示,在student的構造函數中我們使用一種更優的方法后,在初始化Student時便更加靈活了。不僅如此,假設有一天有了新需求:需要為學生增加入學年份字段,那么只需要在構造函數中增加`year?:number`即可,而項目中的其它代碼我們完全不需要進行改動。 **小測試:** 分別為Student及Teacher類增加一個字段`createTime: number`,并將其添加到構造函數中,然后體現一下兩者的區別。 ## C層 student/add/add.component.ts ```javascript import {Component, OnInit} from '@angular/core'; import {Student} from '../../norm/entity/student'; import {FormControl, FormGroup} from '@angular/forms'; @Component({ selector: 'app-add', templateUrl: './add.component.html', styleUrls: ['./add.component.sass'] }) export class AddComponent implements OnInit { student: Student; formGroup: FormGroup; constructor() { } ngOnInit() { this.student = new Student(); this.formGroup = new FormGroup({ name: new FormControl(''), sno: new FormControl('') }); } onSubmit(): void { this.student = this.formGroup.value; console.log(this.student); } } ``` ### 修正V層 C層代碼完成后,我們繼續修正V層。將表單與C層中的屬性相關聯: student/add/add.component.ts ```html <h2>編輯教師</h2> <form (ngSubmit)="onSubmit()" [formGroup]="formGroup"> <label>姓名:<input name="name" formControlName="name"/></label> <label>學號:<input name="sno" formControlName="sno" /></label> <label>班級:todo</label> <button>保存</button> </form> ``` #### 單元測試 測試初始化 student/add/add.component.spec.ts ```javascript import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {AddComponent} from './add.component'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; describe('student/AddComponent', () => { let component: AddComponent; let fixture: ComponentFixture<AddComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [AddComponent], imports: [ ReactiveFormsModule ] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(AddComponent); component = fixture.componentInstance; fixture.detectChanges(); }); fit('should create', () => { expect(component).toBeTruthy(); }); }); ``` ### 重構測試公用類 在前面的測試中,我們開發了testing/FormTest來輔助進行一些表單的測試。在實際的使用中我們發現,進行任何表單的操作都需要一個`fixture`平具,在此我們將夾具剝離到構造函數中。 在testing/FormTest.ts的首部添加如下代碼: ```javascript /** * 表單測試 */ export class FormTest<T?> { private readonly? fixture: ComponentFixture<T?>; constructor(fixture: ComponentFixture<T?>) { this.fixture = fixture; } ``` * ? 在小容器ComponentFixture外面加了一個包裝FormTest。實際上能裝物質的還是ComponentFixture。一旦在包裝上規定了要裝個物質的種類,則包裝中的容器只能裝后該種類。 * ? 字段為只讀屬性。 在該文件的尾部我們加入以下方法: ```javascript /** * 設置input輸入的值 * @param cssSelector CSS選擇器 * @param value 值 */ setInputValue(cssSelector: string, value: string): boolean { return FormTest.setInputValue(this.fixture, cssSelector, value); ? } /** * 點擊某個按鈕 * @param cssSelector CSS選擇器 */ clickButton(cssSelector: string): boolean { return FormTest.clickButton(this.fixture, cssSelector);? } ``` * ? 不造重復的輪子,直接調用原來存在的靜態方法。 ## 完善測試 student/add/add.component.spec.ts ```javascript describe('student/AddComponent', () => { let component: AddComponent; let fixture: ComponentFixture<AddComponent>; let formTest: FormTest<AddComponent>; ? ... beforeEach(() => { ? fixture = TestBed.createComponent(AddComponent); component = fixture.componentInstance; fixture.detectChanges(); formTest = new FormTest(fixture); ? }); /** * 1. 向表單中輸入值 * 2. 點擊保存按鈕 * 3. 斷言輸入的值傳入到了C層 */ fit('should create', () => { expect(component).toBeTruthy(); formTest.setInputValue('input[name="name"]', 'testname'); ① formTest.setInputValue('input[name="sno"]', 'testno'); ① formTest.clickButton('button[type="submit"]'); ② fixture.detectChanges(); ③ expect(component.student.name).toEqual('testname'); ④ expect(component.student.sno).toEqual('testno'); ④ }); ``` * ? 將一些測試用例可能會公共的對象,抽離到方法上層。 * ? 在beforeEach中出現的代碼將在每次測試用例被執行**前**,執行一次。 ``` LOG: Object{name: 'testname', sno: 'testno'} Chrome 78.0.3904 (Mac OS X 10.13.6): Executed 0 of 17 SUCCESS (0 secs / 0 secs) Chrome 78.0.3904 (Mac OS X 10.13.6): Executed 1 of 17 (skipped 16) SUCCESS (0.14 secs / 0.108 secs) TOTAL: 1 SUCCESS TOTAL: 1 SUCCESS ``` ![](https://img.kancloud.cn/e5/5a/e55a6c6f851089990df246e29ee34757_608x95.png) # 參考文檔 | 名稱 | 鏈接 | 預計學習時長(分) | | --- | --- | --- | | 源碼地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step4.5.1](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step4.5.1) | \- |
                  <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>

                              哎呀哎呀视频在线观看