Angular 6 - Preload service with asynchronous requests prior to injecting dependencies

I've encountered an issue with my service that loads a configuration object in memory based on the environment file. The problem is that the settings are read asynchronously, causing the application to start before all settings are loaded and resulting in a crash. Is there a way to ensure that these functions are awaited before completing dependency injection?

Below is my service :

import { Injectable } from '@angular/core';
import { IAppConfig } from '../models/app-config.model';

@Injectable()
export class AppConfig {
    settings: IAppConfig;
    version: any;

constructor() {
    this.loadConfig();
}

// reads env.json file
// loads config setting based on environmental variations

public loadConfig() {
    return new Promise((resolve, reject) => {
        const envFile = '/env.json';
        this.readJsonFile(envFile).
            then((envData) => {
                const configFile = `assets/appconfigs/config.${envData.env}.json`;
                this.version = envData.version;
                this.readJsonFile(configFile).
                    then((configsettings) => {
                        this.settings = configsettings;
                        resolve(this.settings);
                    });
            });
    });
}

// reads json file and returns the json object promise
public readJsonFile(jsonUrl: string): any {
    return new Promise((resolve, reject) => {
        let retObject: any;
        const xhr = new XMLHttpRequest();
        xhr.overrideMimeType('application/json');
        xhr.open('GET', jsonUrl, true);
        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    retObject = JSON.parse(xhr.responseText);
                    resolve(retObject);
                } else {
                    reject(`Could not load file '${jsonUrl}': ${xhr.status}`);
                }
            }
        };
        xhr.send(null);
    });
}

}

I need the settings object to be fully loaded before the application starts. One solution was to make the class static and call loadConfig, but that's problematic for testing environments. Is there a specific way to specify when providing the service in the module to address this?

Answer №1

This proposed solution has not been tested, so there may be uncertainties. One way to approach it could be by utilizing async/await extensively.

Have you considered using Angular's built-in http service instead of manually handling XMLHttpRequest?

A potential implementation could look like the following :

constructor() {
    this.run()
}

async run() {
    await this.loadConfig()
}

public loadConfig() {
    return new Promise( async (resolve, reject) => {
        const envFile = '/env.json';

        let envData = await this.readJsonFile(envFile)

        const configFile = `assets/appconfigs/config.${envData.env}.json`;

        this.version = envData.version;

        this.settings = await this.readJsonFile(configFile);

        resolve();
    });
}

public readJsonFile(jsonUrl: string): any {
    return this.http
        .get(jsonUrl)
        .map(res => res.json())
        .toPromise()
}

Answer №2

Prior to launching the application, I would like the settings object to be fully loaded.

An effective way to achieve this is by utilizing the injection token APP_INITIALIZER: angular.io/APP_INITIALIZER

For your specific scenario, implementing code similar to the following within your AppModule would be sufficient: In your AppModule:

{
  provide: APP_INITIALIZER,
  useFactory: initApp,
  deps: [AppConfig]
}

export function initApp(appConfig: AppConfig) {
  return () => appConfig.loadConfig();
}

To learn more about factory providers, visit: angular.io/FactoryProvider

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

Troubleshooting: Why Your Angular Data Binding is Failing

I am integrating a WCF REST service with an AngularJS application. My goal is to retrieve account information based on the account number provided, however, I am encountering an issue where the text "Account_Type" is displayed three times before showing th ...

Creating reusable functions in VueJS that can be accessed globally by all child components

Looking for assistance in setting up a universal function that can be accessed across all my Vue files. For example, when using this code snippet in a Vue file: @click="ModalShow.show('my-create')" I have defined the following constan ...

The elements appear tiny while the resolution is excessively large on the Ionic mobile device

I recently finished developing an Ionic project and successfully compiled it for both iOS and Android. Surprisingly, everything seems to be working fine on Android devices but I am encountering issues on iOS and when viewing the project from Chrome's ...

Tips for utilizing the JQuery feature to clear input text

After attempting to use onfocus and onblur attributes within the input tag to clear input text, I discovered a JQuery function that should do the trick for me. However, I am encountering issues with getting it to work across multiple fields. $('.defa ...

The jQuery countdown plugin is yielding some unexpected outcomes

Feeling a bit rushed for time, so I thought I'd ask here. The date is currently 2012-10-06 and I'm attempting to implement a jQuery plugin called "jquery.countdown.js". It seems pretty straightforward. Can anyone point out what I might be doing i ...

Sending data to a PHP page to maintain state in an Angular application

Here is my current setup: In a dynamic Angular environment, I have various states connected to PHP pages. These PHP pages rely on specific data variables, typically provided as GET parameters outside of Angular. Now, I am looking for a way to switch to a ...

Prevent the function from being triggered repeatedly while scrolling

When a user scrolls to the bottom of the page, the following code is meant to load the next page. However, there are instances where it repeats itself due to rapid scrolling or while the AJAX content is still loading. Is there a way to prevent this code f ...

Issue: The initial parameter should be a File or Blob object

Hey there! I'm currently utilizing the compressorjs plugin for compressing images, but I'm encountering an issue when selecting images. You can find out more about the plugin here. Here is my code snippet: window.resolveLocalFileSystemURL( ...

Is TypeScript capable of comprehending Svelte components?

When it comes to Svelte, the final output is native JavaScript classes, which TypeScript can understand. However, before TypeScript can recognize Svelte components, they must first be compiled from their initial .html form. This can lead to a 'cannot ...

Clicking on a link initiates the dropdown menu for selecting an option

This project is specifically designed for mobile use, so there's no need to worry about how it will appear on desktop screens. In this project, I have an "a href" with an icon next to it that simulates a button. When a user clicks on it, a dropdown me ...

Guide for transferring the body of a table to a different component without disrupting the design aesthetics

In an attempt to reorganize my large table component, I decided to separate its body section into a new component. However, every time I try to do this, the styling of the table breaks (likely due to the new HTML structure in the code). I'm currently ...

What is the mechanism through which a nested function within an event handler obtains the event object?

How is the e object available to the nested function inside handleClick2 when only the input object is passed? Is this related to the concept of Lexical Environment? handleClick2 = (input) => (e) => { this.setState({ [input]: e.target.va ...

Exploring the functions of `map` and `filter` in the world of

Consider this input: var m = [{ name: 'foo', routes: [{verb: 'post', path: '/foo1'}, {verb: 'get', path: '/foo2'}] }, { name: 'bar', routes: [{verb: 'put', path: ...

How to use Nativescript to retain the keyboard visibility on Android when pressing the Enter key

When attempting to create a chat view in Nativescript Javascript, I encountered an issue. After pressing the "Send" button on the keyboard, the message is sent; however, there is a strange behavior where the first 'enter' press on the keyboard is ...

What is the best way to establish communication with the root component in Angular?

I have implemented a modal in the root component that can be triggered from anywhere. However, I am facing a dilemma on how the bottom component can communicate with the top component without excessive use of callback functions. Root Component <contai ...

Typescript: Firebase App type does not include delete, installations, name, or options properties

Exploring the realm of Typescript and its compatibility with Firebase has been a recent endeavor of mine. I've created a FirebaseProvider that requires a Firebase app to be configured in the following manner: import firebase from "firebase/app&qu ...

Utilizing Bootstrap to allow for seamless text wrapping around a text input field

I am trying to implement a "fill-in-the-blank" feature using Bootstrap, where users need to enter a missing word to complete a sentence. Is there a way to align the text input horizontally and have the rest of the sentence wrap around it? This is my curr ...

Tips for retrieving JSON data using ajax with jPut

Recently, I stumbled upon a handy jQuery plugin called Jput that allows for easy appending of JSON data to HTML content. You can check it out here. However, I am curious about the process of sending and retrieving JSON data via AJAX. Any insights or guida ...

There seems to be an issue with the Angular9 window.open() function as it is

I've encountered a strange issue where the code runs successfully on my local environment, but in production, I keep receiving a bad request 400 error. Any insights on what might be causing this? Here is the URL that triggers the bad request error: h ...

Angular 2 implementes a loading spinner for every HTTP request made

My objective is to implement a spinner functionality whenever an HTTP request occurs in my Angular app. Essentially, I want the user to see a loading screen during these requests within my app component. The setup for my spinner component and spinner servi ...