本节我们尝试一次删除多个学生。 ## 原型 在原型的,则一定要先开发原型!是新手我们应该这样,是老手更应该这样。 若要一次删除多个学生,则需要为每条记录前加一个checkbox选择框。 ```html +++ b/first-app/src/app/student/student.component.html @@ -7,6 +7,7 @@ <table class="table table-striped mt-2"> <thead> <tr class="table-primary"> + <th>选择</th> <th>序号</th> <th>姓名</th> <th>学号</th> @@ -18,6 +19,7 @@ </thead> <tbody> <tr *ngFor="let student of pageData.content; index as index"> + <td><input type="checkbox"></td> <td>{{index + 1}}</td> <td>{{student.name}}</td> <td>{{student.number}}</td> ``` ![image-20210607155806991](https://img.kancloud.cn/ba/17/ba170e1455c5f7756f273d955c9eb270_1146x320.png) 接着再增加一个删除全部的按钮: ```html +++ b/first-app/src/app/student/student.component.html @@ -1,6 +1,7 @@ <div class="row"> <div class="col-12 text-right"> <a class="btn btn-primary mr-2" routerLink="./add"><i class="fas fa-plus"></i>新增</a> + <button class="btn btn-danger mr-2" type="button"><i class="fas fa-trash-alt"></i>删除</button> </div> </div> ``` ![image-20210607160000952](https://img.kancloud.cn/79/45/7945392870548007761bb6682b1ed7e6_798x160.png) ## 功能 实现功能的方式有很多种,我们在此使用一个比较容易理解的。 - ①首先在C层中增加一个数组,用于存待删除学生的**索引值**。注意,这里我们未使用`ID`,因为通过索引值能够轻松的找到`ID`。 - ②某个`checkbox`并点击时,将索引值传给C层。C层根据索引在数组中查找,如果查到了,就将其由数组中删除;如果没有找到,就将其添加到数组中。 - ③点击删除按钮时,如果待删除的数组为空,则弹出提示框;如果数组不为空,则按索引值依次取出待删除的ID,并将其传入M层。 ### ①初始化数组 ```typescript +++ b/first-app/src/app/student/student.component.ts @@ -15,6 +15,8 @@ export class StudentComponent implements OnInit { page = 0; size = environment.size; + beDeletedIndexes = new Array<number>(); + constructor(private studentService: StudentService) { } ``` ### ②传值 ```html +++ b/first-app/src/app/student/student.component.html @@ -20,7 +20,7 @@ </thead> <tbody> <tr *ngFor="let student of pageData.content; index as index"> - <td><input type="checkbox"></td> + <td><input type="checkbox" (click)="onCheckboxClick(index)"></td> <td>{{index + 1}}</td> <td>{{student.name}}</td> <td>{{student.number}}</td> ``` C层: ```typescript /** * checkbox被点击 * @param index 索引值 */ onCheckboxClick(index: number): void { if (this.beDeletedIndexes.indexOf(index) === -1) { this.beDeletedIndexes.push(index); } else { this.beDeletedIndexes = this.beDeletedIndexes.filter(i => i !== index); } } ``` `filter()`是数组的一个内置方法,它的作用是将数组中的数据依次进行过滤,保留符合条件的,移除不符合条件的,比如: ![image-20210607162309892](https://img.kancloud.cn/e5/c7/e5c7052a94407b6bc37a335790010898_968x178.png) 上述方法的作用时将数据中的不等3的数据保留。 ### ③点击删除按钮 ```html - <button class="btn btn-danger mr-2" type="button"><i class="fas fa-trash-alt"></i>删除</button> + <button class="btn btn-danger mr-2" type="button" (click)="onBatchDeleteClick()"><i class="fas fa-trash-alt"></i>删除</button> </div> ``` C层方法: ```typescript + /** + * 批量删除按钮被点击 + */ + onBatchDeleteClick(): void { + if (this.beDeletedIndexes.length === 0) { + Report.warning('出错啦', '请先选择要删除的学生', '返回'); + } + } } ``` - Report是notiflix提供的另一个方法,详情请自行查阅官方文档。 ![image-20210607163337933](https://img.kancloud.cn/8f/ef/8fef3e1ed931effdb2d93f77c60bfb42_1552x514.png) 完成功能: ```typescript +++ b/first-app/src/app/student/student.component.ts onBatchDeleteClick(): void { if (this.beDeletedIndexes.length === 0) { Report.warning('出错啦', '请先选择要删除的学生', '返回'); + } else { + Confirm.show('请确认', '该操作不可逆', '确认', '取消', + () => { + // 根据index获取ids + const ids = [] as number[]; + this.beDeletedIndexes.forEach(index => { + ids.push(this.pageData.content[index].id); + }); + // 调用批量删除 + this.studentService.batchDelete(ids) + .subscribe(() => { + this.beDeletedIndexes = []; + this.loadData(this.page); + }); + }); } } } ``` 最后,在M层初始化个方法,在方法中直接返回供开发用的可订阅对象: ```typescript +++ b/first-app/src/app/service/student.service.ts @@ -47,4 +47,12 @@ export class StudentService { .append('size', size.toString()); return this.httpClient.get<Page<Student>>('/student/pageOfCurrentTeacher', {params: httpParams}); } + + /** + * 批量删除 + * @param ids 学生ID数组 + */ + batchDelete(ids: number[]): Observable<void> { + return of(undefined); + } } ``` 此时,点击删除按钮时将弹出确认对话框,点击确认后将触发M层的方法。并在删除成功后重新向后台发起请求,获取最新的分页数据。 ![image-20210607154235559](https://img.kancloud.cn/3e/50/3e50225c687cfed9d4796bbffc74171c_908x338.png) ## M层 M层负责与API交互,批量删除API如下: ```bash DELETE /student/batchDeleteIds ``` 接收的请求参数为:`ids`,类型为数组。 根据上述信息建立MockApi如下: ```typescript +++ b/first-app/src/app/mock-api/student.mock.api.ts @@ -70,6 +70,14 @@ export class StudentMockApi implements MockApiInterface { }, { method: 'DELETE', url: '/student/(\\d+)' + }, { + method: 'DELETE', + url: '/student/batchDeleteIds', + result: ((urlMatches: any, options: RequestOptions) => { + const httpParams = options.params as HttpParams; + const ids = httpParams.getAll('ids'); + Assert.isArray(ids, '未接收到ids'); + }) } ]; } ``` 单元测试: ```typescript + fit('batchDeleteIds', () => { + const ids = [1, 2, 3]; + let called = false; + service.batchDelete(ids) + .subscribe(() => { + called = true; + }); + expect(called).toBeFalse(); + getTestScheduler().flush(); + expect(called).toBeTrue(); + }); + ``` 完成方法: ```typescript +++ b/first-app/src/app/service/student.service.ts @@ -53,6 +53,7 @@ export class StudentService { * @param ids 学生ID数组 */ batchDelete(ids: number[]): Observable<void> { - return of(undefined); + const stringIds = ids.map(id => id.toString()); + return this.httpClient.delete<void>('/student/batchDeleteIds', {params: {ids: stringIds}}); } } ``` 继`filter()`、`sort()`方法后,我们刚刚又使用了`map()`方法对数组中的各项进行了转换。 ![image-20210607170131502](https://img.kancloud.cn/31/44/31449720f3e2b2dbaeb22e1a13a1a9aa_902x262.png) 单元测试通过,说明方法请求正确。 ## 本节作业 Array中除`filter`、`map`外,还存在常用的`sort`等方法,请参阅官方文档进一步学习。 | 链接 | 名称 | | ------------------------------------------------------------ | -------- | | [https://github.com/mengyunzhi/angular11-guild/archive/step7.5.2.zip](https://github.com/mengyunzhi/angular11-guild/archive/step7.5.2.zip) | 本节源码 | | [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) | map() | | [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) | sort() | | [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) | filter() | | [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) | Array |