按照MVC的開發理論,在C層與V層的分工中,應該這樣:① C層來確定V層顯示什么樣的數據;②V層決定顯示數據的格式(樣式)。也就是說:像`系統的名稱`是什么,除了首頁外還有幾個菜單項,這些都應該是C層來負責的。
## 分層
按上述的理論,我們將V層本應該存在于C層的信息遷移過來。
### C層
nav/nav.component.ts
```
export class NavComponent implements OnInit {
/*標題*/
title: string; ①
/*菜單項*/
menus = new Array<{ url: string; name: string }?>(); ②
constructor() {
}
ngOnInit() {
this.title = '教務管理系統'; ③
this.menus.push({url: 'teacher', name: '教師管理'}); ③
this.menus.push({url: 'klass', name: '班級管理'}); ③
}
}
```
* ? Array是一個容器,在進行初始化時,我們需要為其定義類型。在定義類型時,可以使用`對象`聲明的方法來替待使用`類`進行聲明的方式。
### V層
nav/nav.component.html
```
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<a class="navbar-brand" routerLink="">{{title}}①</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" routerLink="">首頁 <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item" *ngFor="let menu of menus"②>
<a class="nav-link" routerLink="{{menu.url}}③">{{menu.name}}④</a>
</li>
</ul>
</div>
</nav>
```
#### 測試
對nav組件進行測試
```
Failed: Template parse errors: Can't bind to 'routerLink' since it isn't a known property of 'a'. ("
```
提示沒有找到routerLink屬性,則加入路由模塊來修正此問題。
nav/nav.component.spec.ts
```
TestBed.configureTestingModule({
declarations: [NavComponent],
imports: [RouterTestingModule]
})
```
測試通過
## 集成測試
最后使用`ng serve`來啟動項目,并查看其在系統中的表現效果。

我們發現有個小的問題:無論當前是哪個功能界面,被點亮的永遠是`首頁`。這是由于在以下樣式決定的:
nav/nav.component.html
```
<li class="nav-item active①">
<a class="nav-link" routerLink="">首頁 <span class="sr-only">(current)</span></a>
</li>
```
* ① active樣式決定了其點亮的特定
### RouterLinkActive
angular為我們提供了RouterLinkActive來解決此問題,當以RouterLinkActive標識的鏈接被選中時,會自動將RouterLinkActive的值添加加宿主元素的樣式中,比如我們可以這樣用:
```
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav">
<li class="nav-item" routerLinkActive="active"?>
<a class="nav-link" routerLink="">首頁 <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item" *ngFor="let menu of menus" routerLinkActive="active"?>
<a class="nav-link" routerLink="{{menu.url}}">{{menu.name}}</a>
</li>
</ul>
</div>
```
* ? 當宿主節點(元素)或宿主節點下的子節點中的路由被選中時,將active添加到宿主節點的class中。
#### 測試:

但當前我們選擇了班級管理,為什么首頁也被點亮了呢?這是因為班級管理的地址為`/klass`,而`/klass`是基于首頁的路由地址`<空>`的,所以此時的路由實際是:首頁`<空>`的子路由`klass`(想想我們配置路由的過程),所以此時班級管理及首頁都被點亮是正確的。如果我們并不想點亮父路由,則可以使用設置`routerLinkActiveOptions`來解決這個問題:
```
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}"?>
<a class="nav-link" routerLink="">首頁 <span class="sr-only">(current)</span></a>
</li>
```
* ? routerLinkActiveOptions = 路由鏈接激活選項, exact = 嚴格的、精確的
加入該選擇后,當點班級管理或是教師管理時,首頁就不會被點亮了。

## 測試
最后在收尾前我們將所有的`f`由測試代碼中去除,并運行`ng test`看是否當前的開發對歷史功能造成了影響 。
```
Chrome 78.0.3904 (Mac OS X 10.13.6): Executed 11 of 14 SUCCESS (0 secs / 0.443 secs)
Chrome 78.0.3904 (Mac OS X 10.13.6): Executed 14 of 14 SUCCESS (0.57 secs / 0.516 secs)
TOTAL: 14 SUCCESS
TOTAL: 14 SUCCESS
```
未發生其它異常,放寬心的收工。
# 參考文檔
| 名稱 | 鏈接 | 預計學習時長(分) |
| --- | --- | --- |
| 源碼地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step4.2.3](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step4.2.3) | - |
[https://angular.io/api/router/RouterLinkActive](https://angular.io/api/router/RouterLinkActive)
- 序言
- 第一章: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
- 總結
- 開發規范
- 備用