Angular 依赖注入学习笔记之工厂函数的用法
网址:https://angular.institute/di
We can transfer any data through our apps, transform it and replace it.
我们能传递任何数据到我们的应用里,改变其形态,并且替换。
Another case: document and fetch can work in your browser correctly. But one day you need to build your app in SSR or precompile with nodejs. Global entities might be missing or be different in that environment.
document 和 fetch 能在浏览器环境下运行。但是如果在 SSR 下 build 应用,或者用 nodejs precompile,那么这些对象在新的环境下不再可用。
Dependency Injection mechanism manages dependencies in your application. For you as an Angular developer, it is a straightforward instrument. There are two operations: you can provide something into your DI tree or take something from it.
依赖注入机制管理应用的依赖。对于 Angular 开发者来说,有两种操作:
提供数据到依赖注入树中
从依赖注入树中获取数据
The magic is the order in which you provide and take your dependencies. Angular creates a class instance when you ask for this the first time.
当我们试图在 DI 树里第一次获取实例时,Angular 负责实例化。
Providing value is normally used with InjectionToken. This object is like a key for DI mechanism.
我们也可以用依赖注入提供常量,通常借助 InjectionToken. 这个令牌类似依赖注入机制中的 key.
You say "I want to get this data with this key" and ask DI in a component "Do you have something for this key?"
我们使用 InjectionToken 作为 key,询问 Angular 依赖注入机制,“你维护的资源里,有针对这个 key 的值吗?”
看个具体的例子。
export const API_URL = new InjectionToken<string>('The URL of API');
在 api-url.token.ts 里,我们从 @angular/core 里导入了标准的 InjectionToken 构造器,其类型为 string,描述信息为:The URL of API.
在 app.module.ts 里,导入这个 API_URL token,然后在 module 的 NgModule 注解里,使用 useValue 提供 token key 代表的具体值:
如何使用这个 token 呢?参考下图代码:
export class AppComponent { constructor(@Inject(API_URL) readonly apiUrl: string) { /** * Here we asked for something with a key API_URL. * There is our string in DI and we get it */ console.log(apiUrl); } }
语义就是,在 app Component 里,使用 @Inject 注解,向 DI 树里查询,是否存在 key 为 API_URL 的注入值。
We can replace token value at any level of DI tree without any changes in a component - 我们可以在 DI 树上的任意层次结构里,替换 token 的值,而不需要修改 Component
We can mock a token value providing suitable data in tests - 在测试代码里,我们可以 mock 一个 token 值
The component class is fully isolated and can be used without any context
Providing a factory
这是 Angular 依赖注入的高级用法之一。
You can provide some value combining and transforming data from other tokens.
我们可以在 factory 代码里编写一些业务逻辑,执行一些数据结构变换的操作。
看个例子:
定义一个函数:
import { NgModule } from "@angular/core"; import { BrowserModule } from "@angular/platform-browser"; import { FormsModule } from "@angular/forms"; import { AppComponent } from "./app.component"; import { PRESSED_KEY } from "./tokens/pressed-key"; import { Observable, fromEvent } from "rxjs"; import { map } from "rxjs/operators"; import { DOCUMENT } from "@angular/common"; /** * It is a token value factory. * * The factory is called when app.component.ts asks for * the token PRESSED_KEY the first time */ function pressedKeyFactory(documentRef: Document): Observable<string> { return fromEvent(documentRef.body, "keydown").pipe( map((event: KeyboardEvent) => event.key) ); }
构造一个 Observable 对象,当键盘被按下时,从 KeyboardEvent 里解析出具体哪一个键被按下,然后将 key 值通过 Observable 对象返回。
这个函数被当成一个工厂函数,本身接收类型为 Document 的参数,这个参数就是其依赖。
我们通过下列的语法,将名为 PRESSED_KEY 的令牌,和该工厂函数绑定在一起。
@NgModule({ imports: [BrowserModule, FormsModule], declarations: [AppComponent], bootstrap: [AppComponent], providers: [ { provide: PRESSED_KEY, /** * Here you can provide any token and its value will be injected * as a factory function argument */ deps: [DOCUMENT], useFactory: pressedKeyFactory } ] })
该 token 的消费方式: