MrDeibl
MrDeibl

Reputation: 167

Angular lazy-loaded modules with services provideIn this module

I currently have an issue with lazy laoded modules and services which are providedIn this lazy-loaded module.

I have a folder structure looking like this:

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

I want this service1.ts only be available in featureModule2. So i included @Injectable

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

When i try to route to this lazy loaded module i get an console error:

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

My current lazy load route looks like this:

// 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 tried to provide it in the module:

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

That doesn't work.

And i tried to import the service1.ts directly 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 approach lead to the same error message.

The only way to get around this issue at the moment is to create a "wrapper" module which imports all other modules with @Injectable services.

Is there a different way for this problem? The services in each module should not be injected in root or any because they should only be available in each featureModule.

Upvotes: 4

Views: 3134

Answers (2)

Ritesh Waghela
Ritesh Waghela

Reputation: 3727

When we configure a service like this:

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

It means that TestService won't be available to applications unless they import featureModule2.

Since featureModule2 is a lazy loaded module, you definitely not want to specify this in app module imports and hence you will need to either use

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

or

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

The different between above two is that when we use 'root there will be a single instance of that service throught the app. In case of 'any' for lazily loaded module there will be a new instance of that service and for eagerly loaded module it will act as a singleton.

Upvotes: 1

Antoniossss
Antoniossss

Reputation: 32517

You just have to declare it in providers section and it will work. I have used useFactory provider variant in order to clearly show that it is used in order to perform injection and angular is not simply new LazyService just in place. I will work with providers:[LazyService] as well (which is a 'class provider' I belive, equal to useClass:LazyService)

In 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 lazy component from the same module ("printsLazy 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 in different module fails

@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();
  }

}

Working example here https://stackblitz.com/edit/angular-s5xxnb-j9ch1z?file=src%2Fapp%2Forders%2Forder-list%2Forder-list.component.ts

Upvotes: 1

Related Questions