7

I am using an application structure as mentioned below

index.ts
|-app.module.ts
|-app.component.ts
|--hero (directory)
   |-hero.module.ts
   |-hero.ts (Data Object)
   |-hero.service.ts
   |-hero.component.ts
   |-index.ts (this file exports data obj, service, component & module)
|--dashboard (directory)
   |-dashboard.module.ts
   |-dashboard.component.ts
   |-index.ts (this file exports module and component)

I wish to use hero service in dashboard component. Below is the code snippet I am using right now and its working as expected. But not sure if its a good practice.

import { Component, OnInit } from '@angular/core';

import { Hero, HeroService } from '../hero/index';
import {Router} from '@angular/router';

@Component({
    moduleId: module.id,
    selector: 'my-dashboard',
    templateUrl: 'dashboard.component.html',
    styleUrls: ['dashboard.component.css']
})
export class DashboardComponent implements OnInit {
    heroes: Hero[] = [];

    constructor(private heroService: HeroService, private router: Router) { }

    ngOnInit(): void {
        this.heroService.getHeroes()
            .then(heroes => this.heroes = heroes.slice(1, 5));
    }

    gotoDetail(hero: Hero): void {
        let link = ['/detail', hero.id];
        this.router.navigate(link);
    }
}

I am curious to know if there is any way that I can access HeroService with reference to HeroModule rather than separately importing Hero object and HeroService from ../hero/index

Mahesh
  • 1,427
  • 2
  • 19
  • 42
  • 1
    One of the suggestion is to move hero.service.ts to shared package but I don't want to use that approach as logically this service belongs to hero package and only some part of it needs to be referred in some other package. – Mahesh Aug 25 '16 at 13:58
  • Imports are a TypeScript feature and unrelated to Angular. If you want to use a class as type in your code you have to import it (with or without Angular2). `imports: []` in `NgModule` fulfill an entirely different purpose and this **is** an Angular2 feature. – Günter Zöchbauer Aug 26 '16 at 06:48
  • 1
    A very similar question was asked after yours which should lead you in the right direction: http://stackoverflow.com/questions/39621398/angular2-import-components-services-from-module – cmaynard Oct 14 '16 at 12:25
  • I totally understand your question, but other than components, directives, and pipes which can be placed directly on the template of a component, we need to have the source file for service, since typescript will throw an error. There is not getting around this problem until Angular/Typescript comeup with something like a proxy. – GingerBeer May 24 '18 at 10:45

2 Answers2

18

from Range.io

So far our problem is that we are creating two instances of the same services in different levels of the DI tree. The instance created in the lower branch of the tree is shadowing the one defined at the root level. The solution? To avoid creating a second instance in a lower level of the DI tree for the lazy loaded module and only use the service instance registered at the root of the tree.

import { NgModule, ModuleWithProviders } from '@angular/core';
import { CounterService } from './counter.service';

@NgModule({})
export class SharedModule {
  static forRoot(): ModuleWithProviders {
    return {
      ngModule: SharedModule,
      providers: [CounterService]
    };
  }
}

With this setup, we can import this module in our root module AppModule calling the forRoot method to register the module and the service.

...
import { SharedModule } from './shared/shared.module';

@NgModule({
  imports: [
    SharedModule.forRoot(),
    ...
  ],
  ...
})
export class AppModule {}
LeonardoX
  • 1,113
  • 14
  • 31
  • This is great and elegant solution! – Mehdi Oct 18 '17 at 04:36
  • 3
    In all other lazy loaded modules: just import the service directly using module name. Instead of *SharedService.forRoot()* just use *SharedService*. – Anos K. Mhazo Nov 28 '17 at 12:12
  • I think you should include the "how to use" the service in the answer too. – Machado Jan 30 '18 at 18:57
  • 1
    @Machado the topic of this post is regarded to "the best way to share a service", the easiest part is how to use it, just injecting it in the constructor of any component as described in the question: constructor(private heroService: HeroService, private router: Router) – LeonardoX Feb 03 '18 at 23:39
  • This is perfect and solved a problem i was having for several days. The explanation is great also. – Jurgen Welschen Mar 11 '18 at 22:34
  • thanks, what if I want to use the service inside a component belong to a feature module? I imported the SharedModule into the feature module? what else should I do? – userx Apr 22 '18 at 09:44
  • @Furqan Shakir, once you've imported the shared module into your feature module you can use the service as usually, referencing it in your component and injecting it in its constructor. – LeonardoX Apr 22 '18 at 11:13
  • @LeonardoX, I did that, but I keep getting this error: Cannot find module '../services/global-variables.service' – userx Apr 22 '18 at 11:28
  • 1
    @Furqan Shakir, that seems more related to be a wrong path. Try to use absolute paths like: from 'app/yourservice.service' or look for similar problems in stackoverflow – LeonardoX Apr 22 '18 at 11:57
  • @LeonardoX thanks, using absolute paths worked for me. – userx Apr 22 '18 at 12:14
1

Services are shared across the entire application, so if you put it into a module, other component has acces to it. However, you still need to import the classes Hero and HeroService in component where you use them.

Imports at the top of a class and Modules has just a differents purpose.

Alexis Le Gal
  • 327
  • 4
  • 11
  • 2
    That's basically true but lazy-loaded modules are an exception. You need to implement `forRoot()` and provide the services there to make them available globally. – Günter Zöchbauer Aug 26 '16 at 06:42