Make sure the static variable is set up prior to injecting the provider

In our Angular6 application, we utilize a globalcontextServiceFactory to initialize the application before rendering views.

This process involves subscribing to get configuration from a back-end endpoint and then using forkJoin to retrieve environment application data after launch.

An issue arises with a provider that uses a static variable to store the configuration obtained from the subscription. This provider is set up with provideIn: 'root' injector considering hierarchical dependency injectors.

app.module.ts


export function contextServiceFactory(contextService: ContextService): Function {return () => contextService.init();}


@NgModule({
  declarations: [AppComponent],
  imports: [...],
  providers: [...,
    {provide: APP_INITIALIZER, useFactory: contextServiceFactory, deps: [ContextService], multi: true}
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

context.service.ts

@Injectable()
export class ContextService{
  constructor(...){}

init() {
       this.configSubscription = this.getConfig().subscribe((config: Config) => {
      ConfigService.config = config;
        this.globalSubscription = forkJoin(
        this.getDatas1(),
        this.getDatas2(),
        this.getDatas3()
      ).subscribe((response: Object) => {
        this.setDatas1(response[0]),
        this.setDatas2(response[1]),
        this.setDatas3(response[2])
        this.contextInitialized.next(true);
        this.inj.get(Router).initialNavigation(); // <-- Init router when all responses are retrieved
      });
    });

config.service.ts

@Injectable({
  providedIn: 'root'
})
export class ConfigService {
  private static _configuration: Config;

  public static get config(): Config {
    return ConfigService._configuration;
  }

  public static set config(config: Config) {
    ConfigService._configuration = config;
  }
}

test.service.ts

@Injectable({
  providedIn: 'root'
})
export class TestService {

  foo: boolean;

  constructor(private contextService: ContextService) {
// We encounter an error here because TestService is called after ContextService but before the static variable is initialized by init()
    this.foo = ConfigService.config.bar;
  }
}

We see this error in the console: "ERROR TypeError: Cannot read property 'bar' of undefined at new TestService (test.service.ts)"

Question: Is it feasible to wait for the application to be fully loaded before utilizing a static value stored in another service?

Thanks

Answer №1

If we consider going asynchronous using rx as the most straightforward approach:

@Injectable({
  providedIn: 'root'
})
export class ConfigService {
  private static _configuration = new ReplaySubject<Config>(1);

  public static config$ = this._configuration.asObservable();

  public static setConfig(config: Config) {
    this._configuration.next(config);
  }
}

However, the entire process needs to be async (which isn't necessarily a drawback in my view):

@Injectable({
  providedIn: 'root'
})
export class TestService {

  foo$: Observable<boolean> = ConfigService.config$.pipe(map(config => config.foo));

  // You can even eliminate the contextService dependency :), config is updated upon availability
  constructor(private contextService: ContextService) {

  }

  async getFooValue(): Promise<boolean> {
    const firstFooValue = await this.foo$.pipe(first()).toPromise();
    // This fetches the initial value or awaits it (wrapped in a promise)
    return firstFooValue;
  }
}

In response to your query, yes, it is feasible to stall the application until content loads, but this may not offer the best user experience and still requires the use of promises/observables for "blocking":

Your contextservice.init should be asynchronous (returning a promise or an observable) for Angular to determine completion:

This modification might address the issue:

async init() {
    const config = await this.getConfig().toPromise();
    ConfigService.config = config;
    const response = await forkJoin(
        this.getDatas1(),
        this.getDatas2(),
        this.getDatas3()
    ).toPromise();
    this.setDatas1(response[0]);
    this.setDatas2(response[1]);
    this.setDatas3(response[2]);
    this.contextInitialized.next(true);
    this.inj.get(Router).initialNavigation(); // <-- Init router when all responses are retrieved
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to utilize ngStyle in combination with Interpolation?

Within my application, I am faced with a challenge involving two slide bars that generate values ranging from 1 to 100. Based on these generated values, I aim to adjust the margin of a div element in accordance with the percentage output. Despite conductin ...

Having issues with parameterized URL integration between Django2 and Angular2

I am encountering an issue with integrating a URL containing parameters in Angular and Django. When making a call to the url, Django expects a slash at the end while Angular appends a question mark before the parameters. How can this be resolved? Below is ...

Getting an error message like "npm ERR! code ENOTFOUND" when trying to install Angular CLI using the command "

Currently, I am eager to learn Angular and have already installed Node version 18.13.0. However, when attempting to install Angular CLI using the command npm install -g @angular/cli, I encountered an issue: npm ERR! code ENOTFOUND' 'npm ERR! sys ...

Troubleshooting Vue 2 TypeScript Components Import Issue in VS Code

Has anyone experienced issues with TS pointing errors when importing custom components into a .vue file using the options api and webpack? The import is successful, everything works after bundling, but I'm still encountering annoying errors in the .vu ...

Is there a way to set the size of my unique carousel design?

Having some trouble with my modal image carousel; the dimensions keep shifting for different image sizes. Image 1 Image 2 ...

Ensuring type safety at runtime in TypeScript

While delving into the concept of type safety in Typescript, I encountered an interesting scenario involving the following function: function test(x: number){ console.log(typeof x); } When calling this method as test('1'), a compile time er ...

What benefits does Observable provide compared to a standard Array?

In my experience with Angular, I have utilized Observables in the state layer to manage and distribute app data across different components. I believed that by using observables, the data would automatically update in the template whenever it changed, elim ...

Is there any benefit to me verifying for Zone?

I have a custom function that allows me to execute another function within an Angular zone, regardless of whether it was called from outside of Angular. Check out my implementation: export class MyZones { public static maybe(zone: NgZone, callee: () ...

The tsconfig.json file is located separate from the project directory

Working on my project called "portal" has been quite an interesting journey. As I delved deeper into development, I realized the need for multiple projects within the repository. This led me to restructure my project setup like this: A question popped up ...

Step-by-step guide to initializing data within a service during bootstrap in Angular2 version RC4

In this scenario, I have two services injected and I need to ensure that some data, like a base URL, is passed to the first service so that all subsequent services can access it. Below is my root component: export class AppCmp { constructor (private h ...

Angular 13 ModuleWithProviders Bug: A Dilemma Worth Solving

After creating a module and adding a service provider to its forRoot() static method, I imported the module into my app.module.ts file and included it in the imports section as ZooModule.forRoot(). However, when I attempted to inject the service from the Z ...

After successfully binding data in Angular, the Select component is failing to display any content

I encountered an issue where the select option disappeared completely after trying to bind countries data inside a sign-up form. Below is the relevant code snippet: Data Model export class Countries { public name: string; public code: string; ...

Inputting Dates Manually in the Angular Material Datepicker Field

The datepicker function works well unless I manually type in the date. When I input a date between 01.MM.YYYY and 12.MM.YYYY, the value switches to MM.DD.YYYY. However, if I input 16.09.2021 for example, it remains as DD.MM.YYYY. Is there a way to change ...

How can I make my webpage fill the entire width of an iPhone screen when in landscape mode using Angular or React?

While testing my website on my iPhone, I noticed a significant gap appearing on either side of the app in landscape view in Safari. Is there a solution to fix this issue? The problem occurs in both Angular and React applications, examples of which are disp ...

Executing the function in Ionic 4 when the events are absent

In my Ionic 4 multilingual app, I am fetching data from an API based on the selected language. I have set up an event for this purpose, but I face an issue when the event value does not exist - in such cases, I want to run a default function. Below is the ...

Resetting the datetime-local input to its initial value (ngModel) in template forms

In my Angular 6 template form, the user can modify the date/time in the datetime-local input after loading it with the latest data. However, I am struggling to reset the input back to the original date/time (loaded from array "one.classTimesLogOffRevised") ...

Issue in Typescript: The method `clear` or `send` is not recognized in type `unknown` within Protractor framework

Having trouble using clear and sendKeys in Protractor with TypeScript. Could it be that I am missing certain dependencies, as even the click function is giving errors? I have attempted various solutions from Protractor clear() not working, but unfortunate ...

Error: Import statement cannot be used outside a module (@cucumber/cucumber) while using Node.JS, Playwright, and Cucumber framework

I encountered an issue while attempting to compile my Node.js code that is compliant with ECMAScript 6: $ npx cucumber-js --require features/step_definitions/steps.ts --exit import { Before, Given, When, Then } from "@cucumber/cucumber"; ^^^^^^ ...

Preventing angular router events from being logged in the console

In the process of developing an angular application, I have encountered a challenge with the {RouterModule} from @angular/router. Although I rely on console.log to troubleshoot my app, the router module has unleashed a flurry of router events in the conso ...

What is the best way to patiently anticipate a response from customer service

My singltone service contains a synchronous method. Upon injecting the service into a component: constructor( private reonMapLibraryService: ReonMapLibraryService ) {} I am unable to access reonMapLibraryService.data; immediately because it is an asy ...