Angular Provider 在 Lazy loading module 中與一般做 DI service 的不同。

開端:

由於這幾天在做 界面隔離(Interface-Segregation Principle) 的 design pattern 所以在細看 Angular DI 這方面的資料。主要目的就是把原本的 service 在切出一個 interface 來達到 界面隔離(以下簡稱ISP) 。在避免自己忘記之前做一下筆記!😆

系統環境:


  • Ubuntu 18.04 LTS
  • Angular Cli 7.2.3
Angular 提供了 DI 來讓 service 不需要在每個用到的 Component 裡面一直 new service() 產生 instance。除了降低耦合性還增加了彈性容易測試的優點。

非lazy loading module


首先我們先建立一個範例來看看在切割 Module 下有用 Lazy loading module 跟沒用的差別。

 ng new  mytestproj --routing




進到目錄後我們建立一個新 Module 叫做 mymodule

 ng g m mymodule  --routing



接著建立一個屬於 mymodule 的 Component 取名為 user

 ng g c  mymodule/user


接著建立一個 user Service 提供 user Component 使用。

  ng  g s  mymodule/user


所以檔案分佈大概如下:

我們的 user.service.ts 內容如下:
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class UserService { name = 'Hsc'; constructor() { } changeName() { this.name = 'Clover'; } }

裡頭只有一個變數跟一個簡單的 function。

接著我們把 user.component.html 修改如下:
<p> 我是 {{userServ.name}} --- UserModule </p> <button (click)="userServ.changeName()">Change Name</button>

其實就只是顯示 user service 內的 name這個內容,然後按了按鈕之後會把名字改成'Clover' 這麼簡單。

至於 user.component.ts 也很簡單:
import { Component, OnInit } from '@angular/core'; import { UserService } from './../user.service'; @Component({ selector: 'app-user', templateUrl: './user.component.html', styleUrls: ['./user.component.css'] }) export class UserComponent implements OnInit { constructor(private userServ: UserService) { } ngOnInit() { } }

就只是 DI UserService 進來而已。

接著我們修改  mymodule-routing.module.ts 讓 router-outlet 可以一開始載入這個 module 的 Component
import { UserComponent } from './user/user.component'; import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; const routes: Routes = [ {path: '', component: UserComponent} ]; ..... ....

然後在 mymodule.module.ts 內 providers UserService 進來:
import { UserService } from './user.service'; import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MymoduleRoutingModule } from './mymodule-routing.module'; import { UserComponent } from './user/user.component'; @NgModule({ declarations: [UserComponent], imports: [ CommonModule, MymoduleRoutingModule ], providers: [UserService] }) ........ ......

接著在 app.module.ts import MymoduleModule 作為我們 『 lazy loading module』的用法:
import { MymoduleModule } from './mymodule/mymodule.module'; 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, MymoduleModule, AppRoutingModule ], .... ....

我在app.component.html 加入下列程式碼用來顯示 UserService 的 name 內容:
<p> I am {{userName}} --- (AppModule) </p> <router-outlet></router-outlet>

最後 app.component.ts 就是把 UserService  DI進來並且使用 get 來抓取 UserService 的name 內容:
import { Component } from '@angular/core'; import { UserService } from './mymodule/user.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor(private userServ: UserService) { } get userName () { return this.userServ.name; } }

最後我們執行  ng serve 開啟 localhost:4200 就會看到網頁如下:

當我們按下 Change Name 按鈕時候,你看到上下兩個 name 都會跟著改變。
這代表不管是 Mymodule 內部使用的 UserService 或是 app module 這個 root 所使用的 UserService 都是同一個 UserService 的 instance 。( singleton )

Lazy loading Module

接著我們把上述的程式改成採用 lazy loading module 的方式來載入 Mymodule 看看有什麼不同。

首先把 app.module.ts 內有關 MymoduleModule 的東西拿掉:
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 ], ........
............

修改 app-routing.module.ts 的 routes 如下:
const routes: Routes = [ { path: '', loadChildren: './mymodule/mymodule.module#MymoduleModule' } ];

接著我們從新執行一次 ng serve 然後開啟 localhost:4200
這時候按 Change Name 你會發現只有 Mymodule 內的 user component 的 name 有改變

由於 lazy loading module 會在使用者有使用到才會載入Module 並非將所有 module 全都包在 app 裡面,加上我們在 MymoduleModule 內有 providers: [ UserService ] 所以在 載入 Mymodule 之後才會建立一個新的 UserService instance 供 Mymodule 使用

所以 app root module 使用的 UserServiceMymodule 使用的 UserService不同個

REF:


留言

  1. 深入探討 Angular 的 DI 與 Provider 網址已經換啦
    https://old-oomusou.goodjack.tw/angular/di/

    回覆刪除

張貼留言