How to use global module after imported? - javascript

I've followed the example from the docs on how to create a basic config service.
At the bottom of the tutorial it says you can opt to declare it globally:
"Instead of importing ConfigModule repeatingly in all your modules, you can also declare ConfigModule as a global module."
So following the documentation for global modules I have:
Imported Global from #nestjs/common into ConfigModule.
Added the #Global() decorator to ConfigModule.
Imported ConfigModule into AppModule.
Added ConfigModule to the imports array.
So what's next? I have tried to inject ConfigService into AppService however it doesn't resolve.
app.module.ts:
import { Module } from '#nestjs/common';
import { AppService } from './app.service';
import { AppController } from './app.controller';
import { ConfigModule } from '../config/config.module';
#Module({
imports: [
ConfigModule,
],
controllers: [
AppController,
],
providers: [
AppService,
],
})
export class AppModule {}
app.service.ts:
import { Injectable } from '#nestjs/common';
#Injectable()
export class AppService {
private readonly config: ConfigService;
constructor(config: ConfigService) {
this.config = config;
}
getHello(): string {
return config.get('DB_NAME');
}
}
config.module.ts:
import { Module, Global } from '#nestjs/common';
import { ConfigService } from './config.service';
#Global()
#Module({
providers: [
{
provide: ConfigService,
useValue: new ConfigService(`${process.env.NODE_ENV}.env`),
},
],
exports: [
ConfigService,
],
})
export class ConfigModule {}
config.service.ts:
import * as dotenv from 'dotenv';
import * as fs from 'fs';
export class ConfigService {
private readonly envConfig: { [key: string]: string };
constructor(filePath: string) {
this.envConfig = dotenv.parse(fs.readFileSync(filePath));
}
get(key: string): string {
return this.envConfig[key];
}
}
I expect to be able to inject ConfigService and access it from any module.

You're missing the this qualifier in your AppService:
getHello(): string {
return this.config.get('DB_NAME');
^^^^^
}
Also, the import is missing:
import { ConfigService } from './config/config.service';

Related

Nest can't resolve dependencies of the service?

I'm trying access a database using a custom provider as per this guide. At startup, Nestjs throws the error Nest can't resolve dependencies of the EventsService (?). Please make sure that the argument DATA_SOURCE at index [0] is available in the AppModule context.
Here are my files
Database providers
import { DataSource } from 'typeorm';
export const databaseProviders = [
{
provide: 'DATA_SOURCE',
useFactory: async () => {
const dataSource = new DataSource({
type: "mysql",
host: "host",
port: 3306,
username: "username",
password: "password",
synchronize: true,
logging: true,
});
return dataSource.initialize();
},
},
];
Database module
import { databaseProviders } from "./database.providers";
import { Module } from "#nestjs/common";
#Module({
providers: [...databaseProviders],
exports: [...databaseProviders],
})
export class DatabaseModule {}
Events service
import { Inject, Injectable } from '#nestjs/common';
import { DataSource } from 'typeorm';
import { DatabaseModule } from './database.module';
import { Event } from './entities/event.entity';
import { EventInvite } from './entities/eventInvite.entity';
#Injectable()
export class EventsService {
constructor(#Inject("DATA_SOURCE") private readonly database: DataSource) { }
createEvent(userId: string, event: Event) {
this.database.manager.create(Event, event)
}
deleteEvent(eventId: string){
this.database.manager.delete(Event, { eventId })
}
}
Events Module
import { Module } from '#nestjs/common';
import { DatabaseModule } from './database.module';
import { EventsController } from './events.controller';
import { EventsService } from './events.service';
#Module({
imports: [DatabaseModule],
controllers: [EventsController],
providers: [EventsService],
exports: [EventsService]
})
export class EventsModule {}
App module
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { EventsController } from './events/events.controller';
import { EventsService } from './events/events.service';
import { EventsModule } from './events/events.module';
import { DatabaseModule } from './events/database.module';
#Module({
imports: [],
controllers: [AppController, EventsController],
providers: [AppService, EventsService],
})
export class AppModule {}
If I import DatabaseModule inside of AppModule everything works. My question is, why is this required? My understanding thus far is that Nestjs builds a dependency tree, which in this case should look something like AppModule => EventService => DatabaseService. AppModule doesn't directly access DatabaseService, and therefore shouldn't need to import it directly, so why is Nestjs failing to resolve this dependency?
that module isn't global, thus its providers aren't globally available. As you're registering the service EventsService again in AppModule, you need to import the DatabaseModule
I believe this is what you're trying to do (which is pretty much what the docs shows):
#Module({
imports: [EventsModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
than you won't have 2 instances of EventsService and EventsController anymore, only the one registered in EventsModule module.

Nest js: Nest can't resolve dependencies of the Season3Service

I have the following error in nest. I've read the doc but I still don't understand what's going on. This is the full error: "[Nest] 9420 - 21/02/2022, 12:39:24 p. m. ERROR [ExceptionHandler] Nest can't resolve dependencies of the Season3Service (?). Please make sure that
the argument gModel at index [0] is available in the AppModule context". Here is my code:
app.controller.ts:
import { Controller, Get } from '#nestjs/common';
import { ClientRequest } from 'http';
import { AppService } from './app.service';
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#Get()
getHello(): string {
return 'Sarani Mukoe! En el Arco de la Villa del Herrero';
}
}
app.module.ts:
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { Season3Controller } from './season3/season3.controller';
import { Season3Service } from './season3/season3.service';
import { Season3Module } from './season3/season3.module';
import { MongooseModule } from '#nestjs/mongoose';
#Module({
imports: [Season3Module, MongooseModule.forRoot('mongodb://127.0.0.1:27017/DemonSlayer')],
controllers: [AppController, Season3Controller],
providers: [AppService, Season3Service],
})
export class AppModule {}
app.service:
import { Injectable } from '#nestjs/common';
#Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
Then, I have the season3 module folder:
season3.controller.ts:
import { Controller, Get, Post, Put, Delete, Body, Param } from '#nestjs/common';
import { CreateSeasonDto } from './dto/create-season.dto';
import { Season } from './interfaces/Season';
import { Season3Service } from "./season3.service";
#Controller('season3')
export class Season3Controller {
constructor(private season3: Season3Service) {}
#Get()
getSeasons(): Promise<Season[]> {
return this.season3.getSeasons();
}
#Get(':p')
getSeason(#Param('p') id: string): Promise<Season>{
console.log(id);
return this.season3.getSeason(id);
}
#Post()
createSeason(#Body() season: CreateSeasonDto): string {
console.log(
`Título: ${season.titulo}. Cuerpo: ${season.cuerpo}. Realizado: ${season.realizado}`
);
return 'Insertando.... Hinokami Kagura';
}
#Put(':p1')
updateSeason(#Body() cuerpo: CreateSeasonDto, #Param('p1') id): string {
console.log(cuerpo);
console.log(id);
return `Actualizando.... Velocidad Extrema`;
}
#Delete(':id')
deleteSeason(#Param('id') id): string {
console.log(id);
return `Eliminando(${id}) .... Colmillos Afilados`;
}
}
season3.module.ts:
import { Module } from '#nestjs/common';
import { MongooseModule } from '#nestjs/mongoose';
import { Season3Controller } from './season3.controller';
import { Season3Service } from './season3.service';
import { SeasonSchema} from "./schemas/season.schema";
#Module({
imports: [MongooseModule.forFeature([
{name:'g', schema:SeasonSchema}
])],
controllers: [Season3Controller],
providers: [Season3Service],
})
export class Season3Module {}
season3.service.ts:
import { Model } from "mongoose";
import { Injectable } from '#nestjs/common';
import { InjectModel } from '#nestjs/mongoose';
import { Season } from "./interfaces/Season";
#Injectable()
export class Season3Service {
constructor(#InjectModel('g') private seasonModel: Model<Season>) {}
async getSeasons() {
return await this.seasonModel.find();
}
async getSeason(id: string) {
return await this.seasonModel.findById(id);
}
}
It looks like Season3Service is not exported so you won't be able to use it in other modules.
in season3.module.ts add export
import { Module } from '#nestjs/common';
import { MongooseModule } from '#nestjs/mongoose';
import { Season3Controller } from './season3.controller';
import { Season3Service } from './season3.service';
import { SeasonSchema} from "./schemas/season.schema";
#Module({
imports: [MongooseModule.forFeature([
{name:'g', schema:SeasonSchema}
])],
controllers: [Season3Controller],
providers: [Season3Service],
exports: [Season3Service], // <----- make Season3Service public
})
export class Season3Module {}
doc: https://docs.nestjs.com/modules#shared-modules
After this, any other module that imports Season3Module will be able to inject (user) Season3Service
in app.module.ts
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { Season3Controller } from './season3/season3.controller';
import { Season3Module } from './season3/season3.module';
import { MongooseModule } from '#nestjs/mongoose';
#Module({
imports: [Season3Module, MongooseModule.forRoot('mongodb://127.0.0.1:27017/DemonSlayer')],
controllers: [AppController, Season3Controller],
providers: [AppService], // <---- remove Season3Service from providers
})
export class AppModule {}
Now any providers/controllers in AppModule will have access to Season3Service

ngx-translate with Angular universal has problems translating parameterised value

I'm working on migrating my Angular 10 application to SSR using Angular universal and I'm facing issues with ngx-translate while translating parameterised values.
I have a translation
Bei Kauf von {quantity}
where this quantity is an attribute from our component and we normally translate it like
{{ 'LABEL' | translate: {quantity: 1} }} and we will see Bei Kauf von 1
But with SSR it's not at all translating. I see Bei Kauf von {quantity} on the page. I checked many forums and I don't see potential solutions. Any help would be much appreciated.
Here is my translate-server.loader.ts
import { join } from 'path';
import { Observable } from 'rxjs';
import { TranslateLoader } from '#ngx-translate/core';
import {
makeStateKey,
StateKey,
TransferState
} from '#angular/platform-browser';
import * as fs from 'fs';
export class TranslateServerLoader implements TranslateLoader {
constructor(
private transferState: TransferState,
private prefix: string = 'i18n',
private suffix: string = '.json'
) {}
public getTranslation(lang: string): Observable<any> {
return new Observable((observer) => {
const assets_folder = join(
process.cwd(),
'dist',
'my-app',
'browser',
'assets',
this.prefix
);
const jsonData = JSON.parse(
fs.readFileSync(`${assets_folder}/${lang}${this.suffix}`, 'utf8')
);
// Here we save the translations in the transfer-state
const key: StateKey<number> = makeStateKey<number>(
`transfer-translate-${lang}`
);
this.transferState.set(key, jsonData);
observer.next(jsonData);
observer.complete();
});
}
}
export function translateServerLoaderFactory(transferState: TransferState) {
return new TranslateServerLoader(transferState);
}
translate-browser.loader.ts
import { Observable } from 'rxjs';
import { TranslateLoader } from '#ngx-translate/core';
import {
makeStateKey,
StateKey,
TransferState
} from '#angular/platform-browser';
import { TranslateHttpLoader } from '#ngx-translate/http-loader';
import { HttpClient } from '#angular/common/http';
export class TranslateBrowserLoader implements TranslateLoader {
constructor(private http: HttpClient, private transferState: TransferState) {}
public getTranslation(lang: string): Observable<any> {
const key: StateKey<number> = makeStateKey<number>(
`transfer-translate-${lang}`
);
const data = this.transferState.get(key, null);
// First we are looking for the translations in transfer-state,
// if none found, http load as fallback
if (data) {
return new Observable((observer) => {
observer.next(data);
observer.complete();
});
} else {
return new TranslateHttpLoader(this.http).getTranslation(lang);
}
}
}
export function translateBrowserLoaderFactory(
httpClient: HttpClient,
transferState: TransferState
) {
return new TranslateBrowserLoader(httpClient, transferState);
}
app.server.module.ts
import { NgModule } from '#angular/core';
import { ServerModule, ServerTransferStateModule } from '#angular/platform-server';
import { FlexLayoutServerModule } from '#angular/flex-layout/server';
import { TranslateModule, TranslateLoader } from '#ngx-translate/core';
import { translateServerLoaderFactory } from './shared/loaders/translate-server.loader';
import { TransferState } from '#angular/platform-browser';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { EnvironmentService } from '~shared/services/environment.service';
#NgModule({
imports: [
AppModule,
ServerModule,
ServerTransferStateModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: translateServerLoaderFactory,
deps: [TransferState]
}
}),
FlexLayoutServerModule,
],
providers: [EnvironmentService],
bootstrap: [AppComponent],
})
export class AppServerModule {}
app.module.ts
imports: [
....,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: translateBrowserLoaderFactory,
deps: [HttpClient, TransferState]
}
}),
]
Note: It's working fine for all others which don't have any parameterised values in translations
It should be double curly brackets
Bei Kauf von {{quantity}}
wanna notice 2 more things, idk if it changed in last versions of Angular, but don't forget to register TransferState in app.module providers
providers: [
TransferState,
...
]
and for translate-server.loader.ts if you have some type errors for node.js use
/// <reference types="node" /> // add this line
import {join} from 'path';
at the top.
If it doesn't help, try to add
"types" : ["node", "express"],
to your tsconfig.json inside "compilerOptions"
It took me some time))

Nestjs cant resolve dependencies

Can't figure out what's the problem of my code. (I'm new with nestjs, I'm trying to learn it by passing some apps to it). Console log says:
Nest can't resolve dependencies of the UrlsAfipService (?). Please
make sure that the argument at index [0] is available in the ApiModule
context.
UrlsAfipService
import { Injectable } from '#nestjs/common';
import { AfipUrls } from './urls'
#Injectable()
export class UrlsAfipService {
constructor(
private readonly afipUrls: AfipUrls,
) {}
getWSAA () {
return this.afipUrls.homo().wsaa; // <- change to prod() for production
}
getService (service: string) {
return this.afipUrls.homo().service.replace('{service}', service)
}
}
AfipUrls
export class AfipUrls {
homo() {
return {
wsaa: 'https://url.com',
service: 'https://url.com'
}
}
prod() {
return {
wsaa: 'url.com',
service: 'url.com'
}
}
}
ApiModule
import { Module } from '#nestjs/common';
import { ApiController } from './api.controller';
import { UrlsAfipService } from './urls-afip.service'
import { WsaaService } from './wsaa.service'
import { DescribeService } from './describe.service';
#Module({
controllers: [ApiController],
providers: [UrlsAfipService, WsaaService, DescribeService]
})
export class ApiModule {}
AppModule
import { Module } from '#nestjs/common';
import { ApiModule } from './api/api.module';
import { AppController } from './app.controller';
import { AppService } from './app.service';
#Module({
imports: [ApiModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
You have declared AfipUrls as a dependency for UrlsAfipService but it is not provided in any module.
So you have to add AfipUrls to the providers array of your ApiModule. Then it can be injected.
providers: [UrlsAfipService, WsaaService, DescribeService, AfipUrls]
// ^^^^^^^^
Note though, that encoding environment specific values in your code base might be a code smell. Consider creating a ConfigService that encapsulates environment specific variables that are read from environment variables or .env files using dotenv. See this answer for more information.

How to inject $templateCache into angular hybrid APP_INITIALIZER hook?

I'm new to Angular5 and TypeScript, so it's very possible it's a simple thing I'm overlooking.
I have an Angular hybrid app that uses ngUpgrade to run AngularJS and Angular5 side-by-side. I'm trying to inject $templateCache into the OnAppInit function so that I can load all the AngularJS HTML templates before the app completely initializes. I'm getting the error "Cannot find name '$templateCacheService'" as indicated below. Is my syntax wrong or is this not possible?
I "upgrade" $templateCache in upgraded-providers.ts like this:
import { InjectionToken, Directive, ElementRef, Injector } from '#angular/core';
import { UpgradeComponent } from '#angular/upgrade/static';
export const $templateCacheService = new InjectionToken<any>('$templateCacheService');
export const $templateCacheServiceProvider = {
provide: $templateCacheService,
useFactory: (i: any) => i.get('$templateCache'),
deps: ['$injector']
};
Then in app.module.ts, I try to inject it into OnAppInit:
import { NgModule, APP_INITIALIZER, Inject } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
import { MatCommonModule } from '#angular/material';
import { FlexLayoutModule } from '#angular/flex-layout';
import { HttpClientModule, HttpClient, HTTP_INTERCEPTORS } from '#angular/common/http';
import { downgradeInjectable, UpgradeModule, downgradeComponent } from '#angular/upgrade/static';
import { environment } from '../environments/environment';
import {
$templateCacheServiceProvider,
$templateCacheService
} from './upgraded-providers';
import { AppComponent } from './app.component';
import { GlobalVarsService } from './core/global-vars.service';
import { WinAuthInterceptor } from './core/interceptors/win-auth.interceptor';
declare var angular: any;
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
MatCommonModule,
FlexLayoutModule,
HttpClientModule,
UpgradeModule
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: WinAuthInterceptor,
multi: true
},
{
provide: APP_INITIALIZER,
useFactory: OnAppInit,
multi: true,
deps: [GlobalVarsService, HttpClient, $templateCacheService]
},
GlobalVarsService,
$templateCacheServiceProvider
]
})
export class AppModule {
constructor(private upgrade: UpgradeModule, private http: HttpClient) { }
ngDoBootstrap() {
angular.module('app').factory('globalVars', downgradeInjectable(GlobalVarsService));
this.upgrade.bootstrap(document.body, ['app'], { strictDi: true });
}
}
////// THIS NEXT LINE GETS error TS2304: Cannot find name '$templateCacheService' /////
export function OnAppInit(globalVars: GlobalVarsService, http: HttpClient, $templateCache: $templateCacheService) {
return (): Promise<any> => {
return new Promise((resolve, reject) => {
http.get(environment.apiBase + '/api/meta/data').subscribe(x => {
globalVars.MetaData = x;
globalVars.VersionNumber = globalVars.MetaData.versionNumber;
globalVars.IsDebugBuild = globalVars.MetaData.isDebugBuild;
globalVars.User = globalVars.MetaData.user;
globalVars.ApiBase = environment.apiBase;
globalVars.Templates.forEach(template => {
$templateCache.put(template.Item1, template.Item2);
});
resolve();
});
});
};
}
This is TypeScript type error, it doesn't affect how the application works (as long as compilation errors are ignored).
templateCacheService is not a valid type here, because $templateCacheService is a variable (injection token), not a type or an interface.
Only Angular class constructors are annotated with types for DI. Since factory functions are annotated with deps property, types in function signature exist only to provide type safety. If it's not needed, types can be skipped:
export function OnAppInit(
globalVars: GlobalVarsService, http: HttpClient,
$templateCache
) { ... }
Otherwise proper types should be used. $templateCache is an object with get, put, etc methods. Appropriate types are provided with AngularJS #types/angular type definitions. It will be something like:
export function OnAppInit(
globalVars: GlobalVarsService, http: HttpClient,
$templateCache: ng.ITemplateCacheService
) { ... }

Categories

Resources