JWT library error: Generic type 'ModuleWithProviders<T>' requires 1 type argument(s) in Angular 10 - node.js

For an authentication project I am using:
Angular CLI: 11.0.4 for the frontend
Node: 10.19.0 for the backend
OS: linux x64
I receive the following error after ng serve and I am not sure why that is happening, the error seems to be in the library node_modules/angular2-jwt/angular2-jwt.d.ts and not in the code I wrote:
node_modules/angular2-jwt/angular2-jwt.d.ts:88:41 - error TS2314:
Generic type 'ModuleWithProviders' requires 1 type argument(s).
static forRoot(config: AuthConfig): ModuleWithProviders;
Also connected to that (so I believe the errors are concurrent or even, I am afraid, interchangeable), because it was shown as soon as the 'ModuleWithProviders<T>' error was shown, so I though it would make sense to show them both as they are linked together:
Error: node_modules/angular2-jwt/angular2-jwt.d.ts:1:77 - error
TS2307: Cannot find module '#angular/http' or its corresponding type
declarations.
1 import { Http, Request, RequestOptions, RequestOptionsArgs, Response
} from "#angular/http";
So the difficulty I have is also due to the fact that I am not sure which parts of the code are affected so I will put for the sake of completeness the app.module.ts and the files carrying the jwt include
app.module.ts:
import { ValidateService } from './services/validate.service';
import { FlashMessagesModule } from 'angular2-flash-messages';
import { HttpClientModule } from '#angular/common/http';
import { AuthService } from './services/auth.service';
import { AuthGuard } from './guards/auth.guards';
const appRoutes: Routes = [
{path:'', component: HomeComponent},
{path:'register', component: RegisterComponent},
{path:'login', component: LoginComponent},
{path:'dashboard', component: DashboardComponent, canActivate: [AuthGuard]},
{path:'profile', component: ProfileComponent, canActivate: [AuthGuard]},
]
#NgModule({
declarations: [
AppComponent,
NavbarComponent,
LoginComponent,
RegisterComponent,
HomeComponent,
DashboardComponent,
ProfileComponent
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
RouterModule.forRoot(appRoutes),
FlashMessagesModule.forRoot(),
HttpClientModule,
],
providers: [ValidateService, AuthService, AuthGuard],
bootstrap: [AppComponent]
})
export class AppModule { }
auth.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { map } from 'rxjs/operators';
import { tokenNotExpired } from 'angular2-jwt';
#Injectable({
providedIn: 'root'
})
export class AuthService {
authToken: any;
user: any;
constructor(private httpClient: HttpClient) { }
registerUser(user) {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
})
};
return this.httpClient.post('http://localhost:3000/users/register', user, httpOptions);
}
authenticateUser(user) {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
})
};
return this.httpClient.post('http://localhost:3000/users/authenticate', user, httpOptions);
}
getProfile() {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
Authorization: this.authToken,
})
};
this.loadToken();
return this.httpClient.get('http://localhost:3000/users/profile', httpOptions);
}
storeUserData(token, user) {
localStorage.setItem('id_token', token);
localStorage.setItem('user', JSON.stringify(user));
this.authToken = token;
this.user = user;
}
loadToken() {
const token = localStorage.getItem('id_token');
this.authToken = token;
}
loggedIn() {
return tokenNotExpired();
}
logout() {
this.authToken = null;
this.user = null;
localStorage.clear();
}
}
profile.components.ts
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../../services/auth.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
user: Object = {};
constructor(private authService: AuthService, private router: Router) { }
ngOnInit(): void {
this.authService.getProfile().subscribe(profile => {
this.user = profile;
},
err => {
console.log(err);
return false;
})
}
}
I did research on how to solve the problem and here is was I was able to find so far:
this post is very useful because it has my same exact problem.
The answer calls for a bug report repo that, however, does not provide any answer to that.
The answer that was provided suggests to insert the following code:
declare module "#angular/core" {
interface ModuleWithProviders<T = any> {
ngModule: Type<T>;
providers?: Provider[];
}
}
Unfortunately this was not an accepted answer and I am not sure where I can put this piece of code in any part of the app.module.ts I provided above.
I also studied this post which was also useful but did not use the suggestion above.
The strange fact I understand from the error is that it seems to come from the library itself and not from the code that I wrote.
Following this I proceeded with:
rm -rf all the node_modules
rm -rf the package jason file
clean the cache
npm install
But outcome is the same, I always receive the same error on the same library.
Please if anyone had the same problem can you share how it was solved and what should I do more to take care of that.

Insert this piece of code into the angular2-jwt.d.ts class and confirm the class change:
declare module "#angular/core" {
interface ModuleWithProviders<T = any> {
ngModule: Type<T>;
providers?: Provider[];
}
}
But you should use a newer library than this, like #auth0/angular-jwt
After installing this library, you must register its module in the class app.module.ts :
import {JwtModule} from '#auth0/angular-jwt'
imports: [
JwtModule.forRoot({
config: {
tokenGetter:() => {
return localStorage.getItem('access_token');
},
},
})
],
And then you can use it in your AuthService class:
import {JwtHelperService} from '#auth0/angular-jwt';
constructor(public jwtHelper: JwtHelperService) {
}
isAuthenticated(): boolean {
return !this.jwtHelper.isTokenExpired(this.token);
}
All this is explained in a short documentation with examples (https://www.npmjs.com/package/#auth0/angular-jwt), so don't be lazy to read it before using any library.

Related

NestJS serving JSON and adds a "default" section repeating the JSON

I have a strange behaviour on an endpoint in NestJS serving a piece of JSON.
The JS with the JSON object is exporting
module.exports = Object.freeze({
translation: {
TestMessage: 'Bienvenue à React et react-i18next'
}
});
The result on the Client is:
{
"translation": {
"TestMessage": "Bienvenue à React et react-i18next"
},
"default": {
"translation": {
"TestMessage": "Bienvenue à React et react-i18next"
}
}
}
The question is where is the "default" coming from?
To paint the whole picture, below the module, controller and service:
Module
import { Module } from '#nestjs/common';
import { LoggerService } from '#modules/logger';
import { I18nController } from './i18n.controller';
import { I18nService } from './i18n.service';
#Module({
controllers: [I18nController],
providers: [I18nService, LoggerService],
exports: [I18nService]
})
export class I18nModule {}
Controller
import { Controller, Get, Param } from '#nestjs/common';
import { LoggerService } from '#modules/logger';
import { I18nService } from './i18n.service';
#Controller('i18n')
export class I18nController {
constructor(private logger: LoggerService, private i18nService: I18nService) {
this.logger.setContext(I18nController.name);
}
#Get('/:lang')
async getLanguage(#Param('lang') lang: string) {
console.log(lang);
return await this.i18nService.findOneByLanguageCode(lang);
}
}
Service
import { Injectable } from '#nestjs/common';
import { access } from 'fs/promises';
import { constants as fsconstants } from 'fs';
#Injectable()
export class I18nService {
async findOneByLanguageCode(language: string): Promise<any | null> {
const languagefile = __dirname + '/../../public/languages/' + language + '.js';
await access(languagefile, fsconstants.R_OK);
return await import(languagefile);
}
}
From the Client I do a simple http://localhost:3001/i18n/fr-FR
and get the above result.
Again, where is the 'default' section coming from?
There should be esModuleInterop enabled in your tsconfig.json
https://www.typescriptlang.org/tsconfig#esModuleInterop
a default import like import moment from "moment" acts the same as const moment = require("moment").default
That's why you have default object exist.
tsconfig.json
{
"compilerOptions": {
"esModuleInterop": true, // change it to false or remove it
}
}

How to fix AXIOS_INSTANCE_TOKEN at index [0] is available in the Module context

I am using Axios in my project to call some third-party endpoints. I don't seem to understand the
error
Nest can't resolve dependencies of the HttpService (?). Please make sure that the argument
AXIOS_INSTANCE_TOKEN at index [0] is available in the TimeModule context.
Potential solutions:
- If AXIOS_INSTANCE_TOKEN is a provider, is it part of the current TimeModule?
- If AXIOS_INSTANCE_TOKEN is exported from a separate #Module, is that module imported within TimeModule?
#Module({
imports: [ /* the Module containing AXIOS_INSTANCE_TOKEN */ ]
})
This is the module
#Module({
imports: [TerminalModule,],
providers: [TimeService, HttpService],
controllers: [TimeController]
})
export class TimeModule { }
This is the service
#Injectable()
export class TimeService {
constructor(private httpService: HttpService,
#InjectModel('PayMobileAirtime') private time: Model<Time>,
#Inject(REQUEST) private request: any,
) { }
This is an example of one of my get and post methods
async PrimeAirtimeProductList(telcotime: string) {
let auth = await this.TimeAuth()
const productList = await this.httpService.get(`https://clients.time.com/api/top/info/${telcotime}`,
{
headers: {
'Authorization': `Bearer ${auth.token}`
}
}
).toPromise();
return productList.data
}
Post
const dataToken = await this.manageTimeAuth()
const url = `https://clients.time.com/api/dataup/exec/${number}`
const BuyTelcoData = await this.httpService.post(url, {
"product_id": product_id,
"denomination": amount,
"customer_reference": reference_id
}, {
headers: {
'Authorization': `Bearer ${dataToken.token}`
}
}).toPromise();
const data = BuyTelcoData.data;
Import HttpModule from #nestjs/common in TimeModule and add it to the imports array.
Remove HttpService from the providers array in TimeModule. You can directly import it in the TimeService.
import { HttpModule } from '#nestjs/common';
...
#Module({
imports: [TerminalModule, HttpModule],
providers: [TimeService],
...
})
TimeService:
import { HttpService } from '#nestjs/common';
If your response type is an Observable of type AxiosResponse, then import these two as well in the service file TimeService.
import { Observable } from 'rxjs';
import { AxiosResponse } from 'axios';
For reference, check out http-module and this post.
Don't pass HttpService in the providers. Import only HttpModule.

Using nestjs DI with class validator

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

Angular 7/8 - How to get url parameters in app component

I have Single sign on in place but for testing I want to read the values from the url localhost:4200/?id=test&name=testing&email=testing#test.com and pass them to an API in app component.
there will be a flag on which basis I will reading from url instead of using single sign on function
if (url_enabled == true) {
getParamsFromUrl()
} else {
singleSignOn()
}
I tried ActivatedRoute but it doesn't seem to be working.
I have tried queryParams, params, url, queryParamsMap but none of these seems to be working. all I get is empty value.
inside app component
app.component.ts
getParamsFromUrl() {
this._router.events.subscribe((e) => {
if (e instanceof NavigationEnd) {
console.log(e.url)
}
})
}
this.route.queryParams.subscribe(params => {
console.log(params);
})
app.component.html
<router-outlet></router-outlet>
app-routing.module.ts
const routes: Routes = [
{path:'*/:id', component: AppComponent},
];
I have tried whatever I could found on stackoverflow or other blogs. Can somebody point out what am I missing here?
For this route:
You can try this way:
const routes: Routes = [
{path:'*/:id', component: AppComponent},
];
In AppComponent .ts file:
constructor(
private activatedRoute: ActivatedRoute,
) { }
ngOnInit() {
this.activatedRoute.params.subscribe(params => {
const id = params['id'];
console.log('Url Id: ',id);
}
OR
ngOnInit() {
this.activatedRoute.queryParams.subscribe(params => {
const id = +params.id;
if (id && id > 0) {
console.log(id);
}
});
}
first of all there is an url with queryParams like yours :
localhost:4200/?id=test&name=testing&email=testing#test.com
in this way tou get to the queryparams with ActivatedRoute object lik :
this.name = this.activatedRoute.snapshot.queryParamMap.get('name'); // this.name = 'testing'
Or :
this.activatedRoute.queryParams.subscribe(params => {
this.name= params['name'];
});
and the other way is
localhost:4200/test/testing/testing#test.com
you use for sync retrieval (one time) :
this.name = this.activatedRoute.snapshot.ParamMap.get('name');
Angular comes us with the ActivatedRoute object. We can access the URL parameter value in same way its done above with little difference. Data in this type can be accessed with two different ways. One is through route.snapshot.paramMap and the other is through route.paramMap.subscribe. The main difference between the two is that the subscription will continue to update as the parameter changes for that specific route.
ngOnInit() {
this.route.paramMap.subscribe(params => {
this.userType = params.get("userType")
})
}
You need to create a new component and update the routing configuration as follows:
First, create a new component: MainComponent:
import { Component } from '#angular/core';
#Component({
selector: 'main',
template: `<router-outlet></router-outlet>`,
})
export class MainComponent {
constructor() { }
}
Then, update your AppModule:
import { AppComponent } from './app.component';
import { MainComponent } from './main.component';
#NgModule({
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot([
{path: '', component: AppComponent}
])
],
declarations: [ MainComponent, AppComponent ],
bootstrap: [ MainComponent ]
})
export class AppModule { }
Finally, you'll need to update your index.html file(Make sure to load the brand new component instead of the AppComponent):
<main>loading</main>
Now you'll be able to read your parameters as requested in your AppComponent:
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute, Params } from '#angular/router';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
params: Params;
constructor(private route: ActivatedRoute){}
ngOnInit() {
this.route.queryParams.subscribe((params: Params) => {
this.params = params;
console.log('App params', params);
const id = params['id'];
console.log('id', id);
});
}
}
See a working example here: https://read-params-app-component.stackblitz.io/?id=test&name=testing&email=testing#test.com.
And find the source code here.
I hope it helps!
You can try like this
constructor(
private activatedRoute: ActivatedRoute
)
ngOnInit() {
this.activatedRoute.paramMap
.pipe(
tap(console.log(this.activatedRoute.snapshot.paramMap.get(
"id"
)))
).subscribe()
}
Let me know if you need any help
Using Transition from #uirouter/core makes it easy to get params from url.
import {Transition} from '#uirouter/core';
#Component()
export class MyComponent {
public myParam = this.transition.params().myParam;
public constructor(public transition: Transition) {}
}
I used jquery inside angular 8 and got the href using jquery $ variable after declaring it in app component.
import { query } from '#angular/animations';
declare var $: any;

Angular 2 GET request

I am extremely incompetent in Angular 2. I have been using it for a while, and the only thing that I can ever get to work is when I copy and paste from tutorial videos.
With that said, I am creating a REST api (Node.js, expressJS, angular2, mongodb) and I am having trouble calling a GET from frontend to backend. I am trying to call an endpoint (/games) that returns an array of game objects. I want to use this array to display the games eventually, but I can't even get a successfull call working correctly.
I'm trying to use all-games.component.ts to use the service get-last25.service.ts to return all of the games from the database (25 max). I have JWT authentication turned off for this route for now.
The errors I receive:
Unhandled Promise rejection: No provider for GetLast25Service! ; Zone: angular ; Task: Promise.then ;
and
EXCEPTION: Uncaught (in promise): Error: DI Error
and an empty error...
=====================================
Code:
get-last25.service.ts:
import { Injectable } from '#angular/core';
import {Http, Headers} from '#angular/http';
import 'rxjs/add/operator/map';
import { tokenNotExpired } from 'angular2-jwt';
#Injectable()
export class GetLast25Service {
constructor(private http:Http) { }
getLast25(game){
if(game == null || game == undefined){
let headers = new Headers();
headers.append('Content-Type','application/json');
return this.http.get('http://localhost:3000/games',{ headers: headers })
.map(res => res.json());
} else {
let headers = new Headers();
headers.append('Content-Type','application/json');
return this.http.get(`http://localhost:3000/games/${game}`,{ headers: headers })
.map(res => res.json());
}
}
}
all-games.component.ts:
import { Component, Input, OnInit, ViewEncapsulation } from '#angular/core';
import { ActivatedRoute, Router, Params } from '#angular/router';
//import {FlashMessagesService} from 'angular2-flash-messages';
import { AuthService } from '../../services/auth.service';
import { GetLast25Service } from '../../services/get-last25.service';
#Component({
selector: 'app-all-games',
templateUrl: './all-games.component.html',
styleUrls: ['./all-games.component.css']
})
export class AllGamesComponent implements OnInit {
private games: any[];
private comments: any;
constructor(
private route: ActivatedRoute,
private router: Router,
private authService: AuthService,
//private flashMessage: FlashMessagesService,
private getLast25Service: GetLast25Service
) { }
ngOnInit() {
this.getLast25Service.getLast25(null).subscribe(games => {
this.games = games;
},
err => {
return false;
});
}
}
whenever I call get(http://localhost:3000/games), it returns:
[
{
"_id": "58e87513fbcdca1f54b4a84c",
"name": "League of Legends",
"game_endpoint": "lol",
"release_date": "2012-04-23T18:25:43.511Z",
"image_path": "../assets/images/lol.png",
"__v": 0,
"posts": 0,
"subscribers": 0,
"categories": [
"MOBA",
"strategy",
"multiplayer",
"Dota ripoff"
]
},
{
"_id": "58e8823b8da3fa1e6c8f0885",
"name": "Rocket League",
"game_endpoint": "rl",
"release_date": "2012-04-23T18:25:43.511Z",
"image_path": "../assets/images/rocketleague.png",
"__v": 0,
"posts": 0,
"subscribers": 0,
"categories": [
"cars",
"racing",
"soccer",
"chat_disabled"
]
}
]
You need to add the service as a Provider to the module,
#NgModule({
providers: [
GetLast25Service
]
})

Resources