---
# System prepended metadata

title: '**Angular NgModule與RXJS**'
tags: [RXJS, Angular]

---

<style>
.tilte-bg {
  background: #C25F3C;
  color:#FFF;
  padding:2px 8px 5px 8px;
  border-radius:5px;
  font-size:13px;
  display:inline-block;
}

.warning{
  color:#A60101;
}

.tip {
color:#A60101;
}
p{
  font-size:15px;
}
</style>

# **Angular NgModule與RXJS**
###### tags: `Angular`、`RXJS`
> 2021/03/20 筆記



功能介紹
===

<!-- **[NgModule](/features)**
**[中文版](/features-tw)**
**[RXJS](/features-jp)** -->

**[NgModule](#NgModule)**
**[特性模組](#特性模組)**
**[服務與依賴注入](#服務與依賴注入)**
**[RXJS](#RXJS)**



# NgModule
* [參考官方NgModule](https://angular.tw/guide/architecture-modules)
* @NgModule獲取一個元數據對象(imports、declarations、exports...等)，它會告訴Angular如何編譯和啟動本應用。
* 每個 Angular 應用都至少有一個 NgModule，也就是根模組 AppModule。


**@NgModule獲取一個元數據對象**
* imports（匯入表）
使用其他的模組提供的元件
* declarations（可宣告物件表）
屬於該功能模組的Component、Drictive、Pipe。
* exports（匯出表）
可以提供給其他模組使用的元件
* providers
模組可使用的服務
* bootstrap
設定進入點，只有根模組才用得到

# 特性模組
* 特性模塊是為了項目中的一些特定的應用準備的，比如路由/表單
* 特性模塊導入NgModule 和 CommonModule(可使用 ngIf 和 ngFor)，不需要導入 BrowserModule 因BrowserModule 為瀏覽器所做的應用配置只會使用一次所以只需要在根模塊中導入一次。

# 服務與依賴注入
目的是為了當類需要執行其功能時，類會從外部源中請求獲取依賴，而不是自己創建它們。
* @NgModule.providers 中的服務提供者具有全應用級作用域
* 只匯入一次帶提供者的模組，最好在應用的根模組中
* AppComponent 服務只在該元件及其子元件樹中才能使用。 它們具有元件級作用域。在根模組 AppModule 中註冊全應用級服務。
* injector 默認提供的 DI 都是單例實例，若是對 DI 實例做變化下次使用這個 DI 實例時，會是已經變化過的實例。

```
step1 : platformBrowserDynamic()  
step2 : 建立要準備執行 Angular 應用程式的平台」， 
step3 : 取得一個 PlatformRef 物件 ( 包含Injector ) 
step4 : 呼叫其中的 bootstrapModule() 來指定要啟動的第一個模組  step5 : bootstrapModule(AppModule)

// main.ts

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.log(err));

```


:::warning
如果我們有一個帶有某些提供程序的模塊，並直接在AppModule或已在AppModule中導入的任何其他模塊中直接導入此模塊，則這些提供程序將成為應用程序範圍的提供程序。
:::


```
// src/data/comments.ts
  export default [{
    "postId": 1,
    "id": 1,
    "name": "id labore",
    "email": "Eliseo@gardner.biz",
    "body": "laudantium enim quasi est quidem magnam voluptate ipsam"
  },
  {
    "postId": 1,
    "id": 2,
    "name": "quo vero ",
    "email": "Jayne_Kuhic@sydney.com",
    "body": "est natus enim nihil est dolore omnis"
  }];

```

在服務中引入並用一個方法返回：

```
// comment.service.ts
import { Injectable } from '@angular/core';
import comments from '../data/comments';
@Injectable({
  providedIn: 'root'
})
export class CommentService {
  constructor() { }
  getComments() {
    return comments;
  }
}

```
創建一個service組件獲取服務：

```
// service.component.ts
...
export class ServiceComponent implements OnInit {
  constructor(private commentService: CommentService) { 
    console.log(this.commentService.getComments()); // 能够获取到模拟的数据
  }
  // ...
}

```

如果沒有使用依賴注入，常見方法新增一個class如下:

```
// comment.service.ts

import comments from '../data/comments';
export class CommentService {
  constructor() { }
  getComments() {
    return comments;
  }
}

```

```
// service.component.ts

export class ServiceComponent implements OnInit {
  private comments = [];
  private commentService: CommentService;
  constructor() { 
    this.commentServe = new CommentService();
     console.log(this.commentServe.getComments());
  }
  // ...
}


```
ElementInjector元素注入器與ModuleInjector模塊注入器

主要規則是：當組件或指令需要解析依賴時，Angular使用Merge Injector來遍歷element injector tree，如果沒找到該依賴，則遍歷module injector tree 去查找依賴。


* ModuleInjector層次結構——在@NgModule()或@Injectable()中提供的服務。
* ElementInjector層次結構——在@Directive()或@Component()中提供的服務


![](https://i.imgur.com/W7Sh2Ko.jpg)






如果子組件需要解析依賴，那Angular會首先查找該組件的元素注入器，也就是檢查elRef.element.allProviders|publicProviders，然後向上遍歷父視圖元素檢查元素注入器的服務提供商（1），直到父視圖元素等於null（2），則返回startView（3），然後檢查startView.rootData.elnjector（4），最後，只有當令牌沒找到，再去檢查startView.rootData module.injector（5）。（注：元素注入器->組件注入器->模塊注入器）

@Host裝飾器不可以用來在子組件中解析來自父組件的依賴提供者，意味著該裝飾器的依賴解析機制不可以用於多級組件注入器。





# RxJS 

Angular 使用 RxJS 來幫忙處理非同步事件
Reactive programming (響應式程式設計) 是一種「面向資料流」和「變更傳播」的非同步處理的程式設計模式，RxJS 是實現成JavaScript Promise的版本。

**observable (觀察者)**
* 觀察者模式(Observer Pattern)
* 觀察者模式有二個角色：觀察者(Observer)、被觀察者(Observable)
* Observer -> 訂閱(Subscribe) -> Observable
* Observable 有事件發生 -> 通知(Notify) -> Observer


**stream (資料流)**
* 每一個 Observable 就是一個資料流
* 什麼是資料流？可以想像成是會一直增加元素的陣列，當有新的事件發生就 push 進去。
* 專業說法：「時間序列上的一連串資料事件」

**subscribe (訂閱)**
```
observable.subscribe(observer);
// [...].subscribe({...});
// [資料流].sbscribe({處理資料流的程式碼});
```
observer透過訂閱的動作收到消息，會有三種回應的方式
```
observable.subscribe(
    (data) => {
      // 成功的時候做什麼
    },
    (error) => {
      // 失敗的時候做什麼
    },
    () => {
      // 不論成功或失敗，都要做什麼
    }
);
```


Observable是資料流，觀察者收到資料之前，中間可以對資料流做一連串的轉換及處理，變成另一個資料流。而中間轉換的過程，在RxJS的世界中是使用operator來完成。[Operator](https://ithelp.ithome.com.tw/articles/10209779)


* RxJS 和 Promise 用法類似但關鍵詞不同 
* RxJS 和 Promise 差異 :
RxJS 可以使用(unsubscribe)中途撤回取消訂閱、訂閱後多次執行、RxJS 也提供多種工具函式 ([Operator](https://ithelp.ithome.com.tw/articles/10209779)) 。 範例 : [RxJS 和 Promise 使用差異](https://codepen.io/DoraLee000/pen/oNYKzRB?editors=0010)


![](https://i.imgur.com/aSF0xYb.gif)

從動圖上來看資料流（生產線上的月餅半成品）透過不同的operator（操作，ex.壓扁/加料/包裝/塑型）來轉化成不同樣子的資料流，用以面向各式需求的處理。




