1

I want to access my angular service from export function.

I read all the related questions form SO, however for some reason the solutions doesn't work for me.

I added the app-injector.ts helper. It looks like this:

import {Injector} from '@angular/core';

/**
 * Allows for retrieving singletons using `AppInjector.get(MyService)` (whereas
 * `ReflectiveInjector.resolveAndCreate(MyService)` would create a new instance
 * of the service).
 */
export let AppInjector: Injector;

/**
 * Helper to set the exported {@link AppInjector}, needed as ES6 modules export
 * immutable bindings (see http://2ality.com/2015/07/es6-module-exports.html) for
 * which trying to make changes after using `import {AppInjector}` would throw:
 * "TS2539: Cannot assign to 'AppInjector' because it is not a variable".
 */
export function setAppInjector(injector: Injector) {
    if (AppInjector) {
        // Should not happen
        console.error('Programming error: AppInjector was already set');
    } else {
        AppInjector = injector;
    }
}

I added the injector to the AppModule constructor and called the SetAppInector from its body like this:

export class AppModule {
    constructor(injector: Injector) {
        setAppInjector(injector);
    }
}

And, I use the AppInjector to inject the service in my getTranslatedPaginatorIntl() function that is declared in my AppModule:

export function getTranslatedPaginatorIntl() {
    const translate = AppInjector.get(TranslateService);
    const paginatorIntl = new MatPaginatorIntl();

    paginatorIntl.itemsPerPageLabel = translate.instant('Items per page');
    paginatorIntl.nextPageLabel = translate.instant('Next page');
    paginatorIntl.previousPageLabel = translate.instant('Previous page');
    // paginatorIntl.getRangeLabel = serbianRangeLabel;

    return paginatorIntl;
}

The function gets called in the providers part of the @NgModule declaration (I guess that's where the problem lies, because the constructor is probably called after this so the Injector doesn't get injected in time):

providers: [
        ApiService,
        LoaderService,
        { provide: MatPaginatorIntl, useValue: getTranslatedPaginatorIntl() },
        { provide: LocationStrategy, useClass: HashLocationStrategy },
        { provide: MAT_DIALOG_DATA, useValue: {} },
        { provide: MatDialogRef, useValue: {} },
    ]
1
  • Why do you use useValue instead of useFactory? Pretty sure that would solve your problem. Just inject your service into that factory function and you should be good to go without these ugly injector hacks. Commented Dec 7, 2020 at 16:30

1 Answer 1

2

You don't need to hack your way around the injector - Angular has a supported API for your use-case. Instead of { provide: Service, useValue: someFunc() } you can tell angular you want to use a factory function with optional dependencies { provide: Service, useFactory: myFactoryFunc, deps: [MyDepService] }.

In your case it would look something like this:

// The factory function (now with transateService as parameter!)
export const getTranslatedPaginatorIntl = (translateService:TranslateService) => {
    const paginatorIntl = new MatPaginatorIntl();

    paginatorIntl.itemsPerPageLabel = translate.instant('Items per page');
    paginatorIntl.nextPageLabel = translate.instant('Next page');
    paginatorIntl.previousPageLabel = translate.instant('Previous page');

    return paginatorIntl;
}

And in your app.module:

providers: [
    ApiService,
    LoaderService,
    { provide: LocationStrategy, useClass: HashLocationStrategy },
    { provide: MAT_DIALOG_DATA, useValue: {} },
    { provide: MatDialogRef, useValue: {} },
    { 
        provide: MatPaginatorIntl, 
        // The factory function (don't call the function here, angular will do that for you! 
        useFactory: getTranslatedPaginatorIntl, 
        // Provide your dependencies, they will become parameters for the function when angular calls it
        deps: [TranslateService] 
    },
]

For further explanation and details, I suggest the official angular documentation.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.