I am trying to register a service toa specific module with the help of 'provideIn' attribute in my angular7 app.
test.module.ts
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { TestComponent} from './test.component'
#NgModule({
declarations: [TestComponent],
imports: [
CommonModule
],
exports: [TestComponent]
})
export class TestModule { }
test.component
import { Component, OnInit } from '#angular/core';
import { TestServiceService } from '../test-service.service';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit {
constructor(public service: TestServiceService) { }
ngOnInit() {
}
}
test-service.ts
import { Injectable } from '#angular/core';
import { TestModule } from './test/test.module';
#Injectable({
providedIn: TestModule
})
export class TestServiceService {
val = 3;
constructor() { }
getDetails() {
return this.val;
}
}
when I run my app it shows below error
ERROR Error: Uncaught (in promise): Error: StaticInjectorError(AppModule)[TestComponent -> TestServiceService]:
StaticInjectorError(Platform: core)[TestComponent -> TestServiceService]:
NullInjectorError: No provider for TestServiceService!
when I change provideIn atrribute value as 'root' everything works fine.How can I register a service to specific module with the help of 'provideIn' attribute in angular7?
TestService has to be depend on TestModule in order to use providedIn to provide the service to the module. This creates a circular dependency, because TestModule has to depend on TestComponent which depends on TestService. Define the provider in the module so that the module depends on the component and the service.
#NgModule({
declarations: [TestComponent],
imports: [
CommonModule
],
providers: [
TestService
],
exports: [TestComponent]
})
export class TestModule { }
Define the service as Injectable, and set providedIn to null.
#Injectable({
providedIn: null
})
export class TestServiceService {
val = 3;
constructor() { }
getDetails() {
return this.val;
}
}
Change test.component to this :
import { Component, OnInit } from '#angular/core';
import { TestServiceService } from '../test-service.service';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss'],
providers : [TestServiceService]
})
export class TestComponent implements OnInit {
constructor(public service: TestServiceService) { }
ngOnInit() {
}
}
and if you want to have single instance of service in module change test.module.ts to this :
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { TestComponent} from './test.component'
#NgModule({
declarations: [TestComponent],
imports: [
CommonModule
],
providers :[TestServiceService ],
exports: [TestComponent]
})
export class TestModule { }
do one of them
Related
email-verification.guard.ts
#Injectable()
export class EmailVerificationGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const skipEmailVerification = this.reflector.get<boolean>('skipEmailVerification', context.getHandler());
if (skipEmailVerification) {
return true;
}
const request: Request = context.getArgs()[0];
if (!request.authPayload) {
throw new ForbiddenException('User not found');
}
if (!request.authPayload[Auth0Namespace.AppMetadata]) {
throw new ForbiddenException('Please verify your email before continuing');
}
return true;
}
}
dynamic-auth.module.ts
import { DynamicModule, Module } from '#nestjs/common';
import { authService } from './auth.service';
import { EmailVerificationGuard } from './email-verification.guard';
#Module({})
export class DynamicAuthModule {
static register(): DynamicModule {
return {
module: DynamicAuthModule,
providers: [authService, EmailVerificationGuard],
exports: [authService, EmailVerificationGuard],
};
}
}
app module (host)
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { DynamicAuthModule, EmailVerificationGuard } from 'dyamic-auth-module';
#Module({
imports: [DynamicAuthModule.register({ folder: './config' })],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Can I use EmailVerificationGuard in this host module? If not why?
Note:
nestjs packages version: 8.4.7
I tried this but I get this reflector dependency issue, How to resolve this?
Error: Nest can't resolve dependencies of the EmailVerificationGuard (?). Please make sure that the argument Reflector at index [0] is available in the AppModule context.
Potential solutions:
- If Reflector is a provider, is it part of the current AppModule?
- If Reflector is exported from a separate #Module, is that module imported within AppModule?
#Module({
imports: [ /* the Module containing Reflector */ ]
})
Is there any other way to handle this?
I followed the controller-service-repository architecture and I want to use dependency inversion on StoneRepository. Having the code from bellow I get:
[Nest] 22656 - 03/21/2022, 5:01:44 PM ERROR [ExceptionsHandler] this.stoneRepository.getStones is not a function
What have I done wrong?
Please help.
constants.ts
export const STONE_REPOSITORY_TOKEN = Symbol("STONE_REPOSITORY_TOKEN");
app.module.ts
import { Module } from "#nestjs/common";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
import { StoneModule } from "./stone/stone.module";
#Module({
imports: [StoneModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
stone.module.ts
import { Module } from "#nestjs/common";
import { StoneController } from "./stone.controller";
import { StoneService } from "./stone.service";
import { StoneRepository } from "./stone.repository";
import { STONE_REPOSITORY_TOKEN } from "./constants";
#Module({
imports: [],
controllers: [StoneController],
providers: [
{
provide: STONE_REPOSITORY_TOKEN,
useValue: StoneRepository,
},
StoneService,
],
})
export class StoneModule {}
stone.controller.ts
import { Controller, Get } from "#nestjs/common";
import { StoneService } from "./stone.service";
import { Stone } from "./domain/Stone";
#Controller()
export class StoneController {
constructor(private stoneService: StoneService) {}
#Get("/stone")
async getStones(): Promise<Stone[]> {
return await this.stoneService.getStones();
}
}
stone.interface.repository.ts
import { Stone } from "./domain/Stone";
export interface StoneInterfaceRepository {
getStones(): Promise<Stone[]>;
}
stone.service.ts
import { Inject, Injectable } from "#nestjs/common";
import { StoneInterfaceRepository } from "./stone.interface.repository";
import { Stone } from "./domain/Stone";
import { STONE_REPOSITORY_TOKEN } from "./constants";
#Injectable()
export class StoneService {
constructor(
#Inject(STONE_REPOSITORY_TOKEN)
private stoneRepository: StoneInterfaceRepository,
) {}
async getStones(): Promise<Stone[]> {
return await this.stoneRepository.getStones();
}
}
stone.repository.ts
import { Injectable } from "#nestjs/common";
import { StoneInterfaceRepository } from "./stone.interface.repository";
import { Stone } from "./domain/Stone";
#Injectable()
export class StoneRepository implements StoneInterfaceRepository {
async getStones(): Promise<Stone[]> {
return Promise.resolve([new Stone()]);
}
}
You are using useValue for the STONE_REPOSITORY_TOKEN token's custom provider. This means that Nest will inject the direct reference, not the class instance, so you have no access to instance methods, like getStones(). Change your module to this:
#Module({
imports: [],
controllers: [StoneController],
providers: [
{
provide: STONE_REPOSITORY_TOKEN,
useClass: StoneRepository,
},
StoneService,
],
})
export class StoneModule {}
I am new to Angular and I am trying to implement api call which send token in header on all api so I am using Interceptor.
I am not able to set header in request.
jwt.interceptor.ts
import { Injectable } from '#angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '#angular/common/http';
import { Observable } from 'rxjs';
#Injectable()
export class JwtInterceptor implements HttpInterceptor {
constructor() { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let currentUser = localStorage.getItem('useremail')
let currentUserToken = localStorage.getItem('token')
if (currentUser && currentUserToken) {
request = request.clone({
setHeaders: {
'Authorization': `${currentUserToken}`
}
});
}
// console.log("Request", request)
return next.handle(request);
}
}
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http';
import { AppUserListingComponent } from './app-user-listing/app-user-listing.component';
import { JwtInterceptor } from './jwt.interceptor';
#NgModule({
providers: [ { provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true } ],
bootstrap: [AppComponent]
})
export class AppModule { }
user-listing.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { map } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class UserListingService {
apiUrl = "URL";
constructor(
private http: HttpClient
) { }
fetchAllUsers() {
return this.http.get(`${this.apiUrl}/fetchAdminsUsers`)
}
}
What is the reason my code is not working ?
Thanks in advance
In your app.module.ts code there's no import: [HttpClientModule] Do you have imported the HttpClientModule?
I'm experiencing a similar issue with angular 8.x http interceptor is not working OK.
Add imports: [BrowserModule, HttpClientModule] in #NgModule.
Authorization is a special header, you need to add withCredentials: true to the request to add it.
request = request.clone({
setHeaders: {
'Authorization': `${currentUserToken}`
},
withCredentials: true
});
try using instance of like below.
return next.handle(request).pipe(
tap(
event => {
if(event instanceof HttpResponse){
//api call success
console.log('success in calling API : ', event);
}
},
error => {
if(event instanceof HttpResponse){
//api call error
console.log('error in calling API : ', event);
}
}
)
)
I am trying to create a toast using Material Snackbar from a custom class.
I am getting an error in my custom class ( Unable to find open from undefined. ) but working fine in user.service.ts
If ngZone is used, then i am getting an error, (unable to find run from undefined)
Note: In ErrorHandler Class
console.log(this.snackBar) // gives undefined
app.module.ts
providers: [ErrorHandler],
bootstrap: [AppComponent]
})
export class AppModule { }
User Service
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { catchError } from 'rxjs/operators';
import { environment } from '../../environments/environment';
import { ErrorHandler } from '../classes/error-handler';
import {MatSnackBar} from '#angular/material';
#Injectable({
providedIn: 'root'
})
export class UserService {
private url = environment.api+'/login';
constructor(private http: HttpClient, private eh:ErrorHandler, private sb: MatSnackBar) { }
login(credentials){
this.sb.open("hello world"); // Working Fine
return this.http.post(this.url, {})
.pipe(
catchError(this.eh.handleError)
);
}
}
Error Handler Class
import {Component, Injectable, NgZone} from '#angular/core';
import { HttpErrorResponse } from '#angular/common/http';
import { throwError } from 'rxjs';
import * as _ from 'lodash';
import {MatSnackBar} from '#angular/material';
#Injectable({
providedIn: 'root'
})
export class ErrorHandler {
constructor (private snackBar: MatSnackBar) {}
public handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
console.error('An error occurred:', error.error.message);
} else {
console.error("Error code working")
console.log(this.snackBar) // gives undefined
this.snackBar.open("Hello world"); // Throwing Error
}
// return an observable with a user-facing error message
return throwError('Something bad happened; please try again later.');
};
}
Thanks to all. I fixed it. Unfortunately i missed the stackoverflow link.
Change in
catchError((res) => this.eh.handleError(res))
did the trick
UserService
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { catchError } from 'rxjs/operators';
import { environment } from '../../environments/environment';
import { ErrorHandler } from '../classes/error-handler';
import {MatSnackBar} from '#angular/material';
#Injectable({
providedIn: 'root'
})
export class UserService {
private url = environment.api+'/login';
constructor(private http: HttpClient, private eh:ErrorHandler, private sb: MatSnackBar) { }
login(credentials){
this.sb.open("hello world"); // Working Fine
return this.http.post(this.url, {})
.pipe(
catchError((res) => this.eh.handleError(res)) // change in this line
);
}
}
I stumbled upon the same issue in my application a few days ago. The reason is the context of this.
In
This.sb.open("hello world"); // Working Fine
the context of this is the UserService class. While in
this.snackBar.open("Hello world"); // Throwing Error
the context of this changed. Probably to CatchSubscriber.
You already mentioned that:
catchError((res) => this.eh.handleError(res)) // change in this line
resolves the error. Because it changed the context of this back to the UserService class. An alternative solution is to use the arrow syntax for the whole login():
login = (credentials) => {
// your code goes here
}
I've been trying to troubleshoot a strange problem with angular 2 where it isn't detecting my provider declaration, but nothing is working. I can't even replicate it in plunkr.
I'm using angular 2 rc 3 with router 3.0 alpha.8.
Error message is: ORIGINAL EXCEPTION: No provider for TestService!
app.routes.ts:
import { provideRouter, RouterConfig } from '#angular/router';
import { HomeComponent } from './app/home/home.component';
import { LogInComponent } from './app/log-in/log-in.component';
import { SignUpComponent } from './app/sign-up/sign-up.component';
export const routes: RouterConfig = [
{ path: '', component: HomeComponent },
{ path: 'log-in', component: LogInComponent },
{ path: 'sign-up', component: SignUpComponent }
];
export const APP_ROUTER_PROVIDERS = [
provideRouter(routes)
];
main.ts:
import { bootstrap } from '#angular/platform-browser-dynamic';
import { enableProdMode } from "#angular/core";
import { AppComponent } from './app/app.component';
import { APP_ROUTER_PROVIDERS } from './app.routes';
// enableProdMode();
bootstrap(AppComponent, [
APP_ROUTER_PROVIDERS
])
.catch(error => console.log(error));
app/app.component.ts:
import { Component } from '#angular/core';
import { ROUTER_DIRECTIVES } from '#angular/router';
import { TestService } from './shared/test.service';
#Component({
selector: 'my-app',
template: `
<div id="menu">
<a [routerLink]="['/sign-up']"><button>Sign Up</button></a>
</div>
<router-outlet></router-outlet>
`,
directives: [ROUTER_DIRECTIVES],
providers: [TestService]
})
export class AppComponent {
constructor() { }
}
app/sign-up/sign-up.component.ts:
import { Component } from '#angular/core';
import { ROUTER_DIRECTIVES } from '#angular/router';
import { TestService } from '../shared/test.service';
#Component({
selector: 'sign-up',
template: `<h1>Sign up!</h1>`,
directives: [ROUTER_DIRECTIVES]
})
export class SignUpComponent {
constructor(private testService: TestService) {
this.testService.test('works?');
}
}
app/shared/test.service.ts:
import { Injectable } from '#angular/core';
#Injectable()
export class TestService {
constructor() { }
test(message: string) {
console.log(message);
}
}
So, I'm providing the testservice in the base component (app.component.ts) because I want all my components to access the same instance. However, when I navigate to sign-up, I get the no provider for testservice error. If I provide the TestService within the sign-up component, this then works:
import { Component, OnInit } from '#angular/core';
import { ROUTER_DIRECTIVES } from '#angular/router';
import { TestService } from '../shared/test.service';
#Component({
selector: 'sign-up',
template: `<h1>Sign up!</h1>`,
directives: [ROUTER_DIRECTIVES],
providers: [TestService]
})
export class SignUpComponent implements OnInit {
constructor(private testService: TestService) { }
ngOnInit() { }
}
However, I need the same instance accessible throughout my app, so how can I inject this at the main component level?
I even tried replicating this app-level service providing with plunkr with the same version of everything, but it doesn't seem to give me the same error...
http://plnkr.co/edit/5bpeHs72NrlyUITCAJim?p=preview
Injecting something on the app level is done in bootstrap:
main.ts:
import { TestService } from '../shared/test.service';
bootstrap(AppComponent, [
APP_ROUTER_PROVIDERS, TestService
])
For me, some references to the "services" folder were "Services". When I made them all "services" (lower case), it worked.
For example:
import {ApiService} from "./Services/api.service";
didn't work, but this worked:
import {ApiService} from "./services/api.service";
To All future readers - and this is correct for angular 2.0.0 rc-4:
make sure that you follow the below folder structure:
root:
index.html
package.json
systemjs.config.js
tsconfig.json (if using TypeScript)
typings.json
app (folder):
- main.js (the root script for your app)
- app.component.js (the root component for the entire app)
This is crucial for the hierarchical injection to properly scope and identify providers.
Also, and this is very important - if still encountering problems and you are using TypeScript or any other transpiled language- delete any artifacts which your transpiler produces for every associated class in the problematic object graph - this, and the OP's answer eventually helped in my case (*.map.js and *.js files deleted and re-transpiled).
It was a configuration issue after all, and a completely elusive one at that.
Per the ang2 style guide, I had my main.ts one folder up from my main app folder, and in systemjs.config I had to declare the main for app as '../main.js'. When I moved the main file to the root app folder and changed the package declaration in systemjs to 'main.js' it worked.
The odd thing is everything else worked, right up until I try to utilize hierarchical dependency injection.