Unable to Translate Keys using Ng2-Translate in Http Interceptors and Home Component Angular 2 , Angular 6

999 views Asked by At

My Code is like below, I am using 3rd party login (openAM) for my application. when ever I got 401, I should redirect the application to another URL from there user will be logged in.

I user http interceptors to check the status and redirect to login. Question1: Is this approach is correct ?

Question2: Translations are not happening.

check handleHttpResponseCodes method. In that "unauthorisedMessage" is not translating. Not only that any message is not translating. is anything wrong with my code.


import { Injectable, Injector } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import { AuthService } from 'app/blocks/auth/auth.service';
import { TranslateService } from 'ng2-translate';
import { AlertService } from '../../core/services/common';
import { Router } from '@angular/router';
import * as jsToaster from 'toastr';

@Injectable()
export class AppHttpInterceptor implements HttpInterceptor {

    constructor(
        private injector: Injector,
        private router: Router,
        private translate: TranslateService
    ) {}
    intercept(req: HttpRequest < any > , next: HttpHandler): Observable < HttpEvent < any >> {

        // console.log(req);
        if (!window.navigator.onLine) {
            // check to see if there's internet
            // if there is no internet, throw a HttpErrorResponse error
            // since an error is thrown, the function will terminate here
            return Observable.throw(new HttpErrorResponse({
                error: 'NO_INTERNET'
            }));
        } else {
            // else return the normal request
            return this.handleResponse(next, req);
        }
    }
    handleResponse(next, req) {
        return next.handle(req)
            .do((ev: HttpEvent < any > ) => {
                if (ev instanceof HttpResponse) {
                    //console.log('ev in the do: ', ev);
                }
            })
            .catch((response: any) => {
                if (response instanceof HttpErrorResponse) {
                    this.handleHttpResponseCodes(response);
                } else {
                    console.log('::UNKNOWN ERROR::', response);
                }
                return Observable.throw(response);
            });
    }
    handleHttpResponseCodes(exception) {
        let authService = this.injector.get(AuthService);
        let translate = this.injector.get(TranslateService);
        let alertService = this.injector.get(AlertService);
        const router = this.injector.get(Router);


        switch (exception.status) {
            case 401: //UNAUTHORIZED
                debugger;
                this.translate.get('unauthorisedMessage').subscribe((res: string) => {
                    debugger;
                   **//ISSUE: TRANSLATION NOT HAPPENING WHY? //**
                    this.showToast(res, '401'); 
                });

                setTimeout(() => {
                    authService.login_openAM();
                }, 3000);

                break;
            case 400: //BAD REQUEST OR FR SESSION TIME OUT

                this.translate.get('loginSessionExpiredMessage').subscribe((res: string) => {
                    debugger;
                    this.showToast(res, '400');
                });

                setTimeout(() => {
                    authService.loginSessionExpired();
                }, 2000);

                break;
            case 403: // FORBIDDED ACCESS
                //TODO
                break;
            case 502: // BAD GATEWAY

                break;
            case 503: //SERVICE UNAVAILABLE
                jsToaster.error("SERVICE UNAVAILABLE", "503");
                break;
            case 504: //GATEWAY TIMEOUT
                jsToaster.error("SERVICE UNAVAILABLE", "CODE:504-GATEWAY TIMEOUT");
                break;
        }
        console.log(':Service Exception::', exception);
    }

    showToast(title, body, typeOfToast, waitTime) {
        setTimeout(() => {
            jsToaster[typeOfToast](body, title);
        }, waitTime);
    }

}

AppLoadService

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import 'rxjs/add/operator/toPromise';

import { APP_SETTINGS } from 'app/app-settings/settings';
import { AlertService } from '../core/services/common';

@Injectable()
export class AppLoadService {

    constructor(
        private httpClient: HttpClient,
        private alertService: AlertService
    ) { }


    load(): Promise<any> {
        console.log('@started step1');
        return new Promise(resolve => {
            const promise = this.httpClient.get('assets/settings/config.json') // CONTAINS APPLICATION LOGIN AND LOGOUT URL PATHS
                .subscribe(
                    res_config => {
                        console.log('@config json loaded step2');
                        APP_SETTINGS.app_login_url = res_config["app_login_url"] || res_config["app_LOGIN_URL"];
                        APP_SETTINGS.app_logout_url = res_config["app_logout_url"] || res_config["app_LOGOUT_URL"];
                        console.log("::initializeApp:config file loaded");
                        /*=========== AUTH API - START ===========*/
                        this.httpClient.get('/XXXX/v2/authentication-api') 
                            .subscribe(
                                res_auth_context => {
                                    this.storeUserContext(res_auth_context);
                                    console.log('@auth context loaded step3');

                                    /*=========== LOAD PRODUCTS ===========*/
                                    this.httpClient.get('/XXXX/v1/api/lab')
                                        .subscribe(
                                            res_products => {
                                                console.log('@auth context loaded step4');
                                                APP_SETTINGS.isproductsLoaded = true;
                                                APP_SETTINGS.appproducts = res_products;
                                                console.log("::initializeApp:: products Loaded");
                                                resolve();
                                            },
                                            res_products_error => {
                                                console.log("::initializeApp:: products NOT Loaded", res_products_error);
                                                resolve();
                                            }
                                        );
                                    /*=========== LOAD products ===========*/
                                },
                                res_auth_context_error => {
                                    console.log("Auth Call Failed");
                                }
                            );
                        /*=========== AUTH CONTEXT CALL API - END ===========*/

                    },
                    res_config_Error => {
                        console.log("::initializeApp:config file NOT  loaded", res_config_Error);
                    }
                );
        });
    }

    storeUserContext(usetContext: any): any {
        sessionStorage.setItem("AUTH_TOKEN", JSON.stringify(usetContext));
    }

}

APP MODULE

export function createTranslateLoader(http: Http) {
    return new TranslateStaticLoader(http, './assets/i18n', '.json');
}


@NgModule({
    declarations: [
        AppComponent, CustomDateRangePickerComponent
    ],
    imports: [
        // :CORE MODULE: //
        BrowserModule,
        BrowserAnimationsModule,
        HttpClientModule,
        TranslateModule.forRoot(
            {
                provide: TranslateLoader,
                useFactory: (createTranslateLoader),
                deps: [Http]
            }),
        FormsModule,
        CommonModule, //<====added

        //:3RD PARTY MODULE://
        BootstrapModalModule,

        //:APPLICTION MODULES: //
        AppLoadModule, //Startupdata before APP loaded
        AppRoutingModule,
        FooterModule,
        ErrorModule,
        AccessDeniedModule,
        NotFoundModule,
        RouterModule.forRoot([]),
        ToasterModule.forChild(),
    ],
    providers: [
        LoaderService,
        ToasterService,
        StartupService,
        ResponseStatusesService,
        LocalStorageService,
        ApplicationSettingsService,
        LabSelectionService,
        AccountService,
        AuthService,
        AlertService,
        AuthGuard,
        RolesGuard,
        FeaturebasedGuard,
        ErrorLogService,
        {
            provide: ErrorHandler,
            useClass: GlobalErrorsHandler
        },
        {
            provide: HTTP_INTERCEPTORS,
            useClass: AppHttpInterceptor,
            multi: true
        },
        {
            provide: LocationStrategy,
            useClass: HashLocationStrategy
        },
    ],
    exports: [],
    bootstrap: [AppComponent]
})

export class AppModule { }

Home Component

export class HomeComponent implements OnInit {
  constructor(private loaderService: LoaderService, private translate: TranslateService,
    private router: Router,
    private AuthService: AuthService,
    private AlertService: AlertService,
  ) { }

  ngOnInit() {   
    if (this.AuthService.isUserLoggedIn()) {
      let userDetails = this.AuthService.getUserDetails();
      if (userDetails) {
        let redirect_path = this.routeResolver(userDetails);
        //=>ROUTE TO REDIRECT BASED ON LOGGED IN USER ROLE<=//
        this.router.navigate([redirect_path]);
      } else {
        this.AlertService.error("invalidUserDetailsRecieved", "home comp");
      }
    }
  }

Alert Service (I am using angular2 toaster)

import { Injectable } from "@angular/core";
import { Fault } from "../../models/fault";
import { ToasterService, Toast } from "angular2-toaster";
import { TranslateService, LangChangeEvent } from "ng2-translate";

@Injectable()
export class AlertService {
  constructor(
    private toastyService: ToasterService,
    private translate: TranslateService,
  ) { }

  success(title: string, message: string): void {
    this.translate.get([title, message]).subscribe(res => {
      this.showToast(res[title], res[message], "success");
    });
  }

  info(title: string, message: string): void {
    this.translate.get([title, message]).subscribe(res => {
      this.showToast(res[title], res[message], "info");
    });
  }

  warning(title: string, message: string): void {
    let toastBlock = () => {
      this.translate.get([title, message]).subscribe(res => {
        this.showToast(res[title], res[message], "warning");
      });
    }
    this.triggerToastWithMessage(toastBlock);
  }

  error(title: string, message: string): void {
    let toastBlock = () => {
      this.translate.get([title, message]).subscribe(res => {
        this.showToast(res[title], res[message], "error");
      });
    }
    this.triggerToastWithMessage(toastBlock);
  }

  fault(fault: Fault): void {
    this.showToast("", fault.message, "error");
  }

  showToast(title, body, type) {
    var toast: Toast = <Toast>{
      title: title,
      body: body,
      type: type
    };
    this.toastyService.pop(toast);
  }

  triggerToastWithMessage(fn) {
    setTimeout(() => {
      fn();
    }, 500);
  }
}

AppRoutingModule

const routes: Routes = [
    {
        path: '',
        children: [
            { path: '', loadChildren: './home/home.module#HomeModule' },
            { path: 'error', loadChildren: './core/error/error.module#ErrorModule' },
            { path: 'accessDenied', loadChildren: './accessDenied/access-denied.module#AccessDeniedModule' },
            { path: 'notfound', loadChildren: './notFound/not-found.module#NotFoundModule' },
            { path: '**', redirectTo: '/notfound' }
        ]
    }

];

@NgModule({
    declarations: [],
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
})
export class AppRoutingModule { }

HomeRoutingModule

const routes: Routes = [
  {
    path: '',
    canActivate: [AuthGuard],
    component: HomeComponent,
    children: [
      {
        path: 'job-search',
        loadChildren: '../job-search/job-search.module#JobSearchModule',
        canActivate: [RolesGuard],
        data: {
          roles: permitted_roles.JOB_SEARCH,
          router_name: routerNames.JOB_SEARCH,
        }
      },
      
      -------------
      -------------

AuthGaurd

@Injectable()
export class AuthGuard implements CanActivate {


  constructor(
    private router: Router,
    private AuthService: AuthService,
    private AlertService: AlertService,
    private ResponseStatusesService: ResponseStatusesService
  ) {
    console.log('AuthGuard Constructor Executed');
  }


  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    console.log("Authgaurd can activate");
    if (this.AuthService.isUserLoggedIn()) {
      debugger;
      return true;
    }
    return false;
  }
}

Auth Service

public isUserLoggedIn(): boolean {
    const token = sessionStorage.getItem("AUTH_TOKEN");
    if (token) {
      return true;
    } else {
      return false;
    }
  }

3

There are 3 answers

2
rguerin On

Also, try explicitly using the locale code :

this.translate.use('en-EN');
0
Heehaaw On

Make sure you set up the translate service and provide the interceptor correctly. This is what works for me:

// app.module.ts

@NgModule({
  providers: [{ 
    provide: HTTP_INTERCEPTORS, 
    useClass: AppHttpInterceptor, 
    multi: true, 
    deps: [ AuthService, TranslateService, AlertService, Router ] 
  }],
  // ...
})
export class AppModule {

  constructor(translateService: TranslateService, localStorage: LocalStorageService) {

    translateService.addLangs(SUPPORTED_LANGS);
    translateService.setDefaultLang(DEFAULT_LANG);

    // Use either the lang stored in browser local storage... 
    const useLang: string = localStorage.getLang()
      // ...the browser language if supported...
      || SUPPORTED_LANGS.find(lang => lang === translate.getBrowserLang())
      // ...or the default language
      || DEFAULT_LANG;

    translate.use(useLang)
      .pipe(takeWhile(() => this.componentActive), switchMap(() => translate.onLangChange))
      // Update the local storage on language change
      .subscribe((e: TranslationChangeEvent) => localStorage.setLang(e.lang));
  }
  // ...
}
// app-http.interceptor.ts

export class AppHttpInterceptor implements HttpInterceptor {

  constructor(private authService: AuthService,
              private translateService: TranslateService,
              private alertService: AlertService,
              private router: Router) {
    // ...
  }
  // ...
}

Hope this helps a little :-)

4
un.spike On

Basically, you were on the right way. But interceptor should be like that:

import { Injectable } from '@angular/core';
import { HttpHandler, HttpEvent, HttpRequest, HttpErrorResponse, HttpInterceptor } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from "rxjs/operators";

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      catchError((err: HttpErrorResponse, caught: Observable<any>) => {
        switch(err.status){
         case 404:
          return throwError('Errors.404');
        default:
          return throwError('Errors.500')
        }
      })
    );
  }
}

On this implementation, you will just throw back the localization token of error.

On subscription you should handle such errors:

.subscribe((result: any) => {
    ... success result here ...
}, error => {
  this.error = error;  <--- this what you need
});

And on HTML template just use translate pipe for localization:

<div>{{error|translate}}</div>

Here is a working sample