Angular 5 Provide http interceptor based on environment - javascript

I have the following two environments in my angular-cli (v1.5.1, angular v5) application:
dev
prod
Dev makes use of mock data, which I provide with an http-interceptor.
Pro makes use of a live rest api.
How do I provide the http-interceptor on dev, but not on pro?
I already tried the following, but it doesn't work:
{
provide: HTTP_INTERCEPTORS,
useFactory: () => {
if (environment.useMockBackend === true) {
return MockHttpInterceptor;
}
return false;
},
multi: true
}

In my Angular 5.2 project I used following approach.
app.module.ts
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http';
import { environment } from '../environments/environment';
import { MyInterceptor } from './my.interceptor';
const commonProviders = [/*...*/];
const nonProductionProviders = [{
provide: HTTP_INTERCEPTORS,
useClass: MyInterceptor,
multi: true
}];
#NgModule({
imports: [
HttpClientModule,
// ...
],
providers: [
...commonProviders,
...!environment.production ? nonProductionProviders : []
]
})
my.interceptor.ts
import { Injectable } from '#angular/core';
import { HttpEvent, HttpRequest, HttpInterceptor, HttpHandler } from '#angular/common/http';
import { Observable } from 'rxjs';
#Injectable()
export class MyInterceptor implements HttpInterceptor {
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
// ...
return next.handle(req);
}
}

I've come up with the following approach (this is in Angular 7), by drawing on the previous answers from #dhilt and #kemsky:
Your dev environment file
import { HTTP_INTERCEPTORS } from '#angular/common/http';
import { MyDevInterceptor} from './my-dev.interceptor';
export const ENVIRONMENT_SPECIFIC_PROVIDERS = [
{ provide: HTTP_INTERCEPTORS, useClass: MyDevInterceptor, multi: true }
];
environment.prod.ts
export const ENVIRONMENT_SPECIFIC_PROVIDERS = [];
app.module.ts
#NgModule({
declarations: [],
imports: [
HttpClientModule
],
providers: [
ENVIRONMENT_SPECIFIC_PROVIDERS
]
})
It's simple, it works a treat, and it means that your code base contains no references to anything that's not required by your environment.

The idea is to export interceptor providers from environment file, prod environment exports do-nothing interceptor or just any other dummy provider (lets name it DefaultHttpInterceptor) and dev exports MockHttpInterceptor.
dev environment: export const INTERCEPTORS = {provide: HTTP_INTERCEPTORS, ... MockHttpInterceptor}
prod environment: export const INTERCEPTORS = {provide: HTTP_INTERCEPTORS, ... DefaultHttpInterceptor}
Then you can use it like usual:
import { INTERCEPTORS } from './../environments/environment';
#NgModule({
providers : [
...
INTERCEPTORS
...
]
...
})

Related

What makes Keycloak Angular throw an error in this Angular 14 app?

I am working on an app in Angular 14 that requires authentication/authorization, reason for witch I use Keycloak Angular
.
As per the instructions, I have first installed Keycloak Angular with:
npm install keycloak-angular keycloak-js
Then in fe\src\app\app.module.ts:
import { APP_INITIALIZER, NgModule } from '#angular/core';
import { KeycloakAngularModule, KeycloakService } from 'keycloak-angular';
import { BrowserModule } from '#angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
import { MaterialModule } from './shared/material.module';
import { FlexLayoutModule } from '#angular/flex-layout';
// Components
import { ErrorsComponent } from './shared/error.component';
import { AppComponent } from './app.component';
function initializeKeycloak(keycloak: KeycloakService) {
return () =>
keycloak.init({
config: {
url: 'http://localhost:8080/auth',
realm: 'demo',
clientId: 'my-app'
},
initOptions: {
onLoad: 'check-sso',
silentCheckSsoRedirectUri:
window.location.origin + '/assets/silent-check-sso.html'
}
});
}
#NgModule({
declarations: [
ErrorsComponent,
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
MaterialModule,
FlexLayoutModule,
KeycloakAngularModule
],
exports: [],
providers: [
{
provide: APP_INITIALIZER,
useFactory: initializeKeycloak,
multi: true,
deps: [KeycloakService]
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
I have also added silent-check-sso.html to the src\assets\ directory.
The problem
At http://localhost:4200/, I only see a blank page and the browser throws the error:
Refused to display 'http://localhost:8080/' in a frame because it set 'X-Frame-Options' to 'deny'.
Questions
What is causing this error?
What is the easiest and most reliable way to fix it?
This is due that Keycloak will prevent a website from including any login page within an iframe. This is to prevent clickjacking attacks.
To enable this just fallow this guide from keyclock documentation: https://www.keycloak.org/docs/15.0/server_admin/, just check after "Authorizing Iframes"
And also check if flags: checkLoginIframe is set to false.

How do I Access AppModule Module imports from Lazy-loaded Modules?

How do I Access AppModule imports from Lazy-loaded Modules ?
My Angular10 App imports AngularMaterial and NXTranslate Modules in to the AppModule.
NxTranslate calls an ApiService to get a large Lookup object of thousands of translations.
This is translated at the initial loading of the AppModule.
The App has multiple lazy-loaded routes that also need to use the AnagularMaterial and NXTranslate Modules in their features.
If I use a SharedModule to load the Modules then the ApiService is called multiple times. This is obviously not good.
It should only call the ApiService & AngularMaterial once and be available for all modules.
How do I resolve this? I am struggling.
Thanks.
Update
(sorry for the long post)
This is the NXTranslate implementation - it uses a custom class.
import { environment } from './../../../../environments/environment';
import { OSCITranslateService } from './translate.service';
import { NgModule, Injector } from '#angular/core';
import { CommonModule } from '#angular/common';
import {TranslateLoader, TranslateModule} from '#ngx-translate/core';
import {TranslateHttpLoader} from '#ngx-translate/http-loader';
import {HttpClient, HttpClientModule} from '#angular/common/http';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
export class CustomLoader implements TranslateLoader {
localeResourcesUrl =
`${environment.baseUrl}${environment.apiUrl.localeResources}`;
constructor(private http: HttpClient) {}
getTranslation(lang: string): Observable<any> {
let options;
const uri = `${this.localeResourcesUrl}${options && options.key ?
'/' + options.key : ''}`;
let mapped = this.http.get(uri).pipe(
map((response: any) => {
let localeData = {};
let languageCode = response?.languageVariantCode;
response.resources.forEach(item => {
localeData[item.keyName] = item.keyValue;
});
return localeData;
})
);
return mapped;
}
}
#NgModule({
declarations: [],
imports: [
CommonModule,
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: CustomLoader,
deps: [HttpClient]
}
})
],
exports: [ TranslateModule ]
})
export class NxTranslateModule {
constructor(private http: HttpClient) {
}
}
This is the sharedRootModule that imports the AngularMaterial & NXTranslate
import { SharedModule } from './shared.module';
import { NgModule, ModuleWithProviders } from '#angular/core';
#NgModule({
})
export class SharedRootModule {
static forRoot(): ModuleWithProviders<SharedModule> {
return {
ngModule: SharedModule
};
}
}
In AppModule SharedRootModule is imported
...
#NgModule({
declarations: [
AppComponent
],
imports: [
...
SharedRootModule.forRoot()
],
exports: [
...
SharedRootModule
]
....
Are you concerned about the multiple ApiService instances you might end up with? Provide the ApiService within AppModule only, or even better, use the providedIn property right in your service's decorator so it gets injected at application level. (https://angular.io/api/core/Injectable#providedIn)
I would just use a SharedModule that exports the mentioned lazy loaded modules.

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 4 HttpClient in Component Can't resolve all parameters

I started learning Angular 4 and got to the part with HTTP Client. Right now I'm trying to make an http call from the component (yes, I know I should transfer it to service, but still)
But for some reason, when I try to inject HttpClient into my Component I get the next error:
Uncaught Error: Can't resolve all parameters for PromocodeComponent:
(?).
Here's the code of my component:
import { Ticket } from '../../classes/Ticket.class'
import { Component, Input } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'promocode',
templateUrl: './promocode.template.html',
styleUrls: ['./promocode.styles.scss']
})
export class PromocodeComponent {
#Input() ticket: Ticket;
state: String = "normal";
promocode: String = "";
constructor(private http: HttpClient) {}
promocodeValidate(event): void{
console.log(this.promocode);
console.log(event);
this.http.get('/promocode/asdasda').subscribe(data => {
console.log(data);
});
}
}
And my app.module.ts:
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
import { MovieBadgeComponent } from './movie-badge/movie-badge.component';
import { TicketComponent } from './ticket/ticket.component';
import { PromocodeComponent} from './promocode/promocode.component';
import { FormsModule } from '#angular/forms';
import { HttpClientModule } from '#angular/common/http';
#NgModule({
declarations: [
AppComponent,
MovieBadgeComponent,
TicketComponent,
PromocodeComponent
],
imports: [
BrowserModule,
HttpClientModule,
FormsModule
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule {}
It turned out, for me, that I forgot the #Injectable decorator on my service. In earlier angular (>=2), i remember the #Injectable as being optional... Guess not anymore?
Turns out, I missed
"emitDecoratorMetadata": true
in tsconfig
fml...
I got the same error when I ran unit testing (a spec.ts file) with Angular.
The following is what I did:
import { HttpClientModule, HttpClient } from '#angular/common/http';
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ],
imports: [
HttpClientModule
],
schemas: [NO_ERRORS_SCHEMA],
providers: [
HttpClient,
]
})
.compileComponents();
}));
and the error was gone.

angular2 translate ionic 2 No provider for Http! (MyApp -> TranslateService -> Http)

I am using ionic2/angular2 and ng2-translate. I get an error of "No provider for Http! (MyApp -> TranslateService -> Http)". I am not using typescript. I believe that this code is in typescript form. can someone help me convert it to javascript. Because i am using javascript for my ionic2 project. here is the code from the documentation of ng2-translate. I am just new to ionic2 and angular2.
import {provide} from '#angular/core';
import {TranslateService, TranslateLoader, TranslateStaticLoader} from 'ng2-translate/ng2-translate';
#App({
templateUrl: '....',
config: {},
providers: [
provide(TranslateLoader, {
useFactory: (http: Http) => new TranslateStaticLoader(http, 'assets/i18n', '.json'),
deps: [Http]
}),
TranslateService
]
})
here is my app.js
import {App, IonicApp, Platform, Storage, SqlStorage} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {MainPage} from './pages/main/main';
import {TabsPage} from './pages/jeepney/tabs/tabs';
import {LandingPage} from './pages/landingpage/landingpage';
// import {JeepneyRoutesPage} from './pages/jeepney/jeep-routes/jeep-routes';
// import {ListPage} from './pages/list/list';
import {DataService} from './services/data';
import {ConnectivityService} from './providers/connectivity-service/connectivity-service';
import {GoogleMapsService} from './providers/google-maps-service/google-maps-service';
import {LoadingModal} from './components/loading-modal/loading-modal';
import {provide} from 'angular2/core';
import {Http, HTTP_PROVIDERS} from 'angular2/http';
import {TranslateService, TranslateLoader, TranslateStaticLoader} from 'ng2-translate/ng2-translate';
#App({
templateUrl: 'build/app.html',
providers: [DataService,ConnectivityService,
HTTP_PROVIDERS,
provide(TranslateLoader, {
useFactory: (http: Http) => new TranslateStaticLoader(http, 'assets/i18n', '.json'),
deps: [Http]
}),
TranslateService],
directives: [LoadingModal],
config: {
iconMode: 'md',
modalEnter: 'modal-slide-in',
modalLeave: 'modal-slide-out',
pageTransition: 'ios',
tabSubPages: false,
backButtonIcon: 'ios-arrow-back',
tabbarPlacement: 'top',
backButtonText: ''
// menuType: 'reveal'
} // http://ionicframework.com/docs/v2/api/config/Config/
})
class MyApp {
static get parameters() {
return [[IonicApp], [Platform],[TranslateService]];
}
constructor(app, platform,translate) {
this.translate=translate;
// set up our app
this.app = app;
this.platform = platform;
this.initializeApp();
// make HelloIonicPage the root (or first) page
this.rootPage = LandingPage;
// this.initializeTranslateServiceConfig();
var userLang = navigator.language.split('-')[0]; // use navigator lang if available
userLang = /(fr|en)/gi.test(userLang) ? userLang : 'en';
// this language will be used as a fallback when a translation isn't found in the current language
this.translate.setDefaultLang('en');
// the lang to use, if the lang isn't available, it will use the current loader to get them
this.translate.use(userLang);
}
initializeTranslateServiceConfig() {
var prefix = 'assets/i18n/';
var suffix = '.json';
this.translate.useStaticFilesLoader(prefix, suffix);
var userLang = navigator.language.split('-')[0];
userLang = /(de|en|hr)/gi.test(userLang) ? userLang : 'en';
this.translate.setDefaultLang('en');
this.translate.use(userLang);
}
initializeApp() {
this.platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
StatusBar.styleDefault();
// this.storage = new Storage(SqlStorage);
// this.storage.query('CREATE TABLE IF NOT EXISTS people (id INTEGER PRIMARY KEY AUTOINCREMENT, firstname TEXT, lastname TEXT)').then((data) => {
// console.log("TABLE CREATED -> " + JSON.stringify(data.res));
// }, (error) => {
// console.log("ERROR -> " + JSON.stringify(error.err));
// });
});
}
}
You need to provide HTTP_PROVIDERS:
import {HTTP_PROVIDERS, Http} from '#angular/http';
#App({
templateUrl: '....',
config: {},
providers: [
HTTP_PROVIDERS,
provide(TranslateLoader, {
useFactory: (http: Http) => new TranslateStaticLoader(http, 'assets/i18n', '.json'),
deps: [Http]
}),
TranslateService
]
})
this is how it worked out for me.
check out the app.module.ts file
import { NgModule, ErrorHandler } from '#angular/core';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
import { TranslateModule, TranslateStaticLoader, TranslateLoader } from 'ng2-translate/ng2-translate';
import { Http } from '#angular/http'
export function createTranslateLoader(http: Http) {
return new TranslateStaticLoader(http, 'assets/i18n', '.json');
}
#NgModule({
declarations: [
MyApp,
HomePage
],
imports: [
IonicModule.forRoot(MyApp),
TranslateModule.forRoot({
provide: TranslateLoader,
useFactory: (createTranslateLoader),
deps: [Http]
})
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage
],
providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}]
})
export class AppModule {}
you can find a complete repo here:
https://github.com/philipphalder/ionic2-rc3-NG2-Translate

Categories