本节我们完成教师管理中的最后一个功能模块 ---- 删除教师。
相对添加教师、编辑教师模块,删除教师相对要简单一些。删除功能直接集成在了教师列表模块中,仅仅作为教师列表App组件的功能出现。
接下来让我们共同实现教师的删除功能。
## type定义类型
在上节中,大家完成了App组件`teachers`属性类型由`any`改写的工作,参考答案如下:
第一种方式:
```typescript
+++ b/first-app/src/app/app.component.ts
@@ -8,7 +8,12 @@ import {HttpClient} from '@angular/common/http';
})
export class AppComponent implements OnInit {
// 初始化教师数组
- teachers = [] as any[];
+ teachers = [] as {
+ name: string,
+ username: string,
+ email: string,
+ sex: true
+ }[];
constructor(private httpClient: HttpClient) {
console.log(httpClient);
@@ -18,7 +23,12 @@ export class AppComponent implements OnInit {
* 组件初始化完成后将被自动执行一次
*/
ngOnInit(): void {
- this.httpClient.get<[]>('http://angular.api.codedemo.club:81/teacher')
+ this.httpClient.get<{
+ name: string,
+ username: string,
+ email: string,
+ sex: true
+ }[]>('http://angular.api.codedemo.club:81/teacher')
.subscribe(teachers => this.teachers = teachers);
}
}
```
第二种方式:
```typescript
+++ b/first-app/src/app/app.component.ts
@@ -8,7 +8,7 @@ import {HttpClient} from '@angular/common/http';
})
export class AppComponent implements OnInit {
// 初始化教师数组
- teachers = [] as any[];
+ teachers = [] as Teacher[];
constructor(private httpClient: HttpClient) {
console.log(httpClient);
@@ -18,7 +18,17 @@ export class AppComponent implements OnInit {
* 组件初始化完成后将被自动执行一次
*/
ngOnInit(): void {
- this.httpClient.get<[]>('http://angular.api.codedemo.club:81/teacher')
+ this.httpClient.get<Teacher[]>('http://angular.api.codedemo.club:81/teacher')
.subscribe(teachers => this.teachers = teachers);
}
}
+
+/**
+ * 定义一个类型
+ */
+type Teacher = {
+ name: string,
+ username: string,
+ email: string,
+ sex: true
+};
```
教程中,我们基于第二种方式继续向下进行。
## 触发方法
用户点击删除时,需要对应触发C层的方法来完成删除功能。Angular中每个html元素都添加了`click`事件,使用方法如下:
```html
+++ b/first-app/src/app/app.component.html
@@ -17,7 +17,7 @@
<td>{{ teacher.username }}</td>
<td>{{ teacher.email }}</td>
<td *ngIf="teacher.sex; else femaleBlock">男</td>
- <td>删除</td>
+ <td (click)="C层对应的方法()">删除</td>
</tr>
</tbody>
</table>
```
此时当我们点击`td`元素时,则C对应的方法将被触发。比如我们在C层定义如下方法:
```typescript
+++ b/first-app/src/app/app.component.ts
@@ -21,6 +21,10 @@ export class AppComponent implements OnInit {
this.httpClient.get<Teacher[]>('http://angular.api.codedemo.club:81/teacher')
.subscribe(teachers => this.teachers = teachers);
}
+
+ onDelete(): void {
+ console.log('onDelete called');
+ }
}
/**
```
将`fdescript`移动到`src/app/app.component.spec.ts`中,并使用`ng t`启动系统:
![image-20210227181612793](https://img.kancloud.cn/b9/59/b959a7ea7d9ceddabf0a06672a4140c0_1130x344.png)
控制台发生了如上错误,它的意思是说当前测试模块并不认识`<router-outlet></router-outlet>`,解决该问题需要我们前面学习过的两个知识点:
1. 我们在前面提起过,提供`<router-outlet></router-outlet>`的provider为`RouterModule`
2. `RouterModule`仅用于`ng s`中,对应在单元测试`ng t`中应该使用`RouterTestingModule`来替换`RouterModule`.
```typescript
+++ b/first-app/src/app/app.component.spec.ts
@@ -1,12 +1,14 @@
import {TestBed} from '@angular/core/testing';
import {AppComponent} from './app.component';
import {HttpClientModule} from '@angular/common/http';
+import {RouterTestingModule} from '@angular/router/testing';
-describe('AppComponent', () => {
+fdescribe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
- HttpClientModule
+ HttpClientModule,
+ RouterTestingModule
],
declarations: [
AppComponent
```
![image-20210227182101022](https://img.kancloud.cn/67/58/67585c86b2ae2276f325fe06eebfa3a0_2288x500.png)
## 对接后台
我们为大家提供的删除接口如下:
```bash
DELETE /teacher/{id}
```
将要删除教师的ID使用delete方法向后台发起请求则可以完成对应教师的删除功能。在请求所有教师时,后台同样携带了ID属性。
```bash
GET /teacher
```
| **类型Type** | **名称Name** | **描述Description** | **类型Schema** |
| ------------ | ------------ | ------------------- | ------------------------------------------------------------ |
| Response | | | `[{id: number, username: string, name: string, email: string, sex: boolean}, {...}]` |
我们在`Teacher`类型中增加ID属性,并将其应用到V层:
```typescript
+++ b/first-app/src/app/app.component.ts
@@ -31,6 +31,7 @@ export class AppComponent implements OnInit {
* 定义一个类型
*/
type Teacher = {
+ id: number,
name: string,
username: string,
email: string,
```
```html
+++ b/first-app/src/app/app.component.html
@@ -17,7 +17,7 @@
<td>{{ teacher.username }}</td>
<td>{{ teacher.email }}</td>
<td *ngIf="teacher.sex; else femaleBlock">男</td>
- <td (click)="onDelete()">删除</td>
+ <td (click)="onDelete(teacher.id)">删除</td>
</tr>
</tbody>
</table>
```
然后继续在C层的onDelete方法中添加接收教师ID的参数:
```typescript
+++ b/first-app/src/app/app.component.ts
@@ -22,8 +22,8 @@ export class AppComponent implements OnInit {
.subscribe(teachers => this.teachers = teachers);
}
- onDelete(): void {
- console.log('onDelete called');
+ onDelete(id: number): void {
+ console.log('onDelete called', id);
}
}
```
如此以来,在V层点击`删除`时C层便可以接收到预删除教师的ID值。
![image-20210227191537256](https://img.kancloud.cn/63/21/63212354b009daf7606e9b679fe97a13_852x230.png)
接下来我们完成删除功能:
```typescript
+++ b/first-app/src/app/app.component.ts
@@ -22,8 +22,11 @@ export class AppComponent implements OnInit {
.subscribe(teachers => this.teachers = teachers);
}
- onDelete(): void {
- console.log('onDelete called');
+ onDelete(id: number): void {
+ const url = `http://angular.api.codedemo.club:81/teacher/${id}`; 👈
+ this.httpClient.delete(url)
+ .subscribe(data => console.log('删除成功'),
+ error => console.log('删除失败', error));
}
}
```
- 注意:我们在此使用是`` ` ``而非`'`,该按键在键盘上位于按键`1`的左侧。使用````` ``` 可以在其中以`${xxx}`的方式快速嵌入变量。`` `123${a}` `` 等价于`'123' + a`。👈
删除成功后调用`ngOnInit`方法重新请求数据:
```typescript
+++ b/first-app/src/app/app.component.ts
@@ -25,7 +25,7 @@ export class AppComponent implements OnInit {
onDelete(id: number): void {
const url = `http://angular.api.codedemo.club:81/teacher/${id}`;
this.httpClient.delete(url)
- .subscribe(data => console.log('删除成功'),
+ .subscribe(👉() => this.ngOnInit(),
error => console.log('删除失败', error));
}
}
```
- 👉 当请求没有返回值时,此处的参数留空即可,但不能省略`()`
> **注意**:受后台限制,你无法删除ID为1,2的教师预留数据。
我们在Add组件中随意添加一个测试数据:
![image-20210227192648731](https://img.kancloud.cn/2a/8c/2a8ccd81b15d16c050a6a16fd6323776_2204x302.png)
然后点击其删除按扭后:
![image-20210227192753889](https://img.kancloud.cn/2e/a4/2ea4f5c9aab4a97cd73d44ddf8df83cb_2190x198.png)
测试成功。
## 本节作业
1. 将前面章节中使用到字符串拼接变更为`` ` ` ``的形式。
2. 尝试删除教师张三、教师李四,并在控制台中查看错误。
| 名称 | 地址 | 备注 |
| ------------------ | ------------------------------------------------------------ | ---- |
| 绑定语法 | [https://angular.cn/guide/binding-syntax#binding-types-and-targets](https://angular.cn/guide/binding-syntax#binding-types-and-targets) | |
| TypeScript高级类型 | [https://typescript.bootcss.com/advanced-types.html](https://typescript.bootcss.com/advanced-types.html) | |
| 本节源码 | [https://github.com/mengyunzhi/angular11-guild/archive/step2.5.zip](https://github.com/mengyunzhi/angular11-guild/archive/step2.5.zip) | |
- 序言
- 第一章 Hello World
- 1.1 环境安装
- 1.2 Hello Angular
- 1.3 Hello World!
- 第二章 教师管理
- 2.1 教师列表
- 2.1.1 初始化原型
- 2.1.2 组件生命周期之初始化
- 2.1.3 ngFor
- 2.1.4 ngIf、ngTemplate
- 2.1.5 引用 Bootstrap
- 2.2 请求后台数据
- 2.2.1 HttpClient
- 2.2.2 请求数据
- 2.2.3 模块与依赖注入
- 2.2.4 异步与回调函数
- 2.2.5 集成测试
- 2.2.6 本章小节
- 2.3 新增教师
- 2.3.1 组件初始化
- 2.3.2 [(ngModel)]
- 2.3.3 对接后台
- 2.3.4 路由
- 2.4 编辑教师
- 2.4.1 组件初始化
- 2.4.2 获取路由参数
- 2.4.3 插值与模板表达式
- 2.4.4 初识泛型
- 2.4.5 更新教师
- 2.4.6 测试中的路由
- 2.5 删除教师
- 2.6 收尾工作
- 2.6.1 RouterLink
- 2.6.2 fontawesome图标库
- 2.6.3 firefox
- 2.7 总结
- 第三章 用户登录
- 3.1 初识单元测试
- 3.2 http概述
- 3.3 Basic access authentication
- 3.4 着陆组件
- 3.5 @Output
- 3.6 TypeScript 类
- 3.7 浏览器缓存
- 3.8 总结
- 第四章 个人中心
- 4.1 原型
- 4.2 管道
- 4.3 对接后台
- 4.4 x-auth-token认证
- 4.5 拦截器
- 4.6 小结
- 第五章 系统菜单
- 5.1 延迟及测试
- 5.2 手动创建组件
- 5.3 隐藏测试信息
- 5.4 规划路由
- 5.5 定义菜单
- 5.6 注销
- 5.7 小结
- 第六章 班级管理
- 6.1 新增班级
- 6.1.1 组件初始化
- 6.1.2 MockApi 新建班级
- 6.1.3 ApiInterceptor
- 6.1.4 数据验证
- 6.1.5 教师选择列表
- 6.1.6 MockApi 教师列表
- 6.1.7 代码重构
- 6.1.8 小结
- 6.2 教师列表组件
- 6.2.1 初始化
- 6.2.2 响应式表单
- 6.2.3 getTestScheduler()
- 6.2.4 应用组件
- 6.2.5 小结
- 6.3 班级列表
- 6.3.1 原型设计
- 6.3.2 初始化分页
- 6.3.3 MockApi
- 6.3.4 静态分页
- 6.3.5 动态分页
- 6.3.6 @Input()
- 6.4 编辑班级
- 6.4.1 测试模块
- 6.4.2 响应式表单验证
- 6.4.3 @Input()
- 6.4.4 FormGroup
- 6.4.5 自定义FormControl
- 6.4.6 代码重构
- 6.4.7 小结
- 6.5 删除班级
- 6.6 集成测试
- 6.6.1 惰性加载
- 6.6.2 API拦截器
- 6.6.3 路由与跳转
- 6.6.4 ngStyle
- 6.7 初识Service
- 6.7.1 catchError
- 6.7.2 单例服务
- 6.7.3 单元测试
- 6.8 小结
- 第七章 学生管理
- 7.1 班级列表组件
- 7.2 新增学生
- 7.2.1 exports
- 7.2.2 自定义验证器
- 7.2.3 异步验证器
- 7.2.4 再识DI
- 7.2.5 属性型指令
- 7.2.6 完成功能
- 7.2.7 小结
- 7.3 单元测试进阶
- 7.4 学生列表
- 7.4.1 JSON对象与对象
- 7.4.2 单元测试
- 7.4.3 分页模块
- 7.4.4 子组件测试
- 7.4.5 重构分页
- 7.5 删除学生
- 7.5.1 第三方dialog
- 7.5.2 批量删除
- 7.5.3 面向对象
- 7.6 集成测试
- 7.7 编辑学生
- 7.7.1 初始化
- 7.7.2 自定义provider
- 7.7.3 更新学生
- 7.7.4 集成测试
- 7.7.5 可订阅的路由参数
- 7.7.6 小结
- 7.8 总结
- 第八章 其它
- 8.1 打包构建
- 8.2 发布部署
- 第九章 总结