Lazy-loaded modules in Angular that contain services provided within the module

Currently, I am facing a challenge with lazy-loaded modules and services that are provided in these modules. My folder structure looks like this:

app
-> featureModule1 (lazy loaded)
-> featureModule2 (lazy loaded)
-->services
--->service1.ts

I want service1.ts to be exclusively available in featureModule2. So, I included @Injectable

// service.ts    
@Injectable({
   providedIn: featureModule2
})
export class service1 { /* some logic */ }

When trying to route to this lazy-loaded module, a console error occurs:

// console    
ERROR Error: Uncaught (in promise): ReferenceError: Cannot access 'featureModule2' before initialization
    ReferenceError: Cannot access 'featureModule2' before initialization

My current lazy load route is as follows:

// app-routing.module.ts    
const routes: Routes = [
      {path: '', redirectTo: '', pathMatch: 'full'},
      { path: 'feature1', loadChildren: () => import('./feature1/feature1.module').then(m => m.featureModule1) },
      { path: 'feature2', loadChildren: () => import('./feature2/feature2.module').then(m => m.featureModule2) }
    ];

I attempted to provide it in the module:

// featureModule1     
@NgModule({
 declarations: [
  featureComponent
 ],
 imports: [
   ...
 ],
 providers: [
  service1
 ]
})

However, that approach did not work.

I also tried importing service1.ts directly into a component (featureComponent).

// featureComponent
import { service1 } from '../featureModule2/services/service1.service';

@Component({
  ...
})
export class featureComponent {
  constructor(private service1: service1) { }

  ngOnInit(): void {
    this.service1.init();
  }
}

This method resulted in the same error message.

At present, the only solution to bypass this issue is to create a "wrapper" module that imports all other modules with @Injectable services.

Is there an alternative way to address this problem? The services in each module should not be injected in root or any, as they should only be accessible in each featureModule.

Answer №1

To make it work, simply declare it in the providers section. I have utilized the useFactory provider type to demonstrate that injection is necessary and Angular does not just perform a new LazyService operation on the spot. You can also use providers:[LazyService] which is essentially a 'class provider' equivalent to useClass:LazyService.

Within the lazy module:

import {
  CustomerListComponent,
  LazyService
} from './customer-list/customer-list.component';

@NgModule({
  imports: [CommonModule, CustomersRoutingModule],
  declarations: [CustomerListComponent],
  providers: [
    {
      provide: LazyService,
      useFactory: () => new LazyService('proofThatItIsFromPRoviders')
    }
  ]
})
export class CustomersModule {}

In the lazy component within the same module (displays "Lazy service works proofThatItIsFromPRoviders"):

export class LazyService {
  constructor(private test: string) {}
  public say() {
    console.log('Lazy service works', this.test);
  }
}

@Component({
  selector: 'app-customer-list',
  templateUrl: './customer-list.component.html',
  styleUrls: ['./customer-list.component.css']
})
export class CustomerListComponent implements OnInit {
  constructor(private lazy: LazyService) {}

  ngOnInit() {
    this.lazy.say();
  }
}

Injection into a different module will fail:

@Component({
  selector: 'app-order-list',
  templateUrl: './order-list.component.html',
  styleUrls: ['./order-list.component.css']
})
export class OrderListComponent implements OnInit {

  constructor(private lazy:LazyService) { }

  ngOnInit() {
    this.lazy.say();
  }

}

Check out a working example here.

Answer №2

When configuring a service like this:

   @Injectable({
       providedIn: featureModule2
    })
    export class TestService {
    }

This means that TestService will only be accessible to applications if they import featureModule2.

Since featureModule2 is a lazy-loaded module, it's best not to specify it in the app module imports. Instead, you can either use

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

or

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

The difference between the two options is that when using 'root', there will be a single instance of that service throughout the app. If using 'any' for lazily loaded modules, a new instance of the service will be created, while for eagerly loaded modules, it will act as a singleton.

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

Angular5 routing causing issues with component rendering

In my application built with Angular 5, this is how my app.module.ts file looks like. import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angu ...

What are the reasons for the inability to send form-data in Postman?

Encountering an issue when trying to send form-data in postman as Sequelize returns an error: value cannot be null However, everything works fine when sending a raw request with JSON. Have tried using body-parser and multer, but no luck. This is my inde ...

What is the method for determining the width of a Mat-Table once it has been displayed?

When utilizing Angular Material Mat-Table in conjunction with Angular 8, I am passing the dataSource dynamically. The number of rows and columns varies each time. Is there a method to calculate the width of the table once it is rendered on the screen? &l ...

Passing an array of items as a property to a child component in React with Typescript is not possible

In my project, I have multiple classes designed with create-react-app. I am trying to send an array of objects to child components as illustrated below. Items.tsx import * as React from 'react'; import ItemTable from './ItemTable'; imp ...

The state of the checked value remains unaffected when using the Angular Material Checkbox

I am currently working on a list of elements and implementing a filter using pipes. The filter allows for multi-selection, so users can filter the list by more than one value at a time. To ensure that the filter persists even when the window is closed or t ...

Uploading Files to REST API using Angular2

Currently, I am developing a Spring REST API with an interface built using Angular 2. My issue lies in the inability to upload a file using Angular 2. This is my Java Webresource code: @RequestMapping(method = RequestMethod.POST, value = "/upload") publ ...

I encountered an error stating "Buffer is not defined" originating from the Deode/Encode Stream Bundle.js script, not from my own code

I've encountered a major issue while attempting to update my npm project to webpack 5, and now I'm left with just one persistent error: bundle.js:1088566 Uncaught ReferenceError: Buffer is not defined at bundle.js:1044980:24 ...

Angular: Streamlining the Constructor Function for Efficiency

Consider the scenario where we have these two components: export class HeroComponent { constructor( public service1: Service1, public service2: Service2, ) { // perform some action } } export class AdvancedHeroComponent extends HeroCompone ...

What is the best way to accurately parse a Date object within a TypeScript class when the HttpClient mapping is not working correctly?

Task.ts: export class Task { name: string; dueDate: Date; } tasks.service.ts: @Injectable() export class TasksService { constructor(private http: HttpClient) { } getTasks(): Observable<Task[]> { return this.http.get<Ta ...

Why does React / NextJS throw a "Cannot read properties of null" error?

In my NextJS application, I am using useState and useEffect to conditionally render a set of data tables: const [board,setBoard] = useState("AllTime"); const [AllTimeLeaderboardVisible, setAllTimeLeaderboardVisible] = useState(false); const [TrendingCreat ...

The language service for Angular is not functioning properly within the VSCode environment

Angular Latest Version Information Package Version ----------------------------------------------------------- @angular-devkit/architect 0.13.6 @angular-devkit/build-angular 0.13.6 @angular-devkit/build-optimizer 0. ...

The error code TS2345 indicates that the argument type 'Event' cannot be assigned to a parameter type 'string'

Hello, I'm a newcomer to utilizing Angular and I'm struggling to identify where my mistake lies. Below is the TypeScript code in question: import { Component } from '@angular/core'; @Component({ selector: 'app-root' ...

When in development mode, opt for the unminified version of the library in Web

My TypeScript project utilizes a forked version of the apexcharts npm package. When building the project with webpack in development mode, I want to use the unminified version of the apex charts library. However, for production, I prefer to stick with the ...

What is the best way to include a Web Service within an export variable in Angular 2 using TypeScript?

Is there a way to incorporate JSON data retrieved from the server into the export var HEROES: Hero[ ] function? Here is the link: https://angular.io/resources/live-examples/toh-5/ts/eplnkr.html In app/mock-heroes.ts, you will find the following data, im ...

Creating Algorithms for Generic Interfaces in TypeScript to Make them Compatible with Derived Generic Classes

Consider the (simplified) code: interface GenericInterface<T> { value: T } function genericIdentity<T>(instance : GenericInterface<T>) : GenericInterface<T> { return instance; } class GenericImplementingClass<T> implemen ...

Access to Firebase using Google authentication is currently restricted (permission denied)

Currently, I am utilizing Firebase to authenticate users with Google in my Angular project, "project1." When signing anonymously into Firebase, everything runs smoothly. However, if I attempt to sign in with Google using the popup feature, an error occurs: ...

Effective strategies for extracting value from asynchronous Angular events that return Promises

When it comes to subscription blocks, extracting the value from my API is possible. var data = null; this._timinServiceProxy.getDateFromNTP().subscribe(result => { data = result; console.log(data); // The expected result }); console.log(data); ...

Resharper griping about TypeScript object in Angular 2

Currently, I have Resharper 10 integrated into Visual Studio 2015. https://i.stack.imgur.com/vksGb.png In the screenshot, highlighted by the green box, there's an issue with valid decorator syntax which results in this error message: Cannot conve ...

Setting up the Font Awesome Pro version in Angular using the Font-Awesome package

The process of installing the pro version of Angular Font-awesome began with setting up my registry using these commands: npm config set "@fortawesome:registry" https://npm.fontawesome.com/ && \ npm config set "//npm.fontawesome.com/:_authTo ...

Guide on creating a 4-point perspective transform with HTML5 canvas and three.js

First off, here's a visual representation of my objective: https://i.stack.imgur.com/5Uo1h.png (Credit for the photo: ) The concise question How can I use HTML5 video & canvas to execute a 4-point perspective transform in order to display only ...