Skip to content

Instantly share code, notes, and snippets.

@ghetolay
Last active October 2, 2020 13:22
Show Gist options
  • Save ghetolay/6d4dc2c6d26c9b850c1e6be686866c7f to your computer and use it in GitHub Desktop.
Save ghetolay/6d4dc2c6d26c9b850c1e6be686866c7f to your computer and use it in GitHub Desktop.
Create singleton provider for Angular
/*
This function allows you to create singleton service that'll stay singleton throughout all the app.
Even(especially) if you use lazy module.
This covers that problem : https://angular.io/docs/ts/latest/guide/ngmodule.html#!#why-_userservice_-isn-t-shared
Idea isn't mine, it's how they handle it on Angular Material. I'm just creating an helper function for it.
Basically we create a factory provider instead of a plain class provider and inject the service into the factory function.
If the service is present it means it has already been instantiated somewhere in the injector tree and we can return it.
If injection fails and service is nulll this means we are the first provider so we can instantiate and return the service.
*/
export function createSingletonProvider<T extends Type<any>, R>(
service: T,
token?: T | InjectionToken<R>,// `token = service` breaks aot so we use `token || service` instead below
// dependencies must be in same order as the service's constructor signature.
dependencies: any[] = [],
// fake argument here just a trick to create a var
ctorToken = new InjectionToken('service ctor')
): Provider[] {
return [
{
provide: ctorToken,
useValue: service
}, {
provide: token || service,
deps: [ctorToken, [new Optional(), new SkipSelf(), token || service], ...dependencies],
useFactory: createSingletonFactory
}
];
}
export function createSingletonFactory(serviceCtor: Function, parentService?: Type<any>, ...dependencies: any[]) {
if (parentService !== null) {
return parentService;
}
const instance = Object.create(serviceCtor.prototype);
instance.constructor.apply(instance, dependencies);
return instance;
}
@Injectable()
export class Service {
}
@Injectable()
export class ServiceWithDeps {
constructor(dep1: Dep1, dep2: Dep2){ }
}
/* use */
@NgModule({
createSingletonProvider(Service, Service),
// /!\ Dependencies order must be the same as the constructor signature
createSingletonProvider(ServiceWithDeps, ServiceWithDeps, [Dep1, Dep2])
})
/* instead of */
@NgModule({
providers: [
Service,
ServiceWithDeps
]
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment