和开发其它组件一样,我们首先进行一些初始化的工作。使用webstorm打开前台项目,并使用终端进入src/app/student文件夹,使用`ng g c index`自动生成学生模块中的index组件: ``` panjiedeMac-Pro:student panjie$ pwd /Users/panjie/github/mengyunzhi/spring-boot-and-angular-guild/web-app/src/app/student panjiedeMac-Pro:student panjie$ ng g c index CREATE src/app/student/index/index.component.sass (0 bytes) CREATE src/app/student/index/index.component.html (20 bytes) CREATE src/app/student/index/index.component.spec.ts (621 bytes) CREATE src/app/student/index/index.component.ts (266 bytes) UPDATE src/app/student/student.module.ts (654 bytes) panjiedeMac-Pro:student panjie$ ``` 打开`src/app/student/index/index.component.spec.ts`将`describe`修改为`fdescribe`,并对应修改描述信息: ``` fdescribe('Student -> IndexComponent', () => { ``` 在终端中使用`ng test`来启动单元测试,系统将自动打开chrome并展示当前组件的效果。 # 原型 参考前面在纸上画的原型,编辑V层文件进行原型的开发: student/index/index.component.html ```javascript <form> <label>姓名:<input type="text" /></label> <label>学号:<input type="text" /></label> <label>班级:<app-klass-select></app-klass-select></label> </form> <table> <tr> <th>选择</th> <th>序号</th> <th>姓名</th> <th>学号</th> <th>班级</th> <th>操作</th> </tr> <tr> </tr> </table> <div>第4/14页 每页10条 首页 上一页 1 2 3 下一页 尾页</div> ``` ## 单元测试 在V层中引入了app-klass-select,增加关联的测试信息: student/index/index.component.spec.ts ```javascript beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [IndexComponent, KlassSelectComponent✚], imports: [CoreModule, HttpClientTestingModule] ✚ }) .compileComponents(); })); ``` 本例中我们引用了班级选择组件,进而需要在单元测试中引用该组件对应的测试代码,否则班级组件中的数据便是个空值,而**我们太希望在引用其它组件时可以少写或是不写其它组件需要的相应测试代码了!**。我们希望在本组件的单元测试中专注于本组件的功能实现,而尽量少的却关心其它的组件是如何工作的。 ## C层 按需求,我们在C层中需要定义查询参数及分页数据两个属性: student/index/index.component.ts ```javascript export class IndexComponent implements OnInit { /* 查询参数 */ params① = { page: 0, size: 2, klass: Klass, name: new FormControl(), sno: new FormControl() }; /* 分页数据 */ pageStudent② = { totalPages: 0, content: new Array<Student>() }; constructor() { } ngOnInit() { this.pageStudent.totalPages = 2; //③ this.pageStudent.content.push( //③ new Student( { id: 1, name: 'testNName', sno: 'testSno', klass: new Klass(1, 'testKlass', null) })); } /* 查询 */ onQuery() { console.log('query'); } } ``` * ① 5项查询参数。用`=`表示赋初值 * ② 分页数据,包含基本的两项数据信息:总页数及当前数据。字段名根据后台的返回格式进行命名。 * ③ 初始化分页信息及当前页数据 ## V层 按C层的属性值进行V层的数据绑定: ```javascript <form (ngSubmit)="onQuery()"①> <label>姓名:<input [formControl]="params.name"② type="text" /></label> <label>学号:<input [formControl]="params.sno"② type="text" /></label> <label>班级:<app-klass-select [klass]="params.klass"③></app-klass-select></label> <button type="submit">查询</button> </form> <table> <tr> <th>选择</th> <th>序号</th> <th>姓名</th> <th>学号</th> <th>班级</th> <th>操作</th> </tr> <tr *ngFor="let student of pageStudent.content; index as index">④ <td></td> <td>{{index + 1}}</td> <td>{{student.name}}</td> <td>{{student.sno}}</td> <td>{{student.klass.name}}</td> <td></td> </tr> </table> <div *ngIf="pageStudent">第{{params.page}}/{{pageStudent.totalPages}}页 每页{{params.size}}条 首页 上一页 1 2 3 下一页 尾页</div>⑤ ``` * ① 提交表单绑定onQuery方法 * ② 绑定表单项 * ③ 绑定klass * ④ 绑定当前页学生 * ⑤ 绑定分页信息 ## 单元测试 同步修正单元测试的引入: student/index/index.component.spec.ts ```javascript beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [IndexComponent, KlassSelectComponent], imports: [ ReactiveFormsModule, FormsModule, CoreModule, HttpClientTestingModule] }) .compileComponents(); })); ``` ![](https://img.kancloud.cn/ee/d0/eed06a19196347a698bd0622e59f2f12_590x122.png) # 初始化M层 按上一节添加学生的理论,请求学生的分页数据功能应该抽离到M层。 ![](https://img.kancloud.cn/f5/2c/f52c5e65392968e1bdaedb311f18c9e8_505x263.png) 如上图所示,C层在初始化时将调用M层page方法来获取默认数据;点用户点击查询按钮时,将附带查询参数调用M层page方法来获取查询页的数据。 service/student.service.ts ```javascript export class StudentService { constructor(private httpClient: HttpClient) { } /** * 分页 * @param params name:名称,sno:学号,klassId:班级ID,page:第几页,size:每页大小 */ page(params: {name?①: string, sno?①: string, klassId?①: number, page?: number, size?: number} ): Observable<{totalPages: number, content: Array<Student>}②> { return null; } ``` * ① 使用`?`标记可选参数 * ② 定义返回值类型 ## 增加测试方法 对应增加page方法的测试: service/student.service.spec.ts ```javascript describe('service -> StudentService', () => { let service: StudentService; beforeEach(() => TestBed.configureTestingModule({ imports: [HttpClientTestingModule] })); beforeEach(() => { service = TestBed.get(StudentService); }); /* 分页测试 */ it('page', () => { expect(service).toBeTruthy(); }); ``` # 在组件中引入M层 有了M层后,我们在C层中进行引入: student/index/index.component.ts ```javascript export class IndexComponent implements OnInit { /* 查询参数 */ params = { page: 0, size: 2, klass: Klass, name: new FormControl(), sno: new FormControl() }; /* 分页数据 */ pageStudent = { totalPages: 0, content: new Array<Student>() }; constructor(private studentService: StudentService★) { } ``` # 总结 本小节我们主要进行组件的CV初始化,以及该组件对应调用M层的初始化。接下来,按调用顺序由后向前分步进行开发。 # 参考文档 | 名称 | 链接 | 预计学习时长(分) | | --- | --- | --- | | 源码地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step4.6.6](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step4.6.6) | - |