# Binding to user input events 在新增及编辑功能中,我们在`form 表单`中使用了`(ngSubmit)="onSubmit()"`来触发了C层的`onSubmit()`方法最终完成表单的提交。`(ngSubmit)="onSubmit()"`:当用户点击表单提交时,触发我们C层的`onSubmit()`方法。这个过程便是:` Binding to user input events 绑定到用户输入事件`。 用户在使用系统过程中与网页的互动,我们称为`event 事件`。`html event`分别五大类:`windows 事件`,比如我们对浏览器的窗口改变大小时会触发该事件;`Form 事件`,比如我们修改某个表单的值;`Keyboard 事件`,比如我们按下了某个按键;`Mouse 事件`,当我们点击鼠标时会触发;`Media 事件`,控制一些媒体的播放,比如`mp3`的播放、暂停。在`angular`中我们可以非常简单的使用内置的一些事件,比如使用`(ngSubmit)`来获取表单事件,使用`(click)`来获取点击事件。 ## click 在教师列表中,为每一项添加一个删除按钮,当此按钮被点击时,触发C层的`onDelete`方法。 app.component.html ``` <td><a [routerLink]="['/edit', teacher.id]">编辑</a> &nbsp;&nbsp;<button (click)="onDelete()" ➊>删除</button></td> ``` * ➊ 添加按钮,并绑定`click`事件 app.component.ts ``` onDelete(): void { console.log('click delete'); } ``` > 我们约定,与前台事件进行绑定的方法以`on`打头。比如`onSubmit`、`onDelete`。 ## 测试 ![](https://img.kancloud.cn/17/ab/17abb33af91bb913044d4a8f800fdf4a_701x169.png) 打开[http://localhost:4200/](http://localhost:4200/)及浏览器控制台,点击`删除`按钮后在控制台中查看信息,符合预期。 # 传入待删除对象 在事件中调用C层的方法时,还可以进行相应的传值。 ```html <td><a [routerLink]="['/edit', teacher.id]">编辑</a> &nbsp;&nbsp;<button (click)="onDelete(teacher)➊">删除</button></td> ``` * ➊ 向删除方法中传入当前行的对象`teacher` ```js onDelete(teacher: any): void { ➊ console.log(teacher); ➋ } ``` * ➊ 在方法中增加参数`teacher`。 * ➋ 测试 ## 测试 ![](https://img.kancloud.cn/b1/e9/b1e98763cd1a438a172aeba68cffccd8_936x171.png) # delete方法请求后台 同`get`方法的相同,我们直接调用`httpClient.delete(url: string)`方法来进行`delete`方法的请求. ```js /** * 点击删除按钮时删除对应的教师 * @param teacher 要删除的教师 */ onDelete(teacher: any): void { const url = 'http://localhost:8080/Teacher/' + teacher.id; this.httpClient.delete(url) .subscribe(() => { console.log('删除成功'); this.ngOnInit(); ➊ }, () => { console.log('删除失败'); }); } ``` * ➊ 删除成功后,重新加载数据。 ## 测试 ![](https://img.kancloud.cn/bd/2b/bd2ba4127c9544a3d4b524f1c7f6b683_428x36.png) 在开启后台的情况下,将出现`403`无此权限错误;在未开启后台的情况下,将出现`404`资源不存在错误。在未对接后台前,这都是正常的。 # TypeScript `JavaScript`是个伟大、灵活的语言。同时它有具有着上手极简单、精通特别难的特点。这有些类似于网络游戏,刚刚接触的时候感觉没有什么进而会给你极其优越的成就感;但随着时间的推移总会出一现新奇的东西需要学习和掌握。当然了,对于我们而言便是一路踩坑、爬坑的经历。`TypeScript`及其类似的语言正是看到了这一点,使得新手在使用`JavaScript`能够少踩一些坑,使得习惯了使用不太灵活的面向对象的语言的开发人员能够快速开发`JavaScript`程序。其实本质上`TypeScript`并不是一门语言而一个解析器,它使用一些新的语法、规范来约束我们的开发,而最终交给浏览器去执行的仍然是 `JavaScript`,只不过这个 `JavaScript`是由`TypeScript`翻译后自动生成的。 官方如是说:**TypeScript is a superset of JavaScript that compiles to clean JavaScript output.**、**TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. Any browser. Any host. Any OS. Open source.** 如官方所说它是一个`typed superset 有类型的超集`。只所以这样说,是由于`javascript`其实是`untyped 弱类型`的语言。比如我们可以在`js`中这样定义`var a = 3; a = 'yunzhi'; `,这不会发生任何问题。但如果在`ts`中这样写:`let a = 3; a = 'yunzhi';`便会引发一个编译错误: `Error:(42, 5) TS2322: Type '"yunzhi"' is not assignable to type 'number'.`。这时由于`ts`是一种强类型语言,和其它的强类型语言一样,在`ts`中变量一旦规定了类型,那么在赋值时就必须与该类型相匹配。但由于`ts`的最终目标是翻译为`js`,同时呢在`ts`中我们还可以使用原有的一些`js`库,那么就需要有一个兼容性的问题,所以`ts`中使有了类型`any`。所以在`ts`中这样编写是没有问题的:`let a: any; a = 3; a = 'yunzhi';`。而`any`则代表任意类型。 ## 语法较验 在实际的开发中,我们应该最大限度的去规避使用`any`类型,这是因为我们无法由`any`类型中得到任何的帮助信息。比如我们刚刚编写的:`onDelete(teacher: any): void`对于1个月后的自己,此函数等价于`onDelete(a: any): void`,而我们对`a`的值、类型一无所知。 而`typescript`有着强大的语法较验功能,该功能能够帮助我们规避一些拼写时的低端错误。比如我们可以在`onDelete`方法中注明该变量的类型或成功调用该方法该变量需要具有的类型。 ``` onDelete(teacher: {id: number} ➊):void { const url = 'http://localhost:8080/Teacher/' + teacher.id; this.httpClient.delete(url) .subscribe(() => { console.log('删除成功'); this.ngOnInit(); }, () => { console.log('删除失败'); }); } ``` * ➊ 接收的参数teacher的类型为`{} 对象`,且该`{} 对象`必须存在`id`属性,该属性的类型必须为`number`。 如此一来,如果在进行调用时传入的对象无`id`属性,或是`id`属性的类型不是`number`,`ts`在编译过程中便会发生错误来提醒我们此次的调用存在问题,从而避免了生产环境中的一些不可预测错误。同时其它的团队成员只要看到了该参数的定义便会清晰:该参数接收的对象中必须有`id`属性,且此属性的值的类型为`number`,在进行调用时需要遵循此规划来做。 在后续的教程中,我们将越来越多的`定义参数类型`的方法来避免语法错误。除此以外,这种写法还有一个好处是我们以后由于一些特定的不可控的事情导致需要重写该功能时,我们可以根据参数以及注释信息达到快速二次开发的目的。我们认为:软件在开发中需求必然会经过变更,而我们做的所有的规范都是为了满足需求的变更。我们希望随着项目的逐渐增大,尽量少的增加维护难度,最终达到易维护的系统。争取能够使得一个团队写的代码就像一个人写的一样,争取今天读一个月以前写的代码就像读昨天写的代码一样。把开发软件做成一个实实在在的工程,这可能就是`软件工程`名称的由来吧。 # 前后台对接总结 `delete`请求我们接触的第4个请求方法,除此以外我们还会接触到`options`及`patch`方法,我们来简单总结一下: | 请求方法 | 前台方法 | 后台方法 | 适用场景 | | --- | --- | --- | --- | | get | httpClient.get(url: string) | @GetMapping | 按条件查询获取后台的所有数据 | | get | httpClient.get(url: string) | @GetMapping("{id}") | 按条件查询获取后台的某条数据 | | post | httpClient.post(url: string, data: Object) | @PostMapping, @RequestBody | 新增数据 | | put | httpClient.put(url: string, data: Object) | @PutMapping("{id}") @RequestBody | 更新某条数据(一般用于更新实体的所有字段) | | delete | httpClient.delete(url: string) | @DeleteMapping("id") | 删除某条数据 | | options | - | 浏览器自动发生,用于确认某些资源的访问权限 | | patch | httpClient.patch(url: string, data: Object) | @PatchMapping("{id}") @RequestBody | 更新某条数据(一般用于更新实体的部分字段) | # 参考文档 | 名称 | 链接 | 预计学习时长(分) | | --- | --- | --- | | TypeScript(github) | [https://github.com/microsoft/TypeScript](https://github.com/microsoft/TypeScript) | 1 | | TypeScript | [https://www.typescriptlang.org/](https://www.typescriptlang.org/) | 1 | | TypeScript 基础类型 | [https://www.runoob.com/typescript/ts-type.html](https://www.runoob.com/typescript/ts-type.html) | 15 | | TypeScript 函数 | [https://www.runoob.com/typescript/ts-function.html](https://www.runoob.com/typescript/ts-function.html) | 15 | | 用户输入 | [https://www.angular.cn/guide/user-input](https://www.angular.cn/guide/user-input) | 10 | | 源码地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step2.5.1](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step2.5.1) | - |