Creation of AoT-compliant service factories
I am trying to create a factory service for a cache service. The basic requirement is that each service can be installed with a different string each time.
The end result will have multiple cache services , each defined uniquely databaseName
. Each cache can have one or more stores , also identified as unique storeName
. Other services will be able to use these stores:
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')
My current implementations are as follows:
//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 ) {} }
Services are factored:
// 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'); }
And then provided:
// 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 };
Declared in the main module:
// 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 ] }; } }
And finally, it is used in the service:
//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 ) {} }
Everything works fine when using JIT compilers. When I try to compile AoT, I start getting Warning: Can't resolve all parameters for x in y: (?). This will become an error in Angular v5.x
for cache and storage string options.
I have detailed my problem in this question: Toxify for AoT compilation . Now, this is similar to using strings, as the parameters for are @Injectable()
completely impossible with the AoT compiler. Is there any other way to create a factory for services initialized using a string as I described?
I have created a workable example in this repository . Checkout aot
and npm install
and then npm run build.prod.rollup.aot
should work just like that.
source to share
Based on user feedback @estus and @Toxicable Factory
, there shouldn't be a class to be set with only @Injectable
. Removing this tag from the classes IndexedDBCache
and IndexedDBStore
solve the problem.
Factory
must also be in a specific form
export function mainIndexedDBCacheFactory() { return new IndexedDBCache('main'); }
Other iterations of this definition will not work.
export const mainIndexedDBCacheFactory = function() { return new IndexedDBCache('main'); }; export let mainIndexedDBCacheFactory = function() { return new IndexedDBCache('main'); }; export let mainIndexedDBCacheFactory = () => new IndexedDBCache('main');
A export
prefix is required even if the definition is in the same file as Provider
.
source to share