I try without success since a half day to use ngx-translate with the angular universal starter app https://github.com/angular/universal-starter. Have someone an idea why following isn't working?
When I start npm start my server and reload my pages I see briefly that the translation is found before being replaced with the key which traditionally show that something doesn't work with ngx-translate. Also when I change the path of the translation in the server-app.module.ts I see an error in the console of the server, therefore I think that my server part is alright and that the problem comes from the client side.
app.module.ts:
export function exportTranslateStaticLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
imports: [
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: exportTranslateStaticLoader,
deps: [Http]
}
}
)
]
browser-app.module.ts:
imports: [TranslateModule.forChild()]
server-app.module.ts:
export function translateFactory() {
return new TranslateUniversalLoader('./dist/assets/i18n', '.json');
}
imports: [
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: translateFactory
}
})
]
TranslateUniversalLoader:
import {Observable} from "rxjs/Observable";
import {TranslateLoader} from '#ngx-translate/core';
const fs = require('fs');
export class TranslateUniversalLoader implements TranslateLoader {
constructor(private prefix: string = 'i18n', private suffix: string = '.json') {}
public getTranslation(lang: string): Observable<any> {
return Observable.create(observer => {
observer.next(JSON.parse(fs.readFileSync(`${this.prefix}/${lang}${this.suffix}`, 'utf8')));
observer.complete();
});
}
}
webpack.common.js:
plugins: [
new copyWebpackPlugin([
{
from: './src/assets/i18n/en.json',
to: './assets/i18n/en.json'
}
])
]
app.component.ts:
ngOnInit() {
this.translateService.setDefaultLang('en');
this.translateService.use('en');
}
Futhermore, when I query http://localhost:8000/assets/i18n/en.json I get a valid answer back respectively my en.json:
{
"TEST": "Super super"
}
Any help appreciated, this drives me nuts.
Got it, in app.module.ts I should inject HttpClient instead of Http ( the README about AoT still displayed Http):
imports: [
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: exportTranslateStaticLoader,
deps: [HttpClient]
}
}
)
]
Related
I tried to create a dynamic module PermissionModule like the following:
permTestApp.module.ts
#Module({
imports: [PermissionModule.forRoot({ text: 'abc' })],
providers: [],
controllers: [PermissionTestController],
})
export class PermissionTestAppModule {}
permission.module.ts
import { DynamicModule, Module } from '#nestjs/common'
import { PermissionGuard } from './guard/permission.guard'
#Module({})
export class PermissionModule {
public static forRoot(config: { text: string }): DynamicModule {
return {
module: PermissionModule,
providers: [
{
provide: 'xoxo',
useValue: config.text,
},
PermissionGuard,
],
exports: [PermissionGuard],
}
}
}
permission.guard.ts
import {
CanActivate,
ExecutionContext,
Inject,
Injectable,
} from '#nestjs/common'
#Injectable()
export class PermissionGuard implements CanActivate {
constructor(#Inject('xoxo') private readonly config: string) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
console.log(31, this.config)
return true
}
}
AFAIK, 'abc' string must be injected when PermissionGuard is used.
I tried to test it with the following code.
permission.e2e.spec.ts
beforeAll(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
imports: [PermissionTestAppModule],
})
.compile()
app = moduleRef.createNestApplication()
controller = await moduleRef.resolve(PermissionTestController)
await app.init()
})
but it says,
Nest can't resolve dependencies of the PermissionGuard (?). Please make sure that the argument xoxo at index [0] is available in the PermissionTestAppModule context.
Potential solutions:
- Is PermissionTestAppModule a valid NestJS module?
- If xoxo is a provider, is it part of the current PermissionTestAppModule?
- If xoxo is exported from a separate #Module, is that module imported within PermissionTestAppModule?
#Module({
imports: [ /* the Module containing xoxo */ ]
})
What am I doing wrong?
I am trying to inject dependencies in mongoose module for root async. I want to kind of simulate a cascade delete using mongoose hooks.
I have this module, which imports mongoose module, and it imports other modules.
#Module({
imports: [
MongooseModule.forFeatureAsync([
{
imports: [NotificationModule, UserModule, MuseumModule, ImageModule, CommentModule],
name: Shirt.name,
useFactory: (
NotificationService: NotificationService,
UserService: UserService,
MuseumService: MuseumService,
ImageService: ImageService,
ConfigService: ConfigService,
CommentService: CommentService,
) => {
const schema = ShirtSchema;
schema.post('findOneAndDelete', async function (document: ShirtDocument) {
/* Delete notifications */
if (document) {
await NotificationService.deleteManyFromArray(document._id);
/* Remove shirt from museum */
const shirtUser = await UserService.getById(document.shirtUser.userId);
await MuseumService.removeShirtByMuseumId(shirtUser.museums[0], document._id);
/* Delete images from bucket */
if (document.images && document.images.length > 0) {
document.images.forEach(async (image) => {
if (image.thumbnail) {
await ImageService.deleteImageFromBucketS3({
bucket: ConfigService.get('AWS_THUMBNAIL_BUCKET'),
key: getImageUUID(image.thumbnail),
});
}
await ImageService.deleteImageFromBucketS3({
bucket: ConfigService.get('AWS_BUCKET'),
key: getImageUUID(image.cloudImage),
});
});
}
/* Remove shirt comments */
if (document.comments && document.comments.length > 0) {
await CommentService.deleteManyComments(document.comments.map((c) => c._id));
}
}
});
return schema;
},
inject: [NotificationService, UserService, MuseumService, ImageService, ConfigService, CommentService],
},
]),
UserModule,
TeamModule,
BrandModule,
CountryModule,
MuseumModule,
ImageModule,
],
controllers: [ShirtController],
providers: [ShirtService, ShirtRepository],
exports: [ShirtService],
})
export class ShirtModule {}
I also need to do the same in another module, but when I import the
ShirtModule
the compilation fails with the following error:
Error: Nest cannot create the module instance. Often, this is because
of a circular dependency between modules. Use forwardRef() to avoid
it. Scope [AppModule -> UserModule -> MongooseModule -> MuseumModule
-> MongooseModule -> ShirtModule -> MongooseModule]
#Module({
imports: [
MongooseModule.forFeatureAsync([
{
name: 'Museum',
imports: [ShirtModule],
useFactory: () => {
const schema = MuseumSchema;
schema.post('findOneAndDelete', async function (document: MuseumDocument) {});
return schema;
},
},
]),
],
controllers: [MuseumController],
providers: [MuseumService, MuseumRepository],
exports: [MuseumService],
})
export class MuseumModule {}
I tried using
forwardRef(() => )
In both modules, but still the same. I can not understand where is the circular dependency and how to solve it.
Please could you help me?. Also, is this is a good approach to use mongoose hooks using nest?
Thanks
Try to use forwardRef(() => MongooseModule.forFeatureAsync(xxxx)). This is work in my case.
I am using Angular and Electron, and I have a Preferences object I would like to initialize (Preferences.init()) which needs to be executed before any other code is executed. Does Angular or Electron have a specific location where such initialization code should be executed?
At the moment I put it into the constructor of AppComponent but since the function is asynchronous, I have a race condition and occasionally the data is not properly initialized when needed. Any help or suggestion is highly appreciated! Thanks!
You can use the APP_INITIALIZER dependency injection token to do a such thing:
Assuming you created a provider
#Injectable()
export class ThingProvider {
private thing: Thing = null;
constructor(private http: Http) {
}
public getThing(): Thing {
return this.thing;
}
load() {
return new Promise((resolve, reject) => {
this.http
.get('https://my-api.com/thing')
.map(res => res.json())
.subscribe(response => {
this.thing = response['value'];
resolve(true);
})
})
}
}
Then, in your app.module.ts file:
export function thingsProviderFactory(provider: ThingProvider) {
return () => provider.load();
}
And finally, in the same file:
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpModule
],
providers: [
ThingProvider,
{ provide: APP_INITIALIZER, useFactory: thingsProviderFactory, deps: [ThingProvider], multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }
Seems like a real pain in the brain...
There is a huge thread about this on github and other sites, many of them come down to using useContainer from the 'class-validator' but it does not work for me.
async function bootstrap() {
const app = await NestFactory.create(ApplicationModule);
useContainer(app, { fallback: true });
await app.listen(3000);
}
bootstrap();
Here's the injectable:
#ValidatorConstraint({ name: 'uniqueOnDatabase', async: true })
#Injectable()
export class UniqueOnDatabase implements ValidatorConstraintInterface {
constructor(
private readonly userService: UserService,
) {}
public async validate(val: any, args: ValidationArguments): Promise<boolean> {
const user = await this.userService.retrieveOneByEmail(val);
return !user;
}
public defaultMessage(args: ValidationArguments): string {
return `User with such an email address already exists in the DB`;
}
}
All I want to do is use my userService inside that UniqueOnDatabase class.
Here is the module where I am providing the UniqueOnDatabase:
import { Module, CacheModule } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { CacheConfigService } from 'src/config/cache/config.service';
import { CacheService } from './services/cache.service';
import { CodeGenService } from './services/code-gen.service';
import { UserExistanceValidationPipe } from './pipes/user-existance.validation.pipe';
import { UsersModule } from 'src/users/users.module';
import { UniqueOnDatabase } from './validators/unique-on-database.validator';
#Module({
providers: [
CacheService,
CodeGenService,
UniqueOnDatabase,
],
imports: [
CacheModule.registerAsync({
imports: [ConfigModule],
useClass: CacheConfigService,
}),
UsersModule,
],
exports: [
CacheService,
CodeGenService,
UniqueOnDatabase,
],
})
export class SharedModule {}
Thanks #Albert for answering your question.
Adding #Albert's answer just in case someone misses the comments:
#JayMcDoniel Aaah, seems like I've figured out the solution. I should
have used useContainer(app.select(SharedModule), { fallbackOnErrors:
true }); instead of what I did at first...
Thanks again #Albert
In my application, I want to use a single instance of service class throughout my project. The service class will be initialized by a dynamic module.
Things in detail
I have module called LoggerModule which has a static function register. We use this method to initialize in the app module Eg:LoggerModule.register(options). In the register method, we will be returning a dynamic module which will set this options as a custom provider.
Eg:
return {
module: LoggerModule,
providers: [
{
provide: CONFIG_OPTIONS,
useValue: options,
},
LoggerService,
],
exports: [LoggerService],
};
Here we have a LoggerService that injects the CONFIG_OPTIONS, so that we can fetch options using the service class. Now I want to be able to access the service from anywhere in the project by injecting it in my class, but since the module is not global, currently I will have to include LoggerModule.register() in all the modules that I am using. I tried using the #Global() annotation, but it doesn't seem to work.
Can you suggest any methods on how to do this? If yes please share with me an example?
All you should need to do to make a dynamic module global is add the #Global() decorator above the #Module() decorator. Same with any other module you are working with. Here are the docs on it
Edit 11/22/19
Okay, I tried to mimic your setup as close as I could with what was given and what I still had lying around my local machine. I was able to get a global module working with the following setup:
Config Module (what will be the global module)
import { DynamicModule, Module, Provider, Global } from '#nestjs/common';
import { CONFIG_MODULE_OPTIONS } from './config.constants';
import { createConfigProvider } from './config.provider';
import { ConfigService } from './config.service';
import {
ConfigModuleAsyncOptions,
ConfigModuleOptions,
ConfigOptionsFactory,
} from './interfaces/config-options.interface';
#Global()
#Module({})
export class ConfigModule {
static forRoot(options: ConfigModuleOptions): DynamicModule {
return {
module: ConfigModule,
providers: [ConfigService, ...createConfigProvider(options)],
exports: [ConfigService],
};
}
static forRootAsync(options: ConfigModuleAsyncOptions): DynamicModule {
return {
module: ConfigModule,
imports: options.imports || [],
providers: [ConfigService, ...this.createAsyncProviders(options)],
exports: [ConfigService],
};
}
private static createAsyncProviders(
options: ConfigModuleAsyncOptions,
): Provider[] {
if (options.useExisting || options.useFactory) {
return [this.createAsyncOptionsProviders(options)];
}
if (options.useClass) {
return [
this.createAsyncOptionsProviders(options),
{
provide: options.useClass,
useClass: options.useClass,
},
];
}
throw new Error('Invalid ConfigModule configuration.');
}
private static createAsyncOptionsProviders(
options: ConfigModuleAsyncOptions,
): Provider {
if (options.useFactory) {
return {
provide: CONFIG_MODULE_OPTIONS,
useFactory: options.useFactory,
inject: options.inject || [],
};
}
return {
provide: CONFIG_MODULE_OPTIONS,
useFactory: async (optionsFactory: ConfigOptionsFactory) =>
await optionsFactory.createConfigOptions(),
inject: [options.useExisting || options.useClass || ''],
};
}
}
I had this set up for a completely reusable Nest Module but scrapped the idea as there are already a few config modules out there, hence all the boilerplate.
Dyanmic Module (yes I know it's spelled wrong)
import { Module } from '#nestjs/common';
import { DyanmicTestService } from './dyanmic-test.service';
import { DyanmicTestController } from './dyanmic-test.controller';
#Module({
providers: [DyanmicTestService],
controllers: [DyanmicTestController],
})
export class DyanmicTestModule {}
The Dyanmic Service injects the Config Service, but notice we don't import the Config Module here. That's because it is global, and once registered in the App Module, it doesn't need to be imported anywhere else.
App Module
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { DyanmicTestModule } from './dyanmic-test/dyanmic-test.module';
import { ConfigModule } from './config/config.module';
#Module({
imports: [
ConfigModule.forRootAsync({
useFactory: () => ({
fileName: '.env',
useProcess: false,
}),
}),
DyanmicTestModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
All of this code can also be found on my GitHub.