创建 AoT 兼容的服务工厂

Creating AoT compatible Service Factories

我正在尝试为缓存服务创建服务工厂。主要要求是每次都可以使用不同的字符串实例化单个服务。

最终结果将有多个 缓存 服务,每个服务由唯一的 databaseName 定义。每个缓存可以有一个或多个 存储 ,也由唯一的 storeName 定义。其他服务将能够使用这些商店:

mainCache                         = new Cache('main')
  ├── userStore                   = new Store(mainCache, 'user')
  │     ├── userService
  │     └── authenticationService
  │
  └── taskStore                   = new Store(mainCache, 'task')
        └── taskService

fooCache                          = new Cache('foo')
  └── barStore                    = new Store(otherCache, 'bar')

我目前的实现是这样的:

//indexeddb-cache.ts
import { Injectable } from '@angular/core';

@Injectable()
export class IndexedDBCache {
  constructor(
    protected databaseName : string
  ) {}
}

// indexeddb-store.ts
import { Injectable } from '@angular/core';
import { IndexedDBCache } from './indexeddb-cache';

@Injectable()
export class IndexedDBStore {
  constructor(
    protected database : IndexedDBCache,
    protected storeName : string
  ) {}
}

服务工厂化:

// test-api.cache.factory.ts
import { IndexedDBCache } from '../indexeddb-cache/indexeddb-cache';
import { IndexedDBStore } from '../indexeddb-cache/indexeddb-store';

// factory functions for FactoryProviders as exports instead of inline:
// https://github.com/rangle/angular-2-aot-sandbox#func-in-providers-usefactory-top
export function mainIndexedDBCacheFactory() { return new IndexedDBCache('main'); }
export function userIndexedDBStoreFactory(testIndexedDBCache: IndexedDBCache) { return new IndexedDBStore(testIndexedDBCache, 'user'); }
export function taskIndexedDBStoreFactory(testIndexedDBCache: IndexedDBCache) { return new IndexedDBStore(testIndexedDBCache, 'task'); }

然后提供:

// test-api.cache.ts
import { InjectionToken, Provider } from '@angular/core';
import { IndexedDBCache } from '../indexeddb-cache/indexeddb-cache';
import { IndexedDBStore } from '../indexeddb-cache/indexeddb-store';
import { mainIndexedDBCacheFactory,
         taskIndexedDBStoreFactory,
         userIndexedDBStoreFactory } from './test-api.cache.factory';

// Caches

export const mainIndexedDBCache = new InjectionToken<IndexedDBCache>('mainIndexedDBCache');

export let mainIndexedDBCacheProvider : Provider = {
  provide: mainIndexedDBCache,
  useFactory: mainIndexedDBCacheFactory
};

// Stores

export const userIndexedDBStore = new InjectionToken<IndexedDBStore>('userIndexedDBStore');

export let userIndexedDBStoreProvider : Provider = {
  provide: userIndexedDBStore, deps: [mainIndexedDBCache],
  useFactory: userIndexedDBStoreFactory
};

export const taskIndexedDBStore = new InjectionToken<IndexedDBStore>('taskIndexedDBStore');

export let taskIndexedDBStoreProvider : Provider = {
  provide: taskIndexedDBStore, deps: [mainIndexedDBCache],
  useFactory: taskIndexedDBStoreFactory
};

在主模块中声明:

// test-api-module.ts
import { NgModule, ModuleWithProviders } from '@angular/core';
import { mainIndexedDBCacheProvider,
         taskIndexedDBStoreProvider,
         userIndexedDBStoreProvider } from './test-api.cache';

@NgModule({
  imports: [], declarations: [], exports: []
})
export class TestAPIModule {
  static forRoot(): ModuleWithProviders {
    return {
      ngModule: TestAPIModule,
      providers: [
        mainIndexedDBCacheProvider,
        userIndexedDBStoreProvider,
        taskIndexedDBStoreProvider
      ]
    };
  }
}

最后在服务中使用:

//user-service/user.service.ts
import { Inject, Injectable } from '@angular/core';
import { IndexedDBStore } from '../../indexeddb-cache/indexeddb-store';
import { userIndexedDBStore } from '../test-api.cache';

@Injectable()
export class UserService {
  constructor(
    @Inject(userIndexedDBStore) protected cache : IndexedDBStore
  ) {}
}

使用 JIT 编译器时一切正常。当我尝试进行 AoT 编译时,我开始接收缓存和存储服务的字符串参数 Warning: Can't resolve all parameters for x in y: (?). This will become an error in Angular v5.x

我之前在这个问题中详细说明了我的问题:。现在看起来使用字符串作为 @Injectable() 的参数对于 AoT 编译器来说是完全不可能的。是否有任何其他方法可以为使用我所描述的字符串初始化的服务创建工厂?

我在 this repository 中创建了一个可行的示例。检查 aot 分支和 npm install 后跟 npm run build.prod.rollup.aot 应该就像那样工作。

根据用户@estus 和@Toxicable 的反馈,仅由 Factory 实例化的 class 不应是 @Injectable 本身。从 IndexedDBCacheIndexedDBStore class 中删除此标签将解决问题。


Factory也必须是特定的形式

export function mainIndexedDBCacheFactory() { return new IndexedDBCache('main'); }

此定义的其他迭代将不起作用。

export const mainIndexedDBCacheFactory = function() { return new IndexedDBCache('main'); };
export let mainIndexedDBCacheFactory   = function() { return new IndexedDBCache('main'); };
export let mainIndexedDBCacheFactory   = () => new IndexedDBCache('main');

export prefix is required 即使定义与 Provider.

在同一个文件中