Is there a way to import module or add component to main module on condition based
ex:
@Module(
{
modules: [ModuleA, ModuleB, ModuleC]
})
In this in some place i need only ModuleA, not ModuleB and ModuleC, but in some condition, I will be using ModuleB and not others.
Is there a way to import here based on some condition..
Also, I have the same situation in component loading. is that possible in components too?
I would be happy if I have any one of these
Nest version: 4.6.4
For Tooling issues:
- Node version: 8.9.4
- Platform: Windows
I think you are looking for Dynamic Modules
see here https://docs.nestjs.com/modules
at the end of this page.
in Dynamic Module you should have control of what will be included in components array, and so imports.
@shekohex +1
Exactly. The dynamic modules feature is what are you looking for 馃檪 Let us know if you face any issues.
Dynamic modules are great! I'm wondering if there's a way to use DI with Dynamic Modules? I want my imports to be based on a ConfigService value.
@DavidTheProgrammer maybe you could checkout https://github.com/nestjs-community/nestjs-config to see how we implemented this.
@shekohex Thanks a lot for the response! I've posted on Discord and Gitter and haven't gotten any responses. I've had a look at the code and from what I can see, it creates an instance of the service dependent on the options passed. I can't figure out how I can apply it to what I want I want to do, maybe some code can help clarify the issue
@Module({
imports: [EnvModule],
providers: [EnvService],
})
export class ApiModule {
static init(env: EnvService): DynamicModule {
const isProduction = env.isProduction;
const imports = [DatabaseModule, CompanyModule, StoreModule, UserModule, UserCompanyInfoModule];
if (!isProduction) {
imports.push(SeederModule);
}
return {
module: ApiModule,
imports,
};
}
}
I'm trying to inject the service and use one of it's methods to determine the value of the imports array of the module. Any ideas?
Okay, i know someone can help with that :)
cc @bashleigh
I would suggest making a dynamic module and making dynamic providers. You could also use the ClassProvider to keep the token the same with @Inject('CUSTOM_TOKEN').
@Module({})
export class TestClass {
public static forRoot (options: { production: boolean } ): DynamicProvider {
const providers: Provider[] = [];
providers.push(options.production ? ProviderA : ProviderB);
// Second method with useClass Provider
// This would mean your token for the provider will be the same regardless of class used
providers.push({
provide: 'CUSTOM_TOKEN',
useClass: options.production ? ProviderA : ProviderB,
});
return {
module: TestClass,
providers,
};
}
}
I'm trying to inject the service and use one of it's methods to determine the value of the imports array of the module. Any ideas?
From what you're saying here you want to inject the service EnvService. This means having a provider that depends on that provider. For example
@Module({})
export class TestClass {
public static forRoot (options: {
useFactory: (...args: any[]) => { production: boolean },
injects: Array<Type<any> | string | any>
}): DynamicProvider {
const providers: Provider[] = [
{
provide: 'TEST_CLASS_PRODUCTION_BOOLEAN',
useFactory: options.useFactory,
injects: options.injects,
},
{
provide: 'CUSTOM_PROVIDER_TOKEN',
useFactory: (options: {production: boolean}) => production ? ProviderA : ProviderB,
injects: ['TEST_CLASS_PRODUCTION_BOOLEAN'],
},
];
return {
module: TestClass,
providers,
};
}
}
@Module({
imports: [
TestClass.forRoot({
useFactory: (envService: EnvService) => ({ production: envService.isProduction }),
injects: [EnvService],
}),
],
providers: [EnvService]
})
export class SomeOtherModule {}
Hopefully! In the above example you now have this sort of setup and classes will be instanced in this order:
EnvService => 'TEST_CLASS_PRODUCTION_BOOLEAN' => 'CUSTOM_PROVIDER_TOKEN'.
I've not tested it but I think this is the sort of method you was looking for?
You could cut out the other provider if wanted like so, I was trying to avoid a hard coded dependancy but I doubt it matters in your case?
{
provide: 'CUSTOM_PROVIDER_TOKEN',
useFactory: (envService: EnvService) =>envService.isProduction ? ProviderA : ProviderB,
injects: [EnvService],
},
Hi @bashleigh Thanks so much for the response and taking the time to write out these examples, I genuinely appreciate it! I'll try implementing this and I'll be back with feedback. Also, I don't know if it matters but there's a slight correction:
From what you're saying here you want to inject the service EnvService. This means having a provider that depends on that provider. For example
What I actually want is a DynamicModule that determines it's imports: [] based on a value of the EnvService. So it's technically a "module that depends on a provider"... I think.
Hi @bashleigh & @shekohex I wasn't able to tailor your answer to my problem, thank you for the time still. I was however able to track down a similar request here on Github. They're quite a few actually one such issue. The solution there is "just don't do it or do it manually". I ended up creating an instance of my EnvService and luckily it doesn't depend on anything else so it was a breeze.
@Module({
imports: [CompanyModule, StoreModule, UserModule, UserCompanyInfoModule],
})
export class ApiModule {
static async init(): Promise<DynamicModule> {
const env = new EnvService();
const seederModuleArray = [];
if (!env.isProduction) {
seederModuleArray.push(SeederModule);
}
return {
module: ApiModule,
imports: seederModuleArray,
};
}
}
That works fine. I tried using the application context:
const app = await NestFactory.createApplicationContext(AppModule);
const env = app.get(EnvService);
But this obviously resulted in a circular dependency. Using forwardRef resulted in an infinite loop of each module trying to instantiate the other.
The thing I don't understand though is this part in the documentation:
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {
constructor(private readonly catsService: CatsService) {}
}
What configuration can you actually do in Modules?? You can Inject the service yeah, but then what? Can you affect the metadata of the Module in the constructor function??
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Most helpful comment
Okay, i know someone can help with that :)
cc @bashleigh