# 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> <button (click)="onDelete()" ?>刪除</button></td>
```
* ? 添加按鈕,并綁定`click`事件
app.component.ts
```
onDelete(): void {
console.log('click delete');
}
```
> 我們約定,與前臺事件進行綁定的方法以`on`打頭。比如`onSubmit`、`onDelete`。
## 測試

打開[http://localhost:4200/](http://localhost:4200/)及瀏覽器控制臺,點擊`刪除`按鈕后在控制臺中查看信息,符合預期。
# 傳入待刪除對象
在事件中調用C層的方法時,還可以進行相應的傳值。
```html
<td><a [routerLink]="['/edit', teacher.id]">編輯</a> <button (click)="onDelete(teacher)?">刪除</button></td>
```
* ? 向刪除方法中傳入當前行的對象`teacher`
```js
onDelete(teacher: any): void { ?
console.log(teacher); ?
}
```
* ? 在方法中增加參數`teacher`。
* ? 測試
## 測試

# 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('刪除失敗');
});
}
```
* ? 刪除成功后,重新加載數據。
## 測試

在開啟后臺的情況下,將出現`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) | - |
- 序言
- 第一章:Hello World
- 第一節:Angular準備工作
- 1 Node.js
- 2 npm
- 3 WebStorm
- 第二節:Hello Angular
- 第三節:Spring Boot準備工作
- 1 JDK
- 2 MAVEN
- 3 IDEA
- 第四節:Hello Spring Boot
- 1 Spring Initializr
- 2 Hello Spring Boot!
- 3 maven國內源配置
- 4 package與import
- 第五節:Hello Spring Boot + Angular
- 1 依賴注入【前】
- 2 HttpClient獲取數據【前】
- 3 數據綁定【前】
- 4 回調函數【選學】
- 第二章 教師管理
- 第一節 數據庫初始化
- 第二節 CRUD之R查數據
- 1 原型初始化【前】
- 2 連接數據庫【后】
- 3 使用JDBC讀取數據【后】
- 4 前后臺對接
- 5 ng-if【前】
- 6 日期管道【前】
- 第三節 CRUD之C增數據
- 1 新建組件并映射路由【前】
- 2 模板驅動表單【前】
- 3 httpClient post請求【前】
- 4 保存數據【后】
- 5 組件間調用【前】
- 第四節 CRUD之U改數據
- 1 路由參數【前】
- 2 請求映射【后】
- 3 前后臺對接【前】
- 4 更新數據【前】
- 5 更新某個教師【后】
- 6 路由器鏈接【前】
- 7 觀察者模式【前】
- 第五節 CRUD之D刪數據
- 1 綁定到用戶輸入事件【前】
- 2 刪除某個教師【后】
- 第六節 代碼重構
- 1 文件夾化【前】
- 2 優化交互體驗【前】
- 3 相對與絕對地址【前】
- 第三章 班級管理
- 第一節 JPA初始化數據表
- 第二節 班級列表
- 1 新建模塊【前】
- 2 初識單元測試【前】
- 3 初始化原型【前】
- 4 面向對象【前】
- 5 測試HTTP請求【前】
- 6 測試INPUT【前】
- 7 測試BUTTON【前】
- 8 @RequestParam【后】
- 9 Repository【后】
- 10 前后臺對接【前】
- 第三節 新增班級
- 1 初始化【前】
- 2 響應式表單【前】
- 3 測試POST請求【前】
- 4 JPA插入數據【后】
- 5 單元測試【后】
- 6 惰性加載【前】
- 7 對接【前】
- 第四節 編輯班級
- 1 FormGroup【前】
- 2 x、[x]、{{x}}與(x)【前】
- 3 模擬路由服務【前】
- 4 測試間諜spy【前】
- 5 使用JPA更新數據【后】
- 6 分層開發【后】
- 7 前后臺對接
- 8 深入imports【前】
- 9 深入exports【前】
- 第五節 選擇教師組件
- 1 初始化【前】
- 2 動態數據綁定【前】
- 3 初識泛型
- 4 @Output()【前】
- 5 @Input()【前】
- 6 再識單元測試【前】
- 7 其它問題
- 第六節 刪除班級
- 1 TDD【前】
- 2 TDD【后】
- 3 前后臺對接
- 第四章 學生管理
- 第一節 引入Bootstrap【前】
- 第二節 NAV導航組件【前】
- 1 初始化
- 2 Bootstrap格式化
- 3 RouterLinkActive
- 第三節 footer組件【前】
- 第四節 歡迎界面【前】
- 第五節 新增學生
- 1 初始化【前】
- 2 選擇班級組件【前】
- 3 復用選擇組件【前】
- 4 完善功能【前】
- 5 MVC【前】
- 6 非NULL校驗【后】
- 7 唯一性校驗【后】
- 8 @PrePersist【后】
- 9 CM層開發【后】
- 10 集成測試
- 第六節 學生列表
- 1 分頁【后】
- 2 HashMap與LinkedHashMap
- 3 初識綜合查詢【后】
- 4 綜合查詢進階【后】
- 5 小試綜合查詢【后】
- 6 初始化【前】
- 7 M層【前】
- 8 單元測試與分頁【前】
- 9 單選與多選【前】
- 10 集成測試
- 第七節 編輯學生
- 1 初始化【前】
- 2 嵌套組件測試【前】
- 3 功能開發【前】
- 4 JsonPath【后】
- 5 spyOn【后】
- 6 集成測試
- 7 @Input 異步傳值【前】
- 8 值傳遞與引入傳遞
- 9 @PreUpdate【后】
- 10 表單驗證【前】
- 第八節 刪除學生
- 1 CSS選擇器【前】
- 2 confirm【前】
- 3 功能開發與測試【后】
- 4 集成測試
- 5 定制提示框【前】
- 6 引入圖標庫【前】
- 第九節 集成測試
- 第五章 登錄與注銷
- 第一節:普通登錄
- 1 原型【前】
- 2 功能設計【前】
- 3 功能設計【后】
- 4 應用登錄組件【前】
- 5 注銷【前】
- 6 保留登錄狀態【前】
- 第二節:你是誰
- 1 過濾器【后】
- 2 令牌機制【后】
- 3 裝飾器模式【后】
- 4 攔截器【前】
- 5 RxJS操作符【前】
- 6 用戶登錄與注銷【后】
- 7 個人中心【前】
- 8 攔截器【后】
- 9 集成測試
- 10 單例模式
- 第六章 課程管理
- 第一節 新增課程
- 1 初始化【前】
- 2 嵌套組件測試【前】
- 3 async管道【前】
- 4 優雅的測試【前】
- 5 功能開發【前】
- 6 實體監聽器【后】
- 7 @ManyToMany【后】
- 8 集成測試【前】
- 9 異步驗證器【前】
- 10 詳解CORS【前】
- 第二節 課程列表
- 第三節 果斷
- 1 初始化【前】
- 2 分頁組件【前】
- 2 分頁組件【前】
- 3 綜合查詢【前】
- 4 綜合查詢【后】
- 4 綜合查詢【后】
- 第節 班級列表
- 第節 教師列表
- 第節 編輯課程
- TODO返回機制【前】
- 4 彈出框組件【前】
- 5 多路由出口【前】
- 第節 刪除課程
- 第七章 權限管理
- 第一節 AOP
- 總結
- 開發規范
- 備用