Home:ALL Converter>Angular 2: Instantiate a service outside a component with ngx-translate injected in the constructor

Angular 2: Instantiate a service outside a component with ngx-translate injected in the constructor

Ask Time:2017-06-19T18:17:44         Author:berno

Json Formatter

I want to set a variable called bootLang before defining routes to redirect to the correct language instead of a static one. I created a service which computes and returns the language in the local storage (if it's set) or the browser language. To do that i need the TranslateService from the ngx-translate module.

compute-lang-bootstrap.service.ts

import {Injectable} from '@angular/core';
import {TranslateService} from "@ngx-translate/core";

@Injectable()
export class ComputeLangBootstrapService {

  lang_computed: string|null;

  constructor(private translate: TranslateService){}

  getLang(): string {
    this.lang_computed = localStorage.getItem('lang-selected');
    if(!this.lang_computed){
      let browserLang = this.translate.getBrowserLang();
      this.lang_computed = browserLang.match(/en|es|it/) ? browserLang : 'en'
    }
    return this.lang_computed;
  }

}

I need to instantiate this service before defining routes in order to compute the language that i have to place in the redirect route definition.

app-routing.module.ts

import { FooHomeComponent } from "./shore-home/shore-home.component";
import { FooDetailsComponent } from "./tour-details/tour-details.component";
import { ComputeLangBootstrapService } from "./services/compute-lang-bootstrap.service";



let injector = ReflectiveInjector.resolveAndCreate([ComputeLangBootstrapService]);
let bootLang = injector.get(ComputeLangBootstrapService).getLang();

const appRoutes: Routes = [
  { path: ':lang/tour-details/:id', component: FooDetailsComponent },
  { path: ':lang', component: FooHomeComponent },
  { path: '', redirectTo: bootLang, pathMatch: 'full' }
];

@NgModule({
  imports: [
    RouterModule.forRoot(appRoutes),
    CommonModule
  ],
  exports: [
    RouterModule
  ],
  declarations: []
})

export class AppRoutingModule {}

To do that I tried to use a factory that I specified in the AppModule.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpModule, Http } from '@angular/http';
import { TranslateModule, TranslateLoader, TranslateService} from '@ngx-translate/core';
import { TranslateHttpLoader } from "@ngx-translate/http-loader";

import { AppComponent } from './app.component';
import { FooHomeComponent } from './shore-home/shore-home.component';
import { AppRoutingModule } from "./app-routing.module";
import { FooDetailsComponent } from './tour-details/tour-details.component';
import { ComputeLangBootstrapService } from "./services/compute-lang-bootstrap.service";

// AoT requires an exported function for factories
export function HttpLoaderFactory(http: Http) {
  return new TranslateHttpLoader(http);
}

export function CLBFactory(translate: TranslateService) {
  return new ComputeLangBootstrapService(translate);
}

@NgModule({
  declarations: [
    AppComponent,
    FooHomeComponent,     
    FooDetailsComponent
  ],
  imports: [
    AppRoutingModule,
    HttpModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: HttpLoaderFactory,
        deps: [Http]
      }
    })
  ],
  providers: [
    {
      provide: ComputeLangBootstrapService,
      useFactory: CLBFactory,
      deps: [TranslateService]
    }
  ],
  bootstrap: [AppComponent]
})

export class AppModule {}

In this way i'm getting this error.

Uncaught Error: No provider for TranslateService! (ComputeLangBootstrapService -> TranslateService)

Indeed the TranslateService can be used like a service but it's not declared with the @injectable() decorator. I tried many other workarounds to reach my goal like using injector in the ComputeLangBootstrapService to instantiate the TranslateService outside the constructor or creating a Guard but it doesn't get checked in the redirect route because the redirecting happens before the check of the Guard. Someone has an idea of what is the best practice to reach my goal?

Author:berno,eproduced under the CC 4.0 BY-SA copyright license with a link to the original source and this disclaimer.
Link to original article:https://stackoverflow.com/questions/44627897/angular-2-instantiate-a-service-outside-a-component-with-ngx-translate-injected
yy