Ensure service is instantiated within an Angular 2 sub-module (a different approach from AngularJS run block)

Within a sub-module, there is a service that wraps a third-party module, instantiates it, and initializes its service for usage within the app.

@Injectable()
class SubmoduleInitializerService {
    constructor (thirdPartyService: ThirdPartyService) {
      thirdPartyService.initialize(...);
      ...
    }
}

@NgModule({
    imports: [ThirdPartyModule],
    exports: [ThirdPartyModule],
    providers: [
        ThirdPartyService,
        SubmoduleInitializerService
    ]
})
class AppSubmodule {}

The ThirdPartyService is not directly injected in the app but is utilized by other units of the ThirdPartyModule. As long as the SubmoduleInitializerService is injected in the same injector as the ThirdPartyService or in the parent injector, everything functions correctly:

export class AppComponent {
    constructor(
      /* DO NOT REMOVE! BAD THINGS HAPPEN! */
      submoduleInitializerService: SubmoduleInitializerService
    ) {}
    ...
}

This setup has proven to be ineffective as it lacks clarity on why the SubmoduleInitializerService needs to remain injected in the AppComponent without being used in either the class or the template. This caused accidental removals in the past.

In essence, the AppSubmodule module requires an alternative to Angular 1.x's angular.module(...).run(...) block.

What are the possible solutions for this scenario?

Answer №1

APP_INITIALIZER is a service that serves a similar purpose to AngularJS config/run blocks within Angular 2, although it lacks asynchronous initialization capabilities.

If you simply want to eagerly instantiate the SubmoduleInitializerService, you can do so with the following configuration:

@NgModule({
    imports: [ThirdPartyModule],
    exports: [ThirdPartyModule],
    providers: [
        ThirdPartyService,
        SubmoduleInitializerService,
        {
            provide: APP_INITIALIZER,
            useFactory: () => () => {},
            deps: [SubmoduleInitializerService],
            multi: true
        }
    ]
})
class AppSubmodule {}

Since APP_INITIALIZER allows for multiple initialization functions per application, they are executed in the order that modules are loaded.

For synchronous initialization, a shorter and perhaps more suitable alternative is injecting the service into the module's constructor:

@NgModule({
    imports: [ThirdPartyModule],
    exports: [ThirdPartyModule],
    providers: [
        ThirdPartyService,
        SubmoduleInitializerService
    ]
})
class AppSubmodule {
    constructor(sis: SubmoduleInitializerService) {}
}

As mentioned in this answer, APP_INITIALIZER shares similarities with the config block as it is used to configure services before component initialization and can lead to race conditions such as circular dependencies when configuring certain services like the Router.

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

The passing of query string parameters from JavaScript to a PHP processing script is ineffective

I am looking to dynamically populate a jQWidgets listbox control on my webpage with data retrieved from a MySQL database table once the page has finished loading and rendering. PARTIAL SOLUTION: You can find a solution here. NEW PROBLEM: I have created a ...

What is the internal mechanism used by Angular for routing implementation?

My traditional belief about web browsers has been challenged by the behavior of Angular. I used to think that when a user enters a new URL, the browser would fetch a whole new page from the network and display it, replacing the old one. But with Angular, ...

Out of the blue synchronization issues arising from utilizing the nodejs events module

In my code, I am utilizing the Node Events module to execute a function asynchronously. var events = require('events'); var eventEmitter = new events.EventEmitter(); eventEmitter.on('myEvent', f2); function f1(x, y) { console.log( ...

The improved approach to implementing guards in Angular

I am currently looking for the most effective way to utilize Angular "guards" to determine if a user is logged in. Currently, I am checking if the token is stored. However, I am wondering if it would be better to create an endpoint in my API that can verif ...

Rendering data from an API using v-if

Could you help me change the tag that currently displays true or false? I want it to show "FREE" if the event is free and "PAID" if it's not. Check out the Eventbrite API here The response I'm receiving from data.events.is_free is in boolean fo ...

Tips for streamlining the use of http.get() with or without parameters

retrievePosts(userId?: string): Observable<any> { const params = userId ? new HttpParams().set('userId', userId.toString()) : null; return this.http.get(ApiUrl + ApiPath, { params }); } I am attempting to streamline the two http.get ca ...

Anonymous function's return type

Looking for advice on an anonymous function I've written: static oneOf(options: any[], cb?: Function) ValidatorFn { .... } I'm a TypeScript beginner and unsure how to specify that the 'cb' must return a boolean. Can this be done, an ...

"Delivering dynamic HTML content using React/Redux variables in the production environment

I am currently in the process of developing a React web application, and I have encountered an issue where a variable intended to store a schedule is being filled with the HTML content of the page when I build and serve the application. Interestingly, the ...

Transmit ASP Webform data through Visual Studio to an external API

While working on a webform project in Visual Studio, I encountered an issue when trying to send data from the form fields to a 3rd party API using a POST request. Despite my attempts to use JSON to capture the form field data and send it as a JSON object, ...

The format must be provided when converting a Spanish date to a moment object

I am working on an Angular 5 project where I am converting dates to moment objects using the following code: moment(date).add(1, 'd').toDate() When dealing with Spanish locale and a date string like '31/7/2018', the moment(date) funct ...

Generate a dynamic key within a variable and set a corresponding value in typescript

Within my component, I have created a variable as an array called info[], with the intention of dynamically assigning values based on data from a service. export class CostsComponent implements OnInit { public info: any[] = []; constructor(private de ...

What is the proper method for incorporating text into d3.js nodes?

As I venture into the world of d3.js, I've decided to experiment with some sample code from this particular page and tweak it according to my needs. My main goal is to include TEXT within the colored nodes. Although these nodes already have a title p ...

Comparing getElementById with $('#element') for retrieving the length of an input field

Consider the following scenario: <input type="text" id="apple"> Why does the first code snippet work? $(document).ready(function () { alert($('#apple').val().length); }); However, why does the second code snippet not work as expecte ...

Replace the plus signs in a string with spaces using the JSON.stringify method

When utilizing AJAX, I am sending the string someString to a PHP handler. xmlHttp.open("POST", url, true); var someString = 'Foo+Bar'; var params = 'operation='+operation+'&json='+JSON.stringify(someString); xmlHttp.send( ...

Guide on converting this function into a computed property within Vue

Is it possible to concatenate a fixed directory path, which is defined in the data property, with a file name that is determined using v-for? I encountered an issue when attempting to do this using a computed property. The error message displayed was: ...

What are your thoughts on implementing the model-view-controller pattern for a JavaScript game using Three.js?

As I work on a 3D game in three.js that is entirely frontend-based at the moment, I'm considering whether separating the model (state) and view is a wise decision. Currently, I directly manipulate the movement of objects by translating and rotating t ...

"Utilize gulp-connect to store the root as an array

The gulp-connect documentation mentions that options.root can accept either an array or string. My situation involves having a build directory that I want to use as the root, along with a separate sources directory where all my source maps are located. Is ...

Delivering a JSON response containing an item within an array

I am struggling to find the correct way to access the error message related to the 'firstname' field in the JSON-encoded object. The error message states that the length of the value for 'firstname' must be at least 3 characters long. ...

Attempting to send $scope.data into a parameter object when transferring from controller to service within Angular framework

Within my index.html file, the following code exists: <input type="text" ng-model="title" placeholder="Search..." > <button type="submit" href="#" ng-click="getSearch()">Submit</button> <select ng-model="selected" ng-options="obj.val ...

Trouble with the drop-down menu displaying "string:2" in an AngularJS application

Currently, I am utilizing AngularJS ng-model to choose the value from a drop-down menu. Additionally, I am implementing datatable for organizing the columns. <select id="{{user.id}}" ng-model="user.commit" name="options" ng-change="update_commit_level ...