How to use i18n on angular component library - javascript

I use i18n on my own component library, but it's not working
in my module
// AoT requires an exported function for factories
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
#NgModule({
declarations: [myLibraryModuleComponent],
imports: [
CommonModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
})
],
exports: [myLibraryModuleComponent, TranslateModule]
})
export class myLibraryModule { }
I got
http://localhost:4200/assets/i18n/en.json
not found. It's not the good path

Are you using aot?
if yes, as per the documentation, you must use an exported function instead of an inline function
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: (createTranslateLoader),
deps: [HttpClient]
}
})
Notice the createTranslateLoader between the parenthesis.
if no, try this:
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http);
}

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) {}
}

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 "Maximum call stack size exceeded" when use injector

I created Angular 5 project and create one factory which should provide metadata from service before application start.
The problem is that I get error:
RangeError: Maximum call stack size exceeded
at resolveNgModuleDep (core.js:10559)
at _callFactory (core.js:10649)
at _createProviderInstance$1 (core.js:10599)
at resolveNgModuleDep (core.js:10581)
at _createClass (core.js:10622)
at _createProviderInstance$1 (core.js:10596)
at resolveNgModuleDep (core.js:10581)
at NgModuleRef_.get (core.js:11806)
at new MetadataService (metadata.service.ts:23)
at _createClass (core.js:10622)
App module:
#NgModule({
declarations: [
AppComponent,
...
],
imports: [
BrowserModule,
HttpClientModule,
AppRoutingModule,
FormsModule,
FlexLayoutModule,
I18NextModule.forRoot()
],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
MetadataService,
{ provide: APP_INITIALIZER, useFactory: MetadataProviderFactory, deps: [MetadataService], multi: true },
{ provide: APP_INITIALIZER, useFactory: I18NextProviderFactory, deps: [I18NEXT_SERVICE], multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }
Metadata service:
import { Injectable, Injector } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { HttpClient } from '#angular/common/http';
#Injectable()
export class MetadataService {
private http: HttpClient;
constructor(private injector: Injector) {
this.http = injector.get(HttpClient); // this row makes a problem
}
This row makes a problem.
this.http = injector.get(HttpClient);
I put this line avoid circular reference and I get "Maximum call stack size exceeded".
Where I am wrong ?
You can temporarily get over this by delaying your http call that you make on service init, with a simple setTimeOut (don't need any seconds). I've gone through a few open Angular threads and the main issue has to do with HttpInterceptor and circular depedenecy with the HttpClient.

Error: No provider for TranslateStore

"Error: No provider for TranslateStore!" while UI Unit testing translation service in Angular 4.
After I run ng test command I get the test cases failed error along with the above error message.
Try this:
import {TranslateFakeLoader,TranslateLoader,TranslateModule,TranslateService } from '#ngx-translate/core';
TestBed.configureTestingModule({
      imports: [
...
        TranslateModule.forRoot({
          loader: {
            provide: TranslateLoader,
            useClass: TranslateFakeLoader
          }
        })
      ],
...
      providers: [
        TranslateService
      ]
Importing & adding TranslateStore to the providers in my lazy loaded module resolved issue for me.
import { TranslateModule,TranslateService,TranslateStore } from '#ngx-translate/core';
export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
#NgModule({
declarations: [...],
imports: [
TranslateModule.forChild(
  {
loader: {
provide: TranslateLoader,
useFactory: (createTranslateLoader),
deps: [HttpClient]
}
})],
providers:[TranslateStore ]
})
Check the issue link below for more information
https://github.com/ngx-translate/core/issues/883#issuecomment-502037966

Typescript and exported function syntax

I was reading ngx-translate documentation and I found this pieces of code (that are explaining the correct way to make the project Aot compatible.
First
export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
#NgModule({
imports: [
BrowserModule,
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: (createTranslateLoader),
deps: [HttpClient]
}
})
],...
Second
// AoT requires an exported function for factories
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http);
}
#NgModule({
imports: [
BrowserModule,
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
})
],...
My question is: what is the difference between enclosing the function name passed for useFactory with parenthesis and passing it plain?
Thanks
I believe you are being slightly mis-led by a quirk in the examples in the documentation.
The non-AOT version could be:
#NgModule({
imports: [
BrowserModule,
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: (http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
},
deps: [HttpClient]
}
})
],...
And the AOT version must have an exported function...
export function yourFunction(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
#NgModule({
imports: [
BrowserModule,
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: yourFunction,
deps: [HttpClient]
}
})
],...
In other words:
If you want to configure a custom TranslateLoader while using AoT compilation or Ionic, you must use an exported function instead of an inline function.

Categories