Invalid provider error when injecting service into service in Angular 2 - javascript

I currently have a module setup like below (exerpt);
AppModule
RoutingModule
AuthRouteGuard
AuthModule
LoginFormComponent
AuthService
I have defined my AuthService (responsible for handling user authentication and provides a method for determining whether the current user is authenticated) as a provider in my AuthModule;
// auth.module.ts - uses https://github.com/auth0/angular2-jwt
export function authHttpServiceFactory(http: Http, options: RequestOptions) {
return new AuthHttp(new AuthConfig({
tokenName: jwtLocalStorageKey
}), http, options);
}
export let authHttpServiceProvider = {
provide: AuthHttp,
useFactory: authHttpServiceFactory,
deps: [Http, RequestOptions]
};
#NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule
],
exports: [AuthComponent],
declarations: [AuthComponent, LoginComponent, RegisterComponent],
providers: [
AuthService,
authHttpServiceProvider
]
})
export class AuthModule { }
I can use this service with no problem within its sibling LoginFormComponent. When I attempt to use the AuthService within the AuthRouteGuard class in the RoutingModule however I get the following error;
Error: Invalid provider for the NgModule 'AuthModule' - only instances of Provider and Type are allowed, got: [?undefined?, ...]
I have the AuthModule imported within the RoutingModule. The error above occurs as soon as the AuthService is defined as a dependency for the AuthRouteGuard;
export class AuthRouteGuard implements CanActivate {
constructor(
private router: Router,
private authService: AuthService // Removing this injection removes the error
) {}
canActivate() {
// #todo: if not authenticated
this.router.navigate(['/login']);
return true;
}
}
What am I missing here, and why would injecting the service in the constructor cause an invalid provider error that does not occur when that injection is removed?
Edit - Same error occurs if the authHttpServiceProvider provider is removed altogether, so the AuthModule module looks like;
#NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule
],
exports: [AuthComponent],
declarations: [AuthComponent, LoginComponent, RegisterComponent],
providers: [
AuthService
]
})
export class AuthModule { }

Add authHttpServiceProvider to imports of the module. It's exported to global and not available to module. So you can't provide the service because you have unknown provider to the module.
#NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule
],
exports: [AuthComponent],
declarations: [AuthComponent, LoginComponent, RegisterComponent],
providers: [
AuthService
]
})
export class AuthModule {

The actual problem was within the AuthService itself.
AuthModule defined a constant;
export const jwtKey = 'jwt';
Which was being imported into the AuthService and used;
import { jwtKey } from '../auth.module';
For some reason if I remove this import everything works fine.

Related

Use defined value in module provider in other Angular module

I would like use provider value defined in other module. Here is example:
app.module.ts
...
import { ThemeModule } from '../shared/modules/theme/theme.module';
...
#NgModule({
declarations: [
RootComponent,
LoginScreenComponent,
],
imports: [
BrowserModule.withServerTransition({ appId: 'serverApp' }),
BrowserAnimationsModule,
AppRoutingModule,
ConfigModule,
ThemeModule,
....
],
providers: [
...
{ provider: "THEME_NAME", useValue: "VALUE" },
],
bootstrap: [RootComponent]
})
export class MDMToolModule {}
theme.module.ts
import { NgModule } from '#angular/core';
import { ThemeService } from './services/ThemeService';
#NgModule({
imports: [],
declarations: [],
providers: [
{provide: "ThemeService", useFactory: (THEME_NAME) => {
return new ThemeService(THEME_NAME)
}},
],
exports: []
})
export class ThemeModule {}
Is there possibility to pass VALUE defied not in module like above example (THEME NAME)?
if you're providing value in root module then it will be available to all other modules too so you can then simply ask for that value using Inject decorator like this:
#Injectable()
export class ThemeService {
constructor(#Inject("THEME_NAME") theme: string) {}
}
otherwise you can import your MDMToolModule inside ThemeModule though judging by the code you provided I'm assuming this MDMToolModule is your root module,
you can also use Injection Token to avoid using those magic strings like this:
const ThemeName = new InjectionToken("token to inject theme name");
and then use it inside theme.module.ts
#NgModule({
providers: [
{ provide: ThemeName, useValue: "DarkKnight" },
ThemeService
]
})
export class ThemeModule {}
theme.service.ts
#Injectable()
export class ThemeService {
constructor(#Inject(ThemeName) theme: string) {}
}

NestJS - JWTModule context dependency

After run my app i get this...
[Nest] 5608 - 01.01.2021, 18:12:05 [ExceptionHandler] Nest can't resolve dependencies of the JwtService (?). Please make sure that the argument JWT_MODULE_OPTIONS at index [0] is available in the JwtModule context.
Potential solutions:
- If JWT_MODULE_OPTIONS is a provider, is it part of the current JwtModule?
- If JWT_MODULE_OPTIONS is exported from a separate #Module, is that module imported within JwtModule?
#Module({
imports: [ /* the Module containing JWT_MODULE_OPTIONS */ ]
})
can someone tell me what i have wrong with my code?
#Module({
imports: [TypeOrmModule.forFeature([User]),
JwtModule.register({
secretOrPrivateKey: 'secret12356789'
})
],
providers: [UserService]
})
export class AuthModule { }
#Module({
imports: [
TypeOrmModule.forRoot({
//
}),
AuthModule,
UserModule,
JwtModule
],
controllers: [AppController, UserController, AuthController ],
providers: [AppService, UserService, AuthService ],
})
export class AppModule {}
thanks for any help
///////////////////////////////////////////////////////////////
In your AppModule you have the JwtModule imported but adding no options to it. This is what's causing the issue. As you already have the JwtModule registered in the AuthModule, this probably isn't what you're meaning to do.
You have the UserService registered in at least two places (AuthModule and AppModule), you're probably meaning to add the UserService to the exports of UserModule and then add the UserModule to the imports array of the module where you need the UserService.
TL;DR See, copy/paste examples.
Recently had the same issue.
As Nest.JS recommends the JwtModule could be declared in an authorization module: https://docs.nestjs.com/security/authentication. And, yes, it should be declared once with all its settings. The topic message could come from a multiple declaration (some of them has no JWT_MODULE_OPTIONS). Can wrap Jay's response with a prepared working example.
So the auth module should look like:
//./authorization/authorization.module.ts
import { Module } from '#nestjs/common';
import { JwtModule } from '#nestjs/jwt';
import { AuthorizationController } from './authorization.controller';
import { AuthorizationService } from './authorization.service';
#Module({
imports: [
...
JwtModule.register({
secret: process.env.ACCESS_TOKEN_SECRET || 'SOME_SECURE_SECRET_jU^7',
signOptions: {
expiresIn: process.env.TOKEN_EXPIRATION_TIME || '24h',
}
}),
....
],
controllers: [AuthorizationController],
providers: [AuthorizationService],
exports: [AuthorizationService, JwtModule]//<--here exports JwtModule
//as a part of AuthorizationModule
})
export class AuthorizationModule {};
Then your AppModule will inject the exported JwtModule from AuthorizationModule in this way.
//./app.module.js
import { Module } from '#nestjs/common';
import { AuthorizationModule } from './authorization/authorization.module';
#Module({
imports: [
...
AuthorizationModule, //<-- here injects the set up JwtModule
//NB! No additional injections required!
...
],
...
})
export class AppModule {};
Hope, this will help. ;)

HttpInterceptor provided in Angular library is not working from Angular app

I am developing an Angular library where there is an authentication module that provides an HttpInterceptor. The main idea is to have this interceptor working automatically in any app that imports this authentication module without having to do any extra setup at it.
What I have so far is the following:
AuthenticationModule
#NgModule({
imports: [ConcreteAuthModule],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: BearerInterceptor,
multi: true
}
]
})
export class AuthenticationModule {
static forRoot(config: AuthConfig): ModuleWithProviders {
return {
ngModule: AuthenticationModule,
providers: [
{
provide: AUTH_CONFIG,
useValue: config
}
]
};
}
}
ConcreteAuthModule
#NgModule({
imports: [ThirdPartyLibModule],
providers: [
{
provide: AuthenticationService,
useClass: ConcreteAuthService
}
]
})
export class ConcreteAuthModule { }
BearerInterceptor
#Injectable()
export class BearerInterceptor implements HttpInterceptor {
constructor(private authService: AuthenticationService) { }
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
const headers: any = {};
if (this.authService.isUserAuthenticated()) {
headers.Authorization = `Bearer ${this.singleSignOnService.getUserToken()}`;
}
const authReq = req.clone({ setHeaders: headers });
return next.handle(authReq);
}
}
And from a test Angular app I am importing this module the following way at the AppModule:
#NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
HttpClientModule,
AuthenticationModule.forRoot({ /* config stuff */ })
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
I checked how some third party libraries did this, also came across a couple of Stack Overflow questions that discussed about this and they all suggested having an Angular module created (I already have it: AuthenticationModule), then provide the http-interceptor on it (already have it too) and finally importing this module from an Angular app (also did this).
But still, none of the http requests in my app are being intercepted.
Tried importing the BearerInterceptor directly from my test app and providing it on the AppModule like this:
import { BearerInterceptor } from 'my-lib':
#NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
HttpClientModule,
AuthenticationModule.forRoot({ /* config stuff */ })
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: BearerInterceptor,
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule {}
And that works! But this workaround is not what I am looking for...
You're very close to a working solution.
The key is to look at how the module is being imported by the AppModule.
imports: [
BrowserModule,
HttpClientModule,
AuthenticationModule.forRoot({ /* config stuff */ })
],
That is how the AuthenticationModule is imported by the AppModule, but that NgModule does not provide the HTTP_INTERCEPTORS.
You've provided the token in the #NgModule() decorator, but that module is not being used by your application. It's the module defined by the forRoot() function.
Move the declaration of the HTTP_INTERCEPTORS to the forRoot() function.
Try this instead:
#NgModule({
imports: [ConcreteAuthModule]
})
export class AuthenticationModule {
static forRoot(config: AuthConfig): ModuleWithProviders {
return {
ngModule: AuthenticationModule,
providers: [
{
provide: AUTH_CONFIG,
useValue: config
}, {
provide: HTTP_INTERCEPTORS,
useClass: BearerInterceptor,
multi: true
}
]
};
}
}
The problem was because I was installing the library locally for testing purposes like the following:
$ npm i --save my-lib_path/dist/my-lib
After I published it and installed it from the npm registry it worked fine:
$ npm i --save my-lib

Angular passing dynamic entry components into module and then passing them again into another module

I'm making a modal component for a component library. I made a 3rd party modal library that I'm using within my component library. A main feature is being able to pass a component via a service and dynamically adding it to the modal.
My modal lib has a static method that allows you to add your component to the module's entry components. It looks like:
export class A11yModalModule {
static withComponents(components: any[]) {
return {
ngModule: A11yModalModule,
providers: [{
provide: ANALYZE_FOR_ENTRY_COMPONENTS,
useValue: components,
multi: true
}]
};
}
}
Cool, that works. I can pass components into it when I import the module like this: A11yModalModule.withComponents([ModalContentComponent])
My problem occurs when I abstract this out another level. So now instead of 2 modules I have 3. I need to pass a component like I did above from the lib consumer's module, to my component module, and then into the modal module.
How can I pass components from the lib module to the modal module?
I think I'm getting close. Here are my 3 modules
// app module
#NgModule({
declarations: [AppComponent, ModalContentComponent],
imports: [
LibModalModule.withComponents([ModalContentComponent])
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
// lib module
#NgModule({
imports: [CommonModule],
declarations: [LibModal],
providers: [LibModalService],
exports: []
})
export class LibModalModule {
static withComponents(components: any[]) {
return {
ngModule: LibModalModule,
imports: [CommonModule, A11yModalModule.withComponents(components)]
};
}
}
// a11y modal module
#NgModule({
imports: [CommonModule],
declarations: [ModalComponent],
exports: [],
providers: [ModalService, DomService],
entryComponents: [ModalComponent]
})
export class A11yModalModule {
static withComponents(components: any[]) {
return {
ngModule: A11yModalModule,
providers: [{
provide: ANALYZE_FOR_ENTRY_COMPONENTS,
useValue: components,
multi: true
}]
};
}
}
withComponents method should return ModuleWithProviders object which is just wrapper around a module that also includes the providers.
It can't have imports property or something else because it doesn't understand those properties. Here's an excerpt from angular source code that is responsible from reading metadata from ModuleWithProviders:
else if (importedType && importedType.ngModule) {
const moduleWithProviders: ModuleWithProviders = importedType;
importedModuleType = moduleWithProviders.ngModule;
if (moduleWithProviders.providers) {
providers.push(...this._getProvidersMetadata(
moduleWithProviders.providers, entryComponents,
`provider for the NgModule '${stringifyType(importedModuleType)}'`, [],
importedType));
}
}
As we can see angular compiler takes providers from the object that will returned in withComponents method.
So, in order to merge your modules you can either use your approach(provide ANALYZE_FOR_ENTRY_COMPONENTS in LibModalModule.withProviders) or reuse A11yModalModule.withComponents like:
#NgModule({
imports: [CommonModule, A11yModalModule],
providers: [LibModalService],
exports: []
})
export class LibModalModule {
static withComponents(components: any[]) {
return {
ngModule: LibModalModule,
providers: A11yModalModule.withComponents(components).providers
};
}
}
(Tested with AOT)
Also A11yModalModule has to be imported in LibModalModule if we want its providers to be included in our root module injector (And i suppose you're going to use ModalService and DomService that are declated in A11yModalModule). The reason of this is that angular includes all providers from transitive module in root module injector.
See also:
Avoiding common confusions with modules in Angular
What you always wanted to know about Angular Dependency Injection tree
I had a bug that was giving me a false flag. Turns out you can just add the same withComponents method to the component library module and it passes the component through. I'd love an explanation on how this works if anyone knows.
// app module
#NgModule({
declarations: [AppComponent, ModalContentComponent],
imports: [
LibModalModule.withComponents([ModalContentComponent])
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
// lib module
#NgModule({
imports: [CommonModule, A11yModalModule],
declarations: [LibModal],
providers: [LibModalService],
exports: []
})
export class LibModalModule {
static withComponents(components: any[]) {
return {
ngModule: LibModalModule,
providers: [{
provide: ANALYZE_FOR_ENTRY_COMPONENTS,
useValue: components,
multi: true
}]
};
}
}
// a11y modal module
#NgModule({
imports: [CommonModule],
declarations: [ModalComponent],
exports: [],
providers: [ModalService, DomService],
entryComponents: [ModalComponent]
})
export class A11yModalModule {
static withComponents(components: any[]) {
return {
ngModule: A11yModalModule,
providers: [{
provide: ANALYZE_FOR_ENTRY_COMPONENTS,
useValue: components,
multi: true
}]
};
}
}

How to load route configuration from server before dependency injection in angular 4?

My top menu module doesn't know anything about routes and modules, that will be used for menu items before they load from api.
var routeConfig = [
{loadChildren: "./widget1.ts#Widget1Module", path: "widget1.ts"},
{loadChildren: "./widget2.ts#Widget2Module", path: "widget2.ts"},
{loadChildren: "./widget3.ts#Widget3Module", path: "widget3.ts"}
]; // this must be loaded before AppRoutingModule inject
#NgModule({
imports: [
RouterModule.forRoot(
routeConfig
)
],
exports: [ RouterModule ]
})
export class AppRoutingModule {
};
Now i just use routeConfig as global variable and make request from pure javascript before angular imports module. How to do it correctly?
As Gunter said, we can delay bootstrap method and pass some parameters like that. But it is a wrong way in my case.
In angular we can redefine routing in our application. So we can leave initialization parameter for RouterModule empty:
#NgModule({
imports: [ BrowserModule, RouterModule.forRoot([]), HttpModule ],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
But then redefine them after web api retrieve us callback with route configuration:
export class AppComponent implements OnInit {
constructor(private router: Router, private http: Http) {
}
public isLoaded : bool = false;
ngOnInit() {
this.http.get('app/routerConfig.json')
.map((res:any) => res.json())
.subscribe(data => {
this.router.resetConfig(data);
this.isLoaded = true;
}, error => console.log(error));
}
}
Here is an example in Plunker.

Categories