https://www.youtube.com/watch?v=B42zNPN2NvA&list=PL9LUW6O9WZqgUMHwDsKQf3prtqVvjGZ6S&index=8
S06 由 Kevin Yang 大大主講,藉由大大導讀文件後,我們就有能力能自己查文件了
NgModules很重要,因為幾乎所有的東西都要透過NgModules來封包
所有東西的最上層都要透過NgModules來管理
首先,先開好片頭導讀的文章,再跟著影片一起看,以後才有能力自己查文件
今天內容有:(方便search)
Feature Modules的種類
簡單看一下啟動順序
簡介@NgModule裡面各項目:declarations、imports、exports、providers、entryComponents、bootstrap
尤其是providers的各種用法:
useClass、useExisting、useValue
useFactory、deps
SharedModule小技巧
Routing modules 跟 Routed feature modules
@NgModule裡的providers補充
providers 的 useValue 的例子補充
provider多service instance及multi: true範例
service sigleton
參考文章:跨Component的互動
討論:NgModule怎麼切Module?
建立要準備執行Angular App的平台
產生platformBrowserDynamic()物件,呼叫該物件的bootstrapModule(),啟動AppModule
platformBrowserDynamic().bootstrapModule(AppModule)
platformBrowserDynamic().bootstrapModule(AppModule2) // 可以啟動第2個AppModule2
// AppComponent 為 Root Component,所有畫面的起點
// bootstrap 會被視為放入entryComponents:[]避免打包時被移除
bootstrap: [AppComponent]
以前總是亂加,try到東西跑得出來,有的東西亂放編譯也會過
@NgModule({
declarations: [ // 宣告,要用的component、directive、pipe要加進來才能用
AppComponent,
MyAppComponent,
HelloComponent
],
imports: [ // 必需@NgModule({有exports的module})才能import進來
BrowserModule,
AppRoutingModule,
xxModule
],
exports: [
xxModule // exports出來,才能被其他module來imports
]
// 跟DI(Dependency Injection)有關的,全部加在providers
// Service的相依注入符合SOILD原則
// 哪些Service允許被注入,也可在@Service中用providedIn設定
// 一個@NgModule會有1份Service instance,若有多個instance會取最近的那一個
providers: [
DataXXXService,
// 寫法2,provide是別名,useClass是實際用的Service
// 用途:很好抽換其他Service實作(但實作介面要一樣),對外的名稱不變
{provide: DataService, useClass: Data01Service},
// {provide: DataService, useClass: Data02Service},
// useExisting: 使用既有的Service instance
// 使用情境:DataXXXService實作很多方法,可用Interface來限制外面看得到的方法
{provide: DataServiceInterface1, useExisting: DataXXXService},
// {provide: DataServiceInterface2, useExisting: DataXXXService},
// useFactory: xxFunction() 會去呼叫function()回傳一個Service
{
provide: DataServiceInterface2,
useFactory: funFactory,
deps:[HttpClient] // 注入funFactory()需要用到的service
},
// useValue: 可用 數字 | 字串 | Object
// 數字 | 字串 要再使用 InjectionToken 強制產生 token
// 注入時用@Inject,如:
// constructor(@Inject(CONFIG_TOKEN) private config: Config){}
// 個人覺得這比較難,建議研讀 Mike 大大的文章:
// https://ithelp.ithome.com.tw/articles/10208240
// https://wellwind.idv.tw/blog/2018/11/07/mastering-angular-23-injection-tokens/
{ provide: token, useValue: 'A'}
],
entryComponents, // 加進來可避免被ng build --prod移除,如:動態載入的元件-Dialog
bootstrap: [AppComponent] // 只有最上層的Root Module才有
})
// useFactory用的
function funFactory(){
return new DataService();
}
// useValue用的
// 要import {Injectiontoken} from '@angular/core';
// 替 interface 產生 token
export const token = new Injectiontoken<Config>('abc');
export const token2 = new Injectiontoken<string>('abc');
export interface Config { LogLevel: string }
export class AppModule { }
請參考Mike大大的「[Angular 大師之路] Day 06 - 模組化的基本觀念」
https://ithelp.ithome.com.tw/articles/10203876
share.module.ts
@NgModule({
declarations: [aComponent,bComponent,...], // pipe跟directive應該也可以吧
imports: [aModule,bModule,...]
exports: [aComponent,bComponent,aModule,bModule,...]
})
export class SharedModule { }
your.module.ts
@NgModule({
...
imports: [SharedModule]
...
})
建個專案來觀察
app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Route } from '@angular/router';
const routes: Route[] = [
]
@NgModule({
imports: [
RouterModule.forRoot(routes) // 只有最上層的路由用forRoot,其他用forChild
]
})
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { FeatureModule } from './feature.module';
import { FeatureComponent } from './feature.component';
const routes: Routes = [
{ path: '', component: FeatureComponent},
// lazy loading的寫法
// 1.要延遲截入的模組,有自己的子路由
// 2.改用loadChildren
{ path: 'feature', loadChildren: './feature/feature.module#FeatureModule'}
];
@NgModule({
imports: [RouterModule.forChild(routes), FeatureModule],
exports: [RouterModule]
})
export class FeatureRoutingModule { }
1.data-service-interface.ts
export abstract class DataServiceInterface{
displayName(){}
}
// 在要使用DataService的xxx.module.ts裡,要import { DataServiceInterface }
// 因為interface不會輸出成javascript
2.data.service.ts
import { Injectable } from '@angular/core';
import { DataServiceInterface } from './data-service-interface';
@Injectable(
// providedIn,類似寫在providers裡
// 不同在於,providedIn是Tree-shakable Dependencies,若Service沒用到可被移除
{ providedIn: 'root' } // 註冊到最上層,只會有一份instance
)
// 在ts裡implements xxx,就會把xxx視為interface,就一定要實作裡面的function,否則會error
export class DataService implements DataServiceInterface {
constructor(){}
displayName(){}
}
Mike老師的 [Angular 大師之路] Day 21 - 在 @NgModule 的 providers: [] 自由更換注入內容 (2)
https://ithelp.ithome.com.tw/articles/10208240
好處:感覺就只有減少程式碼產生。有利於「測試」嗎?
如果原本有個Service,要用一個Object來取代
@Injectable({
providedIn: 'root'
})
export class DataService {
getData() {
return 100;
}
}
Object裡面定義一個function()
const newData = {
getData: () => {
return 999;
}
}
@NgModule({
providers: [
{
provide: DataServiceInterface,
useValue: newData, // Object不需要InjectionToken強制產生token
}
]
})
Kevin老師親手打的範例
data-service-interface.ts
export abstract class DataServiceInterface{
name // 當 multi: true ,用name來分辨instance
displayName(){}
}
data1.service.ts、data2.service.ts
...
export class DataService implement DataServiceInterface {
name = 'DataService1'; // 當 multi: true 時用來分辨instance
// name = 'DataService2'; // data2.service.ts
...
app.module.ts
import { DataServiceInterface } from './data-service-interface';
@NgModule({
...
providers: [
// 因為 provide 同名字,會適用比較上面的那個
{ provide: DataServiceInterface, useClass: Data1Service },
{ provide: DataServiceInterface, useClass: Data2Service }
// 如果加了 multi: ture 會取到instance的陣列
// 情境:form的validator
{ provide: DataServiceInterface, useClass: Data1Service, multi: true },
{ provide: DataServiceInterface, useClass: Data2Service, multi: true }
// [ Data1Service, Data2Service]
// 0: Data1Service{ name: "DataService1" , __proto__:Data1Service}
// 1: Data2Service{ name: "DataService2" , __proto__:Data2Service}
],
...
})
...
app.component.ts
import { Component } from '@angular/core';
import { DataServiceInterface } from './data-service-interface';
@Component({...})
export class Appcomponent {
// 實際會注入 Data1Service 那個 instance
constructor(private service: DataServiceInterface){
service.displayName();
}
}
angular service singleton or not
預設service的instance只會有一份,所以angular的status管理很方便
// DI 的 service 設 public,別的 component 才取得到
constructor(public
service: DataServiceInterface){}
1.https://angular.io的DOCS
2.FUNDAMETALS / Components & Templates / Component Interaction
https://angular.io/guide/component-interaction
@Input、@Ouput
使用情境:子Component只負責顯示(畫面互動),不負責商業邏輯,的時候
例如:angular material的datapicker-input.ts
Service
使用情境:購物車
當「複雜度高」或「需要高度互動性」時,可使用RxJS
例用Observable (當有值發生時)-推送-> Subject
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable()
export class MissionService {
// Observable string sources
private missionAnnouncedSource = new Subject<string>();
private missionConfirmedSource = new Subject<string>();
// Observable string streams
missionAnnounced$ = this.missionAnnouncedSource.asObservable();
missionConfirmed$ = this.missionConfirmedSource.asObservable();
// Service message commands
announceMission(mission: string) {
this.missionAnnouncedSource.next(mission);
}
confirmMission(astronaut: string) {
this.missionConfirmedSource.next(astronaut);
}
}
[Angular 深入淺出三十天] Day 20 - 路由(三)
例如:https://angular.io/guide/component-interaction^^^^^
^^^^^^^^^^^^^^^^^^^^整合成一個Module
一堆Component