I have a service/provider, let's say it's call ServiceA1, in a module A marked as #Injectable().
In the same module I have another service/provider, let's say it's call ServiceA2, marked as #Injectable().
I want to inject ServiceA1 in ServiceA2 I try with:
This code throw an error: serviceA1 undefined.
My module.ts
Do I have to make another module to use serviceA2 in serviceA1?
If you're injecting a service directly by the class, you do not need the #Inject():
#Injectable()
export class ServiceA2 {
constructor(private serviceA1: ServiceA1) {
}
This is the same as:
#Injectable()
export class ServiceA2 {
constructor(#Inject(ServiceA1) private serviceA1: ServiceA1) {
}
Note that you injected #Inject('ServiceA1') instead of #Inject(ServiceA1). This is matching by a string token instead of a class and can therefore not be resolved.
If you wanted to inject the class by a string token, you would need to declare the provider in your module like this:
providers: [
ServiceA2,
{provide: 'ServiceA1', useClass: ServiceA1},
]
This also works within the same module.
Anyone running into this: make sure you remember to annotate the injected service with #Injectible.
Related
I'm trying to create a module with a service and would like to create a custom Inject decorator for it.
Let's call the Module a Store module, which provides and exports a StoreService and has to have a name. This is the Module:
import { Module } from '#nestjs/common';
import { StoreService } from './store.service';
#Module({})
export class StoreModule {
static register(
name: string,
): DynamicModule {
return {
module: StoreModule,
providers: [
{
provide: STORE_NAME,
useValue: name,
},
StoreService,
],
controllers: [],
exports: [StoreService],
};
}
}
The 'name' is what makes the store unique.
Now I'd like to create a decorator that fetches the store by name, like #InjectStore(name). I've tried to create a token based on the name, like STORE_SERVICE_${name} and injecting that, but the StoreModule needs to be registerred globally for that.
When I set global: true, it causes weird behaviour inside the module itself. Because StoreService is registered multiple times for multiple stores. For example, when I have a store cats and a store dogs, and I create a service inside the module and inject the store via constructor(private readonly store: StoreService), it'll get one of the two.
A workaround for this behaviour is probably to use the #InjectStore decorator, but because I came across this behaviour, I'm wondering if I'm missing something or if there's a better solution, because I want to make sure it injects the right store.
I've searched far and wide for an example implementation in NestJS modules, but just couldn't find what I was looking for.
Thanks in advance!
I was wondering whether I can use a factory to initialize a controller and then add it to a module. Code could look something like this, but this is not working:
const controllerFactory = {
provide: DefinitionController,
useFactory: async (service: DefinitionService) => {
//initialization of controller
return new DefinitionController();
},
inject: [DefinitionService],
};
#Module({
controllers: [controllerFactory],
providers: [DefinitionService],
})
export class DefinitionModule {}
It looks like using factories for controllers is not supported, but I am not sure. There is an example of using factory for providers, but I cannot find anything for controller in documentation or on google.
It's not possible to define your controller with an async factory comparable to custom providers. You cannot add dynamic endpoints/routes unless using the native express/fastify instance:
At the moment there is no way to register a route dynamically except
by using the internal HTTP / Fastify / Express instance
There is an issue where a dynamic routing module is discussed but this will probably not be part of nest very soon:
At the moment both Kamil and I are really busy, so this issue may take
some time - except someone else takes on the task :)
But you can use the OnModuleInit lifecycle event to do static initialization:
#Injectable()
export class DefinitionController implements OnModuleInit {
onModuleInit() {
console.log(`Initialization...`);
}
It will be called once when your app starts and has access to the injected providers in your controller, e.g. your DefinitionService.
I'm creating a #Log() Decorator Function for debugging purposes;
I want that Decorator to delegate some of it's logic to a LoggingService that in turn depends on other services from the app...
I've been trying a lot of different things, the simplest/most straightforward way was to cache the Main (or Shared) Module's Injector as a static prop on the module itself (see StackBlitz example linked below), and that works for lazy-loaded modules, but not for eagerly loaded ones...
Non-working poc: https://stackblitz.com/edit/angular-j1bpvx?file=app%2Fdecorator.ts
Is there a way I could mkae use of that Service in there??
Thanks!
Class decorator is executed once on class definition. In order to avoid race condition when calling AppModule.injector.get(LoggingService) it should be moved to the place where AppModule.injector is already defined, i.e. class method.
It should be:
constructor.prototype[hook] = function (args) {
const loggingService = AppModule.injector.get(LoggingService);
loggingService.log({ ... })
...
This also creates tight coupling with AppModule and prevents the units from being reused or tested separately from it. It's recommended to use another object to hold injector property, e.g. assign injector not in main but in child module that is imported into AppModule:
export class InjectorContainerModule {
static injector: Injector;
constructor(injector: Injector) {
InjectorContainerModule.injector = injector;
}
}
Try stackblitz fixed
This will print
LoggingService: HelloComponent - ngOnInit was called
Minor changes - basically using ReflectiveInjector as in angular Injector#example
import { ReflectiveInjector } from '#angular/core';
const injector = ReflectiveInjector.resolveAndCreate([
{provide: 'loggingService', useClass: LoggingService}
]);
const loggingService = injector.get('loggingService');
I am sure you can use useExisting and use LoggingService as provider in your app module.
I'm using angular. I already know that when an appmodule is importing modules which declares providers, the root injector gets them all and the service is visible to the app - globally. (I'm not talking about lazy loaded modules)
But is it possible that each module will have its own instance of the service?
I thought of maybe something like this :
#NgModule({
providers: [AService]
})
class A {
forRoot() {
return {
ngModule: A,
providers: [AService]
}
}
forChild() {
return {
ngModule: A,
providers: [AService]
}
}
}
But I don't know if it's the right way of doing it
Question
How can I accomplish service per module ?
STACKBLITZ : from my testing , they are using the same service instance
When we provide a service in a feature module that is eagerly loaded
by our app's root module, it is available for everyone to inject. - John Papa
So looks like there is no way to inject at feature-module level, as there are no module level injectors other than the one at root module.
But angular has nodes at each component level for the injector, so such a scenario will have to use coponent level-injectors I guess.
You can also have a parent component inject the service for different children sharing the same instance.
One way is to provide the services at component level. Not sure if that will work for you.
Also, check the multiple edit scenario in the docs
https://angular-iayenb.stackblitz.io
import { Component, OnInit } from '#angular/core';
import {CounterService} from "../../counter.service"
#Component({
selector: 'c2',
templateUrl: './c2.component.html',
styleUrls: ['./c2.component.css'],
providers:[CounterService]
})
export class C2Component implements OnInit {
constructor(private s:CounterService) { }
ngOnInit() {
}
}
Question
How can I accomplish service per module ?
Not with the default Injector. Default Injector keeps nodes at root level and component level, not at feature-module level. You will have to have a custom Injector if there is a real scenario.
Edit: the previous answer from me is not completely correct. The correct way to make this work is to use lazy loaded modules, provide the services there. The given service should not be provided with a static forRoot() method somewhere because then the lazy loaded module will access the root injector.
There is no actual reference to do so because that is not how angular is designed but if you want it that way you have to the opposite of
https://angular.io/guide/singleton-services#providing-a-singleton-service
and
https://angular.io/guide/singleton-services#forroot
Old not completely correct:
You simple have to declare the service you want to be single instance for each and every module within the providers meta data of each and every module. Then Angular will not reuse any instance of the service.
Giving scenario: You have two modules, ModuleA and ModuleB, both need the service but different instance, then you will declare them in the providers section of ModuleA and ModuleB.
Reference:
https://angular.io/guide/singleton-services
Angular 2+ registers providers in the following way:
// #NgModule decorator with its metadata
#NgModule({
declarations: [...],
imports: [...],
providers: [<PROVIDERS GO HERE>],
bootstrap: [...]
})
export class AppModule { }
I want to register application-scoped providers separately from this declaration site.
Specifically, I am using NSwag to generate service clients for my entire Web API and I want to dynamically add them all as providers. However, I'm not sure how to do that since #NgModule is an attribute applied to this AppModule class.
Is this possible?
Any DI provider needs to be included in the module at compile time.
Since Angular dependency injection works with Typescript type symbols / tokens, there's no Javascript functionality to accomplish the same task after it's compiled.
What you can do is dynamically add the provider at compile time, like so:
import { Load, SomeToken } from '../someplace';
#NgModule({
declarations: [...],
imports: [...],
providers: [
{
provide: SomeToken,
useValue: Load(someVariable)
],
bootstrap: [...]
})
export class AppModule { }
and then implement the Load function and token elsewhere:
export const SomeToken = new OpaqueToken<any>('SomeToken');
export const Load = (someVariable) => {
// logic to return an #Injectable here. Variable provided could be something like an environment variable, but it has to be exported and static
}
This approach of course has the limitation of needing to be known at compile time. The other approach is to either globally import all providers that are needed throughout the app regardless of circumstance and then lazy load components that have the appropriate provider injected for that circumstance (Angular will not initialize the provider until a component that utilizes it is initialized), or create a provider that in itself is able to perform the logic regardless of the dynamic criteria. An idea for that is to create another service that utilizes this service and resolves things based off of that dynamic criteria (i.e. you could have a method called GetLoginInfo on the first service and the second service would be able to resolve the correct API call for that method.)
If it's just API information you need (i.e. URLs), then you could possibly achieve the above by grabbing the URL information from a config.json file or API call, and inject those values into the service so the calls and tokens remain the same but use different values. See here for more information on how to accomplish that.