開端:
由於這幾天在做 界面隔離(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。除了降低耦合性還增加了彈性與容易測試的優點。
首先我們先建立一個範例來看看在切割 Module 下有用 Lazy loading module 跟沒用的差別。
ng new mytestproj --routing
非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 使用的 UserService 與 Mymodule 使用的 UserService 是不同個。
深入探討 Angular 的 DI 與 Provider 網址已經換啦
回覆刪除https://old-oomusou.goodjack.tw/angular/di/