I want to use 2 interceptor globally (httpInterceptorProviders,
jwtInterceptorProviders) but it not working in my lazy modules.
I have CoreModule and X lazy load modules. The weird part is that I have an autogenerated code by swagger generator(http services) this calls being intercepted but when I use custom http services interceptor not intercept this requests.
Index.ts where I get the providers
/** Http interceptor providers in outside-in order */
export const httpInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: HttpErrorInterceptor, multi: true }
];
export const jwtInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true }
];
CoreModule, I import my interceptors in providers
#NgModule({
imports: [
// angular
CommonModule,
HttpClientModule,
// ngrx
StoreModule.forRoot(reducers, { metaReducers }),
StoreRouterConnectingModule.forRoot(),
EffectsModule.forRoot([AuthEffects, GoogleAnalyticsEffects]),
environment.production
? []
: StoreDevtoolsModule.instrument({
name: "Angular NgRx Material Starter"
}),
// 3rd party
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
ApiModule.forRoot(() => {
return new Configuration({
basePath: `${environment.HOST}:${environment.PORT}`,
apiKeys: { Authorization: "" }
});
})
],
declarations: [],
providers: [
LocalStorageService,
AuthGuardService,
AnimationsService,
TitleService,
// retryHttpInterceptorProviders,
{ provide: ErrorHandler, useClass: AppErrorHandler },
httpInterceptorProviders,
jwtInterceptorProviders,
{ provide: RouterStateSerializer, useClass: CustomSerializer },
{
provide: HAMMER_LOADER,
useValue: () => new Promise(() => {})
},
AnalyticsService,
LayoutService,
StateService,
PetsServiceWithUpload
],
exports: [TranslateModule]
})
export class CoreModule {
constructor(
#Optional()
#SkipSelf()
parentModule: CoreModule
) {
if (parentModule) {
throw new Error("CoreModule is already loaded. Import only in AppModule");
}
}
}
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(
http,
`${environment.i18nPrefix}/assets/i18n/`,
".json"
);
}
AppModule
#NgModule({
declarations: [AppComponent, NavigationComponent, ErrorsComponent],
imports: [
BrowserAnimationsModule,
BrowserModule,
SharedModule,
AppRoutingModule,
AuthModule,
ThemeModule.forRoot(),
CoreModule
],
bootstrap: [AppComponent]
})
export class AppModule {}
Interceptor
#Injectable()
export class JwtInterceptor implements HttpInterceptor {
private isRefreshing = false;
private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
constructor(private injector: Injector,
private localstorage: LocalStorageService,
private authService: AuthService,
private store: Store<AppState>, ) { }
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
const tokenInfo = this.localstorage.getItem(AUTH_KEY);
if (tokenInfo) {
request = this.addToken(request, tokenInfo.token);
}
console.log('TCL: JwtInterceptor -> request', request);
return next.handle(request).pipe(catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
return this.handle401Error(request, next);
} else {
return throwError(error);
}
}));
}
private addToken(request: HttpRequest<any>, token: string): HttpRequest<any> {
return request.clone({
setHeaders: {
'Authorization': `Bearer ${token}`
}
});
}
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
const tokenInfo = this.localstorage.getItem(AUTH_KEY);
return this.authService.authRefreshtokenPost({ refreshToken: tokenInfo.refreshToken }).pipe(
switchMap((tokenRes: any) => {
this.isRefreshing = false;
this.refreshTokenSubject.next(tokenRes.token);
this.localstorage.setItem(AUTH_KEY,
{ token: tokenRes.token, refreshToken: tokenRes.refreshToken, isAuthenticated: true });
return next.handle(this.addToken(request, tokenRes.token));
}), catchError((error) => {
console.log('TCL: JwtInterceptor -> privatehandle401Error -> error', error);
this.store.dispatch(new AuthActions.Logout({ refreshToken: tokenInfo.refreshToken }));
return next.handle(request);
}));
} else {
return this.refreshTokenSubject.pipe(
filter(token => token != null),
take(1),
switchMap(jwt => {
return next.handle(this.addToken(request, jwt));
}));
}
}
}
Lazy load module
#NgModule({
imports: [
ThemeModule,
PetsRoutingModule,
StoreModule.forFeature("pets", petsReducer),
TranslateModule.forChild({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
},
isolate: true
}),
EffectsModule.forFeature([PetsEffects]),
InfiniteScrollModule,
SharedModule
],
declarations: [
PetsComponent,
CreatePetComponent,
ViewPetComponent,
FormPetComponent,
EditPetComponent,
PetListVirtualComponent
],
providers: []
})
export class PetsModule {}
If I export my 2 interceptors in petsModule its intercept the request but I want to import it once only in core module. I checkout this stackblitz https://stackblitz.com/edit/angular-http-interceptor-working-for-lazy-loaded-module?file=src/app/core/token-interceptor.service.ts and I ensure that I only import HttpClientModule in my core module.
For use interceptor globally and the providers are in core module should add #Injectable({
providedIn: 'root'
}) in the top of interceptors like here
https://stackblitz.com/edit/angular-http-interceptor-working-for-lazy-loaded-module?file=src/app/core/token-interceptor.service.ts
Related
I have an angular application where I want to use authguard to protect some routes but for some reason it does not work. I first make a post request to my spring boot backend, if that gives me success I set a value to true and check that value in my canActivate methode. But it does not work, it does not even go into that method. I thought it is called automatically if I set it in my path in app-route.module. How can I use it correctly?
Authenticationservice with authguard together:
export class AuthenticationService {
constructor(
private http: HttpClient,
private router: Router,
) { }
authenticateUser(login: LoginModel){
return this.http.post(environment.rootUrl + 'authenticate', {
username: login.username,
password: login.password,
}).subscribe({
next: (data) => {
localStorage.setItem('token', data.toString())
}, error: (error) => {
this.isAuthenticated = false
}
})
}
isUserLoggedIn(){
return !!localStorage.getItem('token')
}
}
Authguard:
#Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(
private auth: AuthenticationService,
private router: Router
) {
}
canActivate(): Promise<boolean> {
return new Promise(resolve => {
if (this.auth.isUserLoggedIn()) {
resolve(true)
} else {
this.router.navigate(['authenticate'])
resolve(false)
}
})
}
}
App-module:
#NgModule({
declarations: [AppComponent, NxWelcomeComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
NbLayoutModule,
LoginComponentModule,
AppRoutingModule,
NbThemeModule.forRoot({ name: 'default' }),
NbLayoutModule,
NbEvaIconsModule,
],
providers: [AuthGuard],
bootstrap: [AppComponent],
})
export class AppModule {}
App-routing-module:
const routes: Routes = [
{path: 'dashboard' , component: DashboardComponent, canActivate: [AuthGuard]},
{path: 'authenticate', component: LoginComponent},
{path: '' , redirectTo: 'authenticate', pathMatch: 'full'}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: [AuthGuard]
})
export class AppRoutingModule {
}
You need to route to the dashboard after you do the login:
The routes:
const routes: Routes = [
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [AuthGuard],
},
{ path: '', redirectTo: 'authenticate', pathMatch: 'full' },
];
The Component:
export class AppComponent {
constructor(
private authService: AuthenticationService,
private router: Router
) {}
callServiceMethod(value: string) {
// This will need to wait for auth to finish before navigating in the actual app.
this.authService.authenticateUser(value);
if (this.authService.isAuthenticated) {
this.router.navigate(['dashboard']);
}
}
}
The Guard:
#Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(private auth: AuthenticationService, private router: Router) {}
canActivate(): Promise<boolean> {
return new Promise((resolve) => {
console.log('running auth guard!');
if (this.auth.isUserLoggedIn()) {
resolve(true);
} else {
this.router.navigate(['authenticate']);
resolve(false);
}
});
}
}
Here is an updated example:
https://stackblitz.com/edit/angular-ivy-5excpc?file=src%2Fapp%2Fapp.component.ts
Your canActivate should only take your AuthGuard class which has the resolver.
{path: 'dashboard' , component: DashboardComponent, canActivate: [AuthGuard]}
I am getting this error while while doing npm run serve:ssr
my app.module.ts looks like this:
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'serverApp' }),
HttpClientModule,
AppRoutingModule,
],
providers: [ProductService,
{provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true}
],
bootstrap: [AppComponent]
})
export class AppModule { }
My home.component.ts looks like this:
constructor(private productService: ProductService) { }
ngOnInit(): void {
this.productService.getHomeData().subscribe(
(data) => {
console.log(data);
},
(error) => {
console.log(error);
}
);
}
my product.service looks like this:
#Injectable()
export class ProductService {
getHomeData(): Observable<any> {
return "this.data";
}
}
Kindly help me fix this issue
ERROR TypeError: this.productService.getHomeData is not a function
at HomeComponent.ngOnInit
i have this config for my site :
import { InjectionToken } from '#angular/core';
import { HttpHeaders } from '#angular/common/http';
export let APP_CONFIG = new InjectionToken<IAppConfig>('app.config');
export interface IAppConfig {
apiEndpoint: string;
headersOptions: HttpHeaders;
}
export const AppConfig: IAppConfig = {
apiEndpoint: 'https://localhost:44354/',
headersOptions : new HttpHeaders({ 'Content-Type': 'application/json' }),
};
and i need use the core.module for using the config :
#NgModule({
declarations: [],
imports: [
CommonModule,
ToastrModule.forRoot()
],
exports: [],
providers: [
{
provide: APP_CONFIG,
useValue: AppConfig,
multi: true
},
{
provide: HTTP_INTERCEPTORS,
useClass: RequestInterceptor, multi: true
}
],
})
export class CoreModule {
constructor( #Optional() #SkipSelf() core: CoreModule) {
if (core) {
throw new Error("CoreModule should be imported ONLY in AppModule.");
}
}
}
i using the core.module in app.module .
this my app.module :
#NgModule({
declarations: [
AppComponent,
TopHeaderComponent
],
imports: [
BrowserModule,
HttpClientModule,
SharedModule,
ReactiveFormsModule,
AppRoutingModule,
ToastrModule.forRoot(),
BrowserAnimationsModule,
FormsModule,
CoreModule,
NgZorroAntdModule
],
providers:[{ provide: NZ_I18N, useValue: en_US }],
bootstrap: [AppComponent]
})
export class AppModule { }
now i need to use the AppConfig in my service :
constructor(httpClient: HttpClient, #Inject(APP_CONFIG) private appConfig: IAppConfig) {
super(httpClient);
}
but it show me this error :
Uncaught (in promise): NullInjectorError: StaticInjectorError(AppModule)[[object Object]]:
whats the problem ? how can i solve this problem???
Modify your core module like the following (Remove multi: true)
#NgModule({
declarations: [],
imports: [
CommonModule,
ToastrModule.forRoot()
],
exports: [],
providers: [
{
provide: APP_CONFIG,
useValue: AppConfig //multi: true -> Remove here
},
{
provide: HTTP_INTERCEPTORS,
useClass: RequestInterceptor,
multi: true
}
],
})
export class CoreModule {
constructor( #Optional() #SkipSelf() core: CoreModule) {
if (core) {
throw new Error("CoreModule should be imported ONLY in AppModule.");
}
}
}
Then you can inject in your service as below:
#Injectable()
export class MainService {
constructor(#Inject(APP_CONFIG) private appConfig: IAppConfig) { }
getEndPoint(): string {
alert(this.appConfig.apiEndpoint);
return this.appConfig.apiEndpoint;
}
}
Stackblitz Here
I created a Directive in Angular7 but when i need to use that it show me this error :
Error: StaticInjectorError(AppModule)[NgForOf -> TemplateRef]:
StaticInjectorError(Platform: core)[NgForOf -> TemplateRef]:
NullInjectorError: No provider for TemplateRef!
Error: StaticInjectorError(AppModule)[NgForOf -> TemplateRef]:
StaticInjectorError(Platform: core)[NgForOf -> TemplateRef]:
I create this directive in SharedModule :
#NgModule({
declarations: [FilderrorComponent, UploadfileComponent, ImageComponent, ValidatePermissionDirective],
imports: [
CommonModule
],
exports: [ FilderrorComponent, UploadfileComponent, ImageComponent, ValidatePermissionDirective]
})
export class SharedModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: SharedModule,
providers: [ FilderrorComponent, UploadfileComponent, ImageComponent , ValidatePermissionDirective]
};
}
}
and this is my Directive:
#Directive({
selector: '[appValidatePermission]'
})
export class ValidatePermissionDirective implements OnInit {
show: boolean;
constructor(private templateRef: TemplateRef<any>,
private viewContainerRef: ViewContainerRef
, private dynamic: DynamicPermissionService) { }
// tslint:disable-next-line:no-input-rename
#Input() AccessName: string;
ngOnInit() {
this.ValidatePemission();
if (this.show) {
this.viewContainerRef.createEmbeddedView(this.templateRef);
} else {
this.viewContainerRef.clear();
}
}
ValidatePemission() {
console.log('AccessName : ', this.AccessName);
const find = this.dynamic.dynamicModel.find(x =>
!! x.actionsVM.find(z => z.actionEnglishName === this.AccessName));
console.log(find);
if (find) {
console.log(true);
this.show = true;
} else {
console.log(false);
this.show = false;
}
}
}
and i define the shareModule in AdminModule :
#NgModule({
declarations: [],
imports: [
SharedModule,
AdminpanelRoutingModule,
],
providers: [Toolkit, { provide: HTTP_INTERCEPTORS, useClass: RequestInterceptor, multi: true }]
})
export class AdminpanelModule { }
and I use Directive in HTML :
<span [appValidatePermission]="CreateRole">
<router-outlet></router-outlet>
</span>
Whats The Problem??? How Can I Solve That?
I think the problem comes from the fact that templateRef is null.
Try intializing it before using it maybe
I have this in a service- I18nProviderService:
createTranslationProviders(response) {
const locale = 'de';
let translationProvider: any;
translationProvider = [{
provide: TRANSLATIONS,
useValue: response
},
{
provide: TRANSLATIONS_FORMAT,
useValue: 'xlf'
},
{
provide: LOCALE_ID,
useValue: locale
}
];
return translationProvider;
}
getTranslationProvider() {
this.getTranslationFile()
.subscribe(
(response: Response) => this.createTranslationProviders(response),
(error) => console.log(error)
);
}
and this in my app.module.ts
export function startupServiceFactory(myService: I18nProviderService): Function {
return () => myService.getTranslationProvider();
}
#NgModule({
declarations: [
],
imports: [
BrowserModule,
ReactiveFormsModule,
HttpModule,
NguiAutoCompleteModule
],
providers: [I18nProviderService,
{
provide: APP_INITIALIZER,
useFactory: (myService: I18nProviderService) => () => myService.getTranslationProvider(),
deps: [I18nProviderService],
multi: true
},
],
bootstrap: [AppComponent]
})
export class AppModule {}
Why is the TRANSLATIONS_FORMAT, LOCALE_ID and TRANSLATIONS not mapped correctly. Means, if I trace the LOCALE_ID in my app.component.ts it has the default value.. not "de". I like to load my xlf translation file before app is init.