Conditions are in an angular type provider with AOT

I am facing an issue with my Angular project that is compiled using AOT. I am trying to dynamically register a ClassProvider based on certain configurations. The simplified code snippet I am currently using is below:

const isMock = Math.random() > 0.5;

@NgModule({
  // ...
  providers: [
    { provide: MyServiceBase, useClass: (isMock) ? MyServiceMock : MyService },
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

However, the problem arises when I compile this code with AOT, as it always resolves to the same service instead of dynamically switching between services. It works as expected when compiled without AOT.

You can find the entire code example on GitHub here: https://github.com/vdolek/angular-test/tree/aot-conditioned-provider-problem. The behavior differs when running with ng serve and ng serve --aot.

Is there a way to achieve the dynamic provider registration behavior I need? I am aware of using a FactoryProvider, but that would involve duplicating service dependencies which I'd like to avoid.

Answer №1

To meet the dynamic requirements, utilize factory providers with the useFactory attribute.

I have cloned your repository and made changes to your app.module.ts file to make it compatible with AOT compilation.

Please modify app.module.ts as shown below:

export let myServiceFactory = () => {

  const isMock = Math.random() > 0.5;

  return isMock ? new MyServiceMock() : new MyService();
}; 

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [
    {provide: MyServiceBase, useFactory: myServiceFactory},
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

If your service has dependencies on other services (which is likely), you can use the deps argument to inject these dependencies.

For instance, if MyServiceBase depends on MyService1 and MyService2, your factory function should be defined like this:

export let myServiceFactory = (service1:MyService1, service2:MyService2) => {

  const isMock = Math.random() > 0.5;

  return isMock ? new MyServiceMock(service1, service2) : new MyService(service1, service2);
}; 

Your provider declaration would then look like this:

providers: [
    {
       provide: MyServiceBase, 
       useFactory: myServiceFactory,
       deps: [MyService1, MyService2]
    },
]

Refer to this guide for more details on Angular dependency injection methods.

Answer №2

It seems that using a factory is the only option, as pointed out by @jeanpaul-a. However, managing dependencies in this approach may not be very straightforward. One alternative solution could be utilizing the Injector. A possible implementation could look like this:

@NgModule({
  imports:      [ BrowserModule, FormsModule ],
  declarations: [ AppComponent, HelloComponent ],
  providers: [
    Dep1Service,
    Dep2Service,
    { provide: MyServiceBase, useFactory: createService, deps: [Injector] }
  ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

export function createService(injector: Injector) {
  const isMock = Math.random() > 0.5;
  if (mock) {
    return new MyService1(injector.get(Dep2Service));
  } else {
    return new MyService2(injector.get(Dep1Service));
  }
}

Another approach you can take is to define MyServiceBase as an interface and utilize InjectionToken. You can explore a live example here (please note that the class names are different).

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

Update the message displayed in the user interface after the view has been fully rendered in an Express application, following the execution of asynchronous

I have created a simple express app that reads files from a directory, renames them, zips the files asynchronously, and then renders another view. The file reading and renaming are done synchronously, while the zipping part is handled asynchronously. My cu ...

Displaying the information fetched using axios on the screen using node.js

Is there a way to display the information from the input boxes in the image on the screen using node js? ...

Attempting to adjust the width of a text animation loaded with jQuery using Textillate, but encountering difficulties

I found a captivating animation on this website: http://codepen.io/jschr/pen/GaJCi Currently, I am integrating it into my project. #content { position: relative; margin-left: auto; margin-right: auto; width: 1000px; height: 700px; } ...

The WebView.HitTestResult method is currently only receiving <img src> elements and not <a href> elements

I am attempting to open a new window in the Android browser using "_blank". I have set up an event listener for this purpose. mWebView.getSettings().setSupportMultipleWindows(true); mWebView.setWebChromeClient(new WebChromeClient() { ...

Error encountered when utilizing Meteor in conjunction with TypeScript

Currently, I am in the process of building a web application using Meteor and TypeScript within the Nitrous.io cloud development environment. After installing the TypeScript compiler, I integrated TypeScript libraries from https://github.com/meteor-typesc ...

What is the best approach to updating multiple rows instead of just the first row using the update button located on both sides using PHP and AJAX?

Starting fresh, so I might sound like a beginner with this question. How can I make use of the update button to update specific rows instead of just the top row? Every time I try to update, it only works for the top row. Here's the code snippet from ...

The issue with Angular 4 imports not refreshing in a .less file persists

Currently, I am in the process of developing a small Angular project that utilizes less for styling purposes. My intention is to separate the styling into distinct folders apart from the components and instead have a main import file - main.less. This fil ...

Concealing overflow in a sticky container when a fixed child element is present

I am currently working on a website where I have implemented slide-up section panels using position: sticky while scrolling. However, I am encountering issues with fixed elements within the sticky panels not properly hiding outside of the respective sectio ...

Having trouble getting Angular-UI-Select to work with a basic variable on $scope?

How can you reset an array with selected values so that the values can be reselected from the dropdown? I am working with a people array where the values are initially displayed in a select dropdown. When I choose certain names, they get transferred to th ...

Is it possible for Node.js to not automatically restart the server when modifying .js files?

Right now I have node-supervisor set up to detect changes in .js files, and while it works well, I've realized that it restarts the server every time a js file is saved. Is there a way to save a server-side .js file without triggering a server restart ...

The specified "ID" type variable "$userId" is being utilized in a positional context that is anticipating a "non-null ID" type

When attempting to execute a GraphQL request using the npm package graphql-request, I am exploring the use of template literals. async getCandidate(userId: number) { const query = gql` query($userId: ID){ candidate( ...

Clicking on Rows in DataTables: Enhancing

I have been utilizing the jquery datatables plugin, and have encountered an issue with the row click functionality. Strangely, it only seems to work on the first page of the table. When I navigate to any subsequent pages, the row click fails to respond whe ...

The function has been called but it did not return a

It seems that there is confusion surrounding the .toHaveBeenCalled() Matcher in Jasmine. While it should return a Promise that resolves when the function has been called, some users are experiencing it returning undefined instead. For example: it('sh ...

Arrange pictures and div elements in a horizontal layout

I am facing an issue with my code that is displaying 5 'cards' in a messed up layout instead of a straight line. I have tried to align them vertically, but some are still higher and not in the right positions. For reference, I want the layout to ...

Tips for extracting key values from an array of objects in Typescript

I am working with an array called studyTypes: const studyTypes = [ { value: "ENG", label: "ENG-RU", }, { value: "RU", label: "RU-ENG", }, ]; Additionally, I have a state variable set ...

AngularJS Splice Function Used to Remove Selected Items from List

I previously inquired about a method to remove items from the Grid and received a solution involving the Filter method. However, I am specifically looking for a way to remove items using the Splice Function instead. You can find my original question here: ...

Greetings, Angular2 application with TypeScript that showcases the beauty of the world

I've been working on my first angular2 program and noticed some deviations from the expected output. typings.json: { "ambientDependencies": { "es6-shim": "github:DefinitelyTyped/DefinitelyTyped/es6-shim/es6-shim.d.ts#7de6c3dd94feaeb21f20054b9f ...

Encountering difficulties setting cookies within the app router in Next.js

I've been diving into next.js and I'm trying to figure out how to set a cookie using the code below. However, I'm encountering an error: "Unhandled Runtime Error. Error: Cookies can only be modified in a Server Action or Route Handler." I ...

The specified 'IArguments' type does not qualify as an array type

Currently working on crafting a personalized logger. It's a fairly straightforward process, but I'm running into some errors that are muddying up my output. Here's what I have so far: @Injectable() export class Logger { log(...args: any ...

Discovering the worth of specific selections in a dropdown menu

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> </head> <body> <select name="states" id="states"> <option value="100">Hawaii</option> <option value="107">Texa ...