在前面的章節(jié)中,我們已經(jīng)使用Angular成功的顯示了Hello World!。下面,我們一起探索一下src下的app文件夾。
```
├── app
│?? ├── app-routing.module.ts ?
│?? ├── app.component.html ? ?
│?? ├── app.component.sass ? ?
│?? ├── app.component.spec.ts ?
│?? ├── app.component.ts ? ?
│?? └── app.module.ts ?
```
此文件夾中有6個(gè)文件組成,共分成3個(gè)大部分。
* ? 路由文件,負(fù)責(zé)數(shù)據(jù)轉(zhuǎn)發(fā)。
* ? `app`組件。
* ? 組件視圖(V層)。
* ? 組件樣式(V層)。
* ? `app`組件對(duì)應(yīng)的測(cè)試文件。
* ? 組件主體文件(C層)。
* ? `app`模塊,巧的是它的名字與組件重名(僅僅是名字相同而已,就像我們學(xué)校有條路起名為逸夫,大家稱(chēng)它為逸夫路;我還學(xué)校有個(gè)圖書(shū)館,巧的是也叫逸夫,只不過(guò)它是逸夫樓一樣)。
他們間大體的關(guān)系如下圖示:

# 模塊
一個(gè)angular應(yīng)用通常會(huì)有多個(gè)模塊(Module)組成。angular中的模塊共有9個(gè)配置部分。其中最常用的有4部分,筆者將其命名為:私有資源列表、公有資源列表、協(xié)作模塊列表以及擴(kuò)展功能列表。

以`app`模塊為例認(rèn)識(shí)下模塊的定義與基本組成:
app.module.ts
```typescript
import {BrowserModule} from '@angular/platform-browser'; ?
import {NgModule} from '@angular/core'; ?
import {AppRoutingModule} from './app-routing.module'; ?
import {AppComponent} from './app.component'; ?
@NgModule({ ?
declarations: [ ?
AppComponent ?
],
imports: [ ?
BrowserModule, ?
AppRoutingModule, ?
],
providers: [], ?
bootstrap: [AppComponent] ?
})
export class AppModule { ?
}
```
* ? 與php中的use, java中的import相同
* ? Angular的注解,將`AppModule`聲明為模塊
* ? 聲明當(dāng)前模塊中的私有資源列表
* ? 將`app`組件加入到`app`模塊的私有資源列表
* ? 聲明模塊的協(xié)作模塊列表
* ? 將瀏覽器模塊加入到協(xié)作列表中,該模塊能夠提供的功能是:渲染html文件等。
* ? 將路由模塊加入到協(xié)作列表中,該模塊提供的功能由`app-routing.module.ts`中的`AppRouting`模塊所決定。
* ? 擴(kuò)展功能列表
* ? 啟動(dòng)組件,由于某些配置的原因,當(dāng)程序運(yùn)行時(shí)會(huì)自動(dòng)啟動(dòng)該組件。
* ? 定義類(lèi)名并使用`export`聲明,表明該類(lèi)可被`app.module.ts`以外的文件引用。
通過(guò)上面的解析可看到,使用`@NgModule`來(lái)聲明某類(lèi)為angular應(yīng)用中的一個(gè)模塊,使用`@NgModule`中配置相應(yīng)的屬性來(lái)對(duì)此模塊進(jìn)行配置。其中屬性`declarations`用于聲明私有資源列表,屬性`imports`用于聲明協(xié)作模塊列表,屬性`providers`用于聲明擴(kuò)展功能列表。
所以模塊的組成也可以使用屬性關(guān)鍵字進(jìn)行如下描述:

## import與imports
?處的import與?處的imports之間除了長(zhǎng)的像以外,本質(zhì)上并沒(méi)有其它的任何關(guān)聯(lián)。
import是typescript的語(yǔ)法,屬于編程語(yǔ)言層面,表示由某個(gè)文件中引入某個(gè)類(lèi)、接口、方法至此文件。與其它語(yǔ)言的命名空間極為類(lèi)型,稍有不同的是typescript并不需要在文件中使用package或namespace的字樣來(lái)規(guī)定命名空間,而是將文件的存儲(chǔ)位置直接做為了命名空間來(lái)使用。
而imports是屬于angular這個(gè)基于typescript開(kāi)發(fā)而來(lái)的框架的`@NgModule`注解的一個(gè)屬性。它的含意是聲明angular中的模塊引入的其它的模塊。在angular應(yīng)用中,功能的完成依賴(lài)模塊間的協(xié)同作業(yè),而imports的作用便是來(lái)聲明某個(gè)模塊需要的協(xié)同者。
# 打開(kāi)項(xiàng)目
找到前面安裝的WebStorm并運(yùn)行,選擇open并找到項(xiàng)目文件:


稍等片刻,待WebStrom初始化完成。接著打開(kāi)shell進(jìn)入項(xiàng)目文件夾,并使用`ng serve --open`啟動(dòng)項(xiàng)目并自動(dòng)打開(kāi)瀏覽器。
# 依賴(lài)注入
在Anguar中發(fā)起網(wǎng)絡(luò)請(qǐng)求需要借助`AppRoutingModule`中被聲明公有資源列表的`HttpClient`。`angular`中的組件可以構(gòu)造函數(shù)中聲明自己所需要的依賴(lài)類(lèi)型。angular在初始化該組件時(shí),將嘗試在組件所在模塊中按構(gòu)造函數(shù)中類(lèi)型自動(dòng)注入相關(guān)功能的實(shí)例。以`App`組件需要具有網(wǎng)絡(luò)請(qǐng)求功能的`HttpClient`為例:
AppComponent.ts
```typescript
import { Component } from '@angular/core';
import {HttpClient} from '@angular/common/http';?
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.sass']
})
export class AppComponent {
constructor(private httpClient: HttpClient) { ?
console.log(httpClient);
}
title = 'hello-world';
}
```
? 使用import由`@angular/common/http`中引入`HttpClient`到此文件。
? 構(gòu)造函數(shù)中聲明依賴(lài)類(lèi)型為HttpClient。
> 在后續(xù)的教程中將只給出文件名,當(dāng)未對(duì)文件路徑做特殊說(shuō)明時(shí),表示該文件相對(duì)于`src/app/`文件夾。比如:`AppComponent.ts`實(shí)際為`src/app/AppComponent.ts`。
此時(shí)我們刷新頁(yè)面查看控制臺(tái),將得到如下錯(cuò)誤:

這是由于angular在嘗試注入HttpClient的過(guò)程中發(fā)生了錯(cuò)誤。angular能夠成功完成HttpClient的注入,則`App`組件所在的`App`模塊需要滿(mǎn)足以下任一條件:
<hr>
???????????????????????????? **以下內(nèi)容很重要,請(qǐng)牢記!** ????????????????????????????
1. 在當(dāng)前模塊**私有資源列表**中能夠找到`HttpClient`。
2. 當(dāng)前模塊的父模塊的**私有資源列表**中能夠找到`HttpClient`。
3. 在前模塊**協(xié)作模塊列表**中的任意模塊的**公有資源列表**中找到`HttpClient`
4. 在前模塊父模塊的**協(xié)作模塊列表**中的任意模塊的**公有資源列表**中找到`HttpClient`
???????????????????????????? **以上內(nèi)容很重要,請(qǐng)牢記!** ????????????????????????????
<hr>
當(dāng)前`App`模塊可如下可描述為:

App模塊在創(chuàng)建AppComponent時(shí),由于在其私有資源中找到了AppComponent,近而進(jìn)一步為其注入`HttpClient`。在注入`HttpClient`注入未成功進(jìn)而發(fā)生錯(cuò)誤。

這當(dāng)然也在控制臺(tái)中報(bào)發(fā)下錯(cuò)誤的原因:
```
RROR NullInjectorError: StaticInjectorError(AppModule)[AppComponent -> HttpClient]:
StaticInjectorError(Platform: core)[AppComponent -> HttpClient]:
NullInjectorError: No provider for HttpClient!
```
> 如果某種錯(cuò)誤是第一次出現(xiàn),出現(xiàn)后你完全沒(méi)有任何解決它的思路,此時(shí)你絕對(duì)不應(yīng)該去傻傻的看代碼是不是哪有問(wèn)題。你的正確操作應(yīng)該是:翻譯!
```
嚴(yán)重錯(cuò)誤 空注入器錯(cuò)誤:靜態(tài)注入器錯(cuò)誤(AppMoule上發(fā)生的[在向AppComponent注入HttpClient時(shí)]:
靜態(tài)注入器錯(cuò)誤(在Platform:core上發(fā)生的)[在向AppComponent注入HttpClient時(shí)]:
空注入器錯(cuò)誤: 沒(méi)有HttpCilent的提供者
```
出錯(cuò)的原因找到了,解決問(wèn)題的方法也就隨著有了。

>[success] 在angular中,任何模塊都屬于Root模塊的子模塊。
對(duì)應(yīng)代碼如下:
app.module.ts
```typescript
import {NgModule} from '@angular/core';
import {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
import {HttpClientModule} from '@angular/common/http'; ?
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule ?
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
}
```
* ? 由@angular/common/http中引入HttpClientModule至此文件
* ? 在協(xié)作模塊列表中加入可以提供`HttpClient`的`HttpClientModule`
# 測(cè)試
打開(kāi)控制臺(tái)(F12),查看測(cè)試消息如下:

控制臺(tái)中成功的打印了`HttpClient`對(duì)象的信息同時(shí)未發(fā)生任何錯(cuò)誤,注入成功。下個(gè)小節(jié)中展示如何使用`HttpClient`發(fā)起網(wǎng)絡(luò)請(qǐng)求。
## 生活與計(jì)算機(jī)
計(jì)算機(jī)來(lái)源于生活,現(xiàn)實(shí)生活中大概會(huì)有這樣的場(chǎng)景:技術(shù)部門(mén)組織春游需要一輛巴士,向公司統(tǒng)一協(xié)調(diào)部門(mén)打電話(huà)告知其需求,即:我們需要一輛巴士,此外還需要配備一名司機(jī)。

上面的需求翻譯成代碼語(yǔ)言如下:
```
export class Play {
constructor(private bus: Bus, private driver: Driver)
}
}
```
其實(shí)此時(shí)我們并不關(guān)心統(tǒng)一協(xié)調(diào)部門(mén)為我們提供的巴士是公司自有車(chē)輛還是其由外面租賃的,也不關(guān)心巴士的顏色是紅的還是綠的,當(dāng)然也不關(guān)心司機(jī)是男是女。我們關(guān)心是功能,即巴士有運(yùn)載人員的功能,司機(jī)有駕駛巴士的功能。
統(tǒng)一協(xié)調(diào)部門(mén)得到這個(gè)需要以后,去調(diào)配巴士和司機(jī),比如此時(shí)公司的資源是這樣的:

統(tǒng)一調(diào)度部門(mén)發(fā)現(xiàn)能夠滿(mǎn)足我們的需求,于是將巴士及能駕駛巴士的司機(jī)指派給我們。

要知道并不是所有的公司都有擁有自有用車(chē)的,那么當(dāng)這類(lèi)公司中的內(nèi)部部門(mén)向統(tǒng)一協(xié)調(diào)部分發(fā)送用車(chē)需求時(shí),統(tǒng)一協(xié)調(diào)部門(mén)該怎么辦呢?相信你早有了答案 ---- 找第三租車(chē)公司呀!
按上面的思路,假設(shè)這個(gè)公司的汽車(chē)服務(wù)需要借肋于租車(chē)公司、零食需要借助于超市、藥品需要借助于藥店,那么就會(huì)出現(xiàn)如下情況:

當(dāng)其它部門(mén)提出`汽車(chē)+司機(jī)+藥品+零食`、`汽車(chē)+司機(jī)+藥品`、`藥品+零食`需求時(shí),統(tǒng)一協(xié)調(diào)部門(mén)都是可以做到的。但如果有部門(mén)提出`飛機(jī)+零食`的需求時(shí),統(tǒng)一協(xié)調(diào)部門(mén)就做不到了,此時(shí)統(tǒng)一協(xié)調(diào)部門(mén)就會(huì)報(bào)異常,因?yàn)樗覆坏絗飛機(jī)`的供應(yīng)商來(lái)滿(mǎn)足其它部門(mén)的需求。
>[success] 在計(jì)算機(jī)的世界里,我們把這種借助統(tǒng)一協(xié)調(diào)部門(mén)來(lái)自動(dòng)提供指定需求的方法稱(chēng)為**依賴(lài)注入 ---- Dependency injection**,也就是你在面試時(shí)可能會(huì)被問(wèn)到的`DI`。
## 計(jì)算機(jī)與生活
在我們剛剛接觸的Angular的世界里,`app module(模塊)`就是這個(gè)公司下的統(tǒng)一協(xié)調(diào)部門(mén),負(fù)責(zé)該公司一切資源的調(diào)配;`app component組件`則是提出需求的其它部門(mén),提出需求的方法是通過(guò)構(gòu)造函數(shù)來(lái)聲明。
所以我們前面剛剛添加過(guò)的app組件:
AppComponet.ts
```
constructor(private httpClient: HttpClient) { ?
console.log(httpClient);
}
```
? 找自己所在的module要`HttpClient`。
前面我們分析`AppMoudle`時(shí)講過(guò)了:`AppComponent`屬于`AppMoudle`。所以這里的`HttpClient`應(yīng)該由`AppMoudle`來(lái)提供,但此時(shí)`AppMoudle`還沒(méi)有提供`HttpClient`的能力,所以我們打開(kāi)控制臺(tái)后,就出現(xiàn)了:
有了以上的提示再加上我們前面對(duì)DI知識(shí)的學(xué)習(xí),解決這個(gè)問(wèn)題也就不困難了。
# 本節(jié)小測(cè)
? 控制臺(tái)報(bào)錯(cuò)為:
```
RROR NullInjectorError: StaticInjectorError(AppModule)[AppComponent -> HttpClient]:
StaticInjectorError(Platform: core)[AppComponent -> HttpClient]:
NullInjectorError: No provider for HttpClient!
```
報(bào)錯(cuò)內(nèi)容一直在說(shuō)`HttpClient`錯(cuò)誤,而在`AppModule`中卻這樣聲明:
```
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule ?
],
```
問(wèn)題:為什么提示說(shuō)缺少HttpClient,但我們卻在此imports了`HttpClientModule`呢?
- 序言
- 第一章:Hello World
- 第一節(jié):Angular準(zhǔn)備工作
- 1 Node.js
- 2 npm
- 3 WebStorm
- 第二節(jié):Hello Angular
- 第三節(jié):Spring Boot準(zhǔn)備工作
- 1 JDK
- 2 MAVEN
- 3 IDEA
- 第四節(jié):Hello Spring Boot
- 1 Spring Initializr
- 2 Hello Spring Boot!
- 3 maven國(guó)內(nèi)源配置
- 4 package與import
- 第五節(jié):Hello Spring Boot + Angular
- 1 依賴(lài)注入【前】
- 2 HttpClient獲取數(shù)據(jù)【前】
- 3 數(shù)據(jù)綁定【前】
- 4 回調(diào)函數(shù)【選學(xué)】
- 第二章 教師管理
- 第一節(jié) 數(shù)據(jù)庫(kù)初始化
- 第二節(jié) CRUD之R查數(shù)據(jù)
- 1 原型初始化【前】
- 2 連接數(shù)據(jù)庫(kù)【后】
- 3 使用JDBC讀取數(shù)據(jù)【后】
- 4 前后臺(tái)對(duì)接
- 5 ng-if【前】
- 6 日期管道【前】
- 第三節(jié) CRUD之C增數(shù)據(jù)
- 1 新建組件并映射路由【前】
- 2 模板驅(qū)動(dòng)表單【前】
- 3 httpClient post請(qǐng)求【前】
- 4 保存數(shù)據(jù)【后】
- 5 組件間調(diào)用【前】
- 第四節(jié) CRUD之U改數(shù)據(jù)
- 1 路由參數(shù)【前】
- 2 請(qǐng)求映射【后】
- 3 前后臺(tái)對(duì)接【前】
- 4 更新數(shù)據(jù)【前】
- 5 更新某個(gè)教師【后】
- 6 路由器鏈接【前】
- 7 觀察者模式【前】
- 第五節(jié) CRUD之D刪數(shù)據(jù)
- 1 綁定到用戶(hù)輸入事件【前】
- 2 刪除某個(gè)教師【后】
- 第六節(jié) 代碼重構(gòu)
- 1 文件夾化【前】
- 2 優(yōu)化交互體驗(yàn)【前】
- 3 相對(duì)與絕對(duì)地址【前】
- 第三章 班級(jí)管理
- 第一節(jié) JPA初始化數(shù)據(jù)表
- 第二節(jié) 班級(jí)列表
- 1 新建模塊【前】
- 2 初識(shí)單元測(cè)試【前】
- 3 初始化原型【前】
- 4 面向?qū)ο蟆厩啊?/a>
- 5 測(cè)試HTTP請(qǐng)求【前】
- 6 測(cè)試INPUT【前】
- 7 測(cè)試BUTTON【前】
- 8 @RequestParam【后】
- 9 Repository【后】
- 10 前后臺(tái)對(duì)接【前】
- 第三節(jié) 新增班級(jí)
- 1 初始化【前】
- 2 響應(yīng)式表單【前】
- 3 測(cè)試POST請(qǐng)求【前】
- 4 JPA插入數(shù)據(jù)【后】
- 5 單元測(cè)試【后】
- 6 惰性加載【前】
- 7 對(duì)接【前】
- 第四節(jié) 編輯班級(jí)
- 1 FormGroup【前】
- 2 x、[x]、{{x}}與(x)【前】
- 3 模擬路由服務(wù)【前】
- 4 測(cè)試間諜spy【前】
- 5 使用JPA更新數(shù)據(jù)【后】
- 6 分層開(kāi)發(fā)【后】
- 7 前后臺(tái)對(duì)接
- 8 深入imports【前】
- 9 深入exports【前】
- 第五節(jié) 選擇教師組件
- 1 初始化【前】
- 2 動(dòng)態(tài)數(shù)據(jù)綁定【前】
- 3 初識(shí)泛型
- 4 @Output()【前】
- 5 @Input()【前】
- 6 再識(shí)單元測(cè)試【前】
- 7 其它問(wèn)題
- 第六節(jié) 刪除班級(jí)
- 1 TDD【前】
- 2 TDD【后】
- 3 前后臺(tái)對(duì)接
- 第四章 學(xué)生管理
- 第一節(jié) 引入Bootstrap【前】
- 第二節(jié) NAV導(dǎo)航組件【前】
- 1 初始化
- 2 Bootstrap格式化
- 3 RouterLinkActive
- 第三節(jié) footer組件【前】
- 第四節(jié) 歡迎界面【前】
- 第五節(jié) 新增學(xué)生
- 1 初始化【前】
- 2 選擇班級(jí)組件【前】
- 3 復(fù)用選擇組件【前】
- 4 完善功能【前】
- 5 MVC【前】
- 6 非NULL校驗(yàn)【后】
- 7 唯一性校驗(yàn)【后】
- 8 @PrePersist【后】
- 9 CM層開(kāi)發(fā)【后】
- 10 集成測(cè)試
- 第六節(jié) 學(xué)生列表
- 1 分頁(yè)【后】
- 2 HashMap與LinkedHashMap
- 3 初識(shí)綜合查詢(xún)【后】
- 4 綜合查詢(xún)進(jìn)階【后】
- 5 小試綜合查詢(xún)【后】
- 6 初始化【前】
- 7 M層【前】
- 8 單元測(cè)試與分頁(yè)【前】
- 9 單選與多選【前】
- 10 集成測(cè)試
- 第七節(jié) 編輯學(xué)生
- 1 初始化【前】
- 2 嵌套組件測(cè)試【前】
- 3 功能開(kāi)發(fā)【前】
- 4 JsonPath【后】
- 5 spyOn【后】
- 6 集成測(cè)試
- 7 @Input 異步傳值【前】
- 8 值傳遞與引入傳遞
- 9 @PreUpdate【后】
- 10 表單驗(yàn)證【前】
- 第八節(jié) 刪除學(xué)生
- 1 CSS選擇器【前】
- 2 confirm【前】
- 3 功能開(kāi)發(fā)與測(cè)試【后】
- 4 集成測(cè)試
- 5 定制提示框【前】
- 6 引入圖標(biāo)庫(kù)【前】
- 第九節(jié) 集成測(cè)試
- 第五章 登錄與注銷(xiāo)
- 第一節(jié):普通登錄
- 1 原型【前】
- 2 功能設(shè)計(jì)【前】
- 3 功能設(shè)計(jì)【后】
- 4 應(yīng)用登錄組件【前】
- 5 注銷(xiāo)【前】
- 6 保留登錄狀態(tài)【前】
- 第二節(jié):你是誰(shuí)
- 1 過(guò)濾器【后】
- 2 令牌機(jī)制【后】
- 3 裝飾器模式【后】
- 4 攔截器【前】
- 5 RxJS操作符【前】
- 6 用戶(hù)登錄與注銷(xiāo)【后】
- 7 個(gè)人中心【前】
- 8 攔截器【后】
- 9 集成測(cè)試
- 10 單例模式
- 第六章 課程管理
- 第一節(jié) 新增課程
- 1 初始化【前】
- 2 嵌套組件測(cè)試【前】
- 3 async管道【前】
- 4 優(yōu)雅的測(cè)試【前】
- 5 功能開(kāi)發(fā)【前】
- 6 實(shí)體監(jiān)聽(tīng)器【后】
- 7 @ManyToMany【后】
- 8 集成測(cè)試【前】
- 9 異步驗(yàn)證器【前】
- 10 詳解CORS【前】
- 第二節(jié) 課程列表
- 第三節(jié) 果斷
- 1 初始化【前】
- 2 分頁(yè)組件【前】
- 2 分頁(yè)組件【前】
- 3 綜合查詢(xún)【前】
- 4 綜合查詢(xún)【后】
- 4 綜合查詢(xún)【后】
- 第節(jié) 班級(jí)列表
- 第節(jié) 教師列表
- 第節(jié) 編輯課程
- TODO返回機(jī)制【前】
- 4 彈出框組件【前】
- 5 多路由出口【前】
- 第節(jié) 刪除課程
- 第七章 權(quán)限管理
- 第一節(jié) AOP
- 總結(jié)
- 開(kāi)發(fā)規(guī)范
- 備用