3

I tried to create some core module for just like in tutorial except one detail, I want provide only services. So my CoreModule should be like - singleton services provider, because I don't want to provide tons of services in AppModule like in app versions <= RC4. I tried to do that stuff but it is not working. I did some mistake? Thanks for any help.

import {
  ModuleWithProviders, NgModule,
  Optional, SkipSelf }       from '@angular/core';

import { CommonModule }      from '@angular/common';

import { CommunicatePatientListService }    from './services/communicate_patients_list.service';
import { HTTPPatientsListService }    from './services/http_patients_list.service';
import { SharedService }    from './services/shared_service';


@NgModule({
  imports:      [ CommonModule ],
  providers:    [
    CommunicatePatientListService,
    HTTPPatientsListService,
    SharedService
  ],
  exports: []
})
export class CoreModule {}

Update 2. I tried different ways to do that things that was provided to me and I found an interesting moment.

When I import in COREModule singleton services via standard import it doesn't works but when I imported it via System.js aliases it is working now. I am really wondering how it works. Here is code

CoreModule:
import {
  ModuleWithProviders, NgModule,
  Optional, SkipSelf }       from '@angular/core';

import { CommonModule }      from '@angular/common';


import { HeaderModule } from './modules/header/header.module';
import { FooterComponent } from './modules/footer/footer.component';


//THAT DOESNT WORK
// import { CommunicatePatientListService }    from './services/communicate_patients_list.service';
// import { HTTPPatientsListService }    from './services/http_patients_list.service';
// import { SharedService }    from './services/shared_service';
// import { SchedulerSharedService }    from './services/scheduler_shared.service';
// import { FormChangeService }    from './services/on_form_change.service';

//IT IS WORKING NOW
import { CommunicatePatientListService } from '%sharedServices%/communicate_patients_list.service';
import { HTTPPatientsListService } from 'httpPatientsListService';
import { SharedService } from 'sharedService';
import { SchedulerSharedService } from 'schedulerSharedService';
import { FormChangeService } from 'formChangeService';



@NgModule({
  imports:      [
    CommonModule,
    HeaderModule,
  ],
  declarations: [
    FooterComponent
  ],

  exports: [
    FooterComponent,
    HeaderModule
  ]
})
export class CoreModule {

  constructor (@Optional() @SkipSelf() parentModule: CoreModule) {
    if (parentModule) {
      throw new Error(
        'CoreModule is already loaded. Import it in the AppModule only');
    }
  }

  static forRoot(): ModuleWithProviders {
    return {
      ngModule : CoreModule,
      providers:    [
        CommunicatePatientListService,
        HTTPPatientsListService,
        SharedService,
        FormChangeService,
        SchedulerSharedService
      ]
    };
  }


}

Patients List component

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

import { CommunicatePatientListService } from '%sharedServices%/communicate_patients_list.service';
    import { HTTPPatientsListService } from 'httpPatientsListService';
    import { SharedService } from 'sharedService';
    import { SchedulerSharedService } from 'schedulerSharedService';
    import { FormChangeService } from 'formChangeService';

    import { Config } from 'appConfig';
    /* ------- !Config  ---------*/

    const MODULE_NAME: string = 'patients_list';
    const MODULE_PATH: string = `${Config.getProdFolderName()}/modules/patients/${MODULE_NAME}`;


    @Component({
      templateUrl: `${MODULE_PATH}/${MODULE_NAME}.component.html`,
      styleUrls: [`${MODULE_PATH}/${MODULE_NAME}.component.css`,]
    })


    export class PatientsListComponent implements OnInit {
      constructor(
        private patientsListService:HTTPPatientsListService,
        private patsListServCom:CommunicatePatientListService,
        private schedulerSharedService:SchedulerSharedService,
        private sharedService:SharedService
      ) {
  }
Cobus Kruger
  • 8,338
  • 3
  • 61
  • 106
Velidan
  • 5,526
  • 10
  • 48
  • 86

1 Answers1

19

The best approach is to create module with providers. Keep in mind that if your service is in shared module, you may get multiple instances of it. Best idea then is to configure module with Module.forRoot.

By convention, the forRoot static method both provides and configures services at the same time. It takes a service configuration object and returns a ModuleWithProviders.

Here is full Doc about it.

Call forRoot only in the root application module, AppModule. Calling it in any other module, particularly in a lazy loaded module, is contrary to the intent and is likely to produce a runtime error.

Remember to import the result; don't add it to any other @NgModule list.

@NgModule({
    imports: [CommonModule],
    declarations: [SomeComponent],
    exports: [SomeComponent]
})
export class CoreModule {
    static forRoot(): ModuleWithProviders {
        return {
            ngModule: CoreModule,
            providers: [SomeService]
        };
    }
}

Then import module looks like:

@NgModule({
  imports: [
    /** other modules import **/
    CoreModule.forRoot(), // you can also pass some config here if needed
    routing
  ],
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

If you want to prevent reimport of the CoreModule

Only the root AppModule should import the CoreModule. Bad things happen if a lazy loaded module imports it.

@NgModule({
  imports:      [ CommonModule ],
  declarations: [ SomeComponent ],
  exports:      [ SomeComponent ],
})
export class CoreModule {

  constructor (@Optional() @SkipSelf() parentModule: CoreModule) {
    if (parentModule) {
      throw new Error(
        'CoreModule is already loaded. Import it in the AppModule only');
    }
  }

  static forRoot(config: UserServiceConfig): ModuleWithProviders {
    return {
      ngModule: CoreModule,
      providers: [SomeService]
    };
  }
}
vardius
  • 6,326
  • 8
  • 52
  • 97
  • Vardious thatns for your help. Could you check my update? I tried to apply this stuff but it doesn't work somehow. Thanks! – Velidan Oct 06 '16 at 08:59
  • now what module are you using ? importing `CoreModule.forRoot()` ansures that your services will be set up all right. and visible across all aplication. Now if you want to use components, or exported modules. you have to import this module also in module you want to use it (if this module is not AppModule where you already imported this) then you import this module without `forRoot` method. And then you will be able to use components in this module also. – vardius Oct 06 '16 at 09:03
  • I used patientsModule that have routing (and it is LazyLoaded). Inside that module I tried to use service from CoreModule. But I got an error that I showed above. When services was provided directly in AppModule that was working fine. Maybe it impossible to provide singletons via CoreModule to whole application? – Velidan Oct 06 '16 at 09:07
  • While many components share the same service instances, they rely on Angular dependency injection to do this kind of sharing, not the module system. Do not specify app-wide singleton providers in a shared module. A lazy loaded module that imports that shared module will make its own copy of the service. If CoreModule provides the HTTPPatientsListService. Angular registers that provider with the app root injector, making a singleton instance of the HTTPPatientsListService available to any component that needs it, whether that component is eagerly or lazily loaded. – vardius Oct 06 '16 at 09:13
  • Okay, So if I udnerstand you correct, to make service REAL singleton for any component (lazy or not) I should provide it it AppModule (RootModule)? Is it correct? But why exactly CoreModule was created?What stuff can be provided there if I should provde services still in AppModule? I thought that inside that module (CoreModule) I can provide all singletons and cleanup my AppModule... – Velidan Oct 06 '16 at 09:27
  • You need to read whole doc i provided. It will answer a lot of questions you have right now. [Doc](https://angular.io/docs/ts/latest/guide/ngmodule.html) as it says I quote: "At the moment, our root folder is cluttered with the UserService and the TitleComponent that only appears in the root AppComponent. We did not include them in the SharedModule for reasons just explained. Instead, we'll gather them in a single CoreModule that we import once when the app starts and never import anywhere else." – vardius Oct 06 '16 at 09:38
  • I saw it, but I don't udnerstand. So best place for singletones it still APp module? (Because I can't move it to SharedModule because in the doc I saw that ng2 will create different instance of the same service in case of Lazy Component.). Sorry for stupid question but I still don't understand that moment. – Velidan Oct 06 '16 at 09:54
  • Looks like yes, just like oldschool. Thanks so much Vardious for your help. – Velidan Oct 06 '16 at 10:07
  • It is easy, it should not create multiple instances if you use `foorRoot` method, and import that module at AppComponent will make this services register in that method available across all aplication. You import module with that method only once, in the AppModule. Later if you want to sue shared components, you import SharedModule without `forRoot` function – vardius Oct 06 '16 at 10:13
  • Sounds pretty simple and cool :) But it doesn't pupulate singleton services for whole app (I showed it above). – Velidan Oct 06 '16 at 10:24
  • can you paste `PatientsListComponent` ? – vardius Oct 06 '16 at 10:32
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/125092/discussion-between-vardius-and-velidan). – vardius Oct 06 '16 at 10:39
  • Yes, sure. Take a look please. I found an interesting solution for that but I don't know why it works now :) – Velidan Oct 06 '16 at 11:55
  • If it works, fine :) i would ahve to see project structer to get the idea what have changed. I hope you have only one service file. – vardius Oct 06 '16 at 11:58
  • @Vardius what needs to be done in order to access a pipe from a shared module (imported with .forRoot() in the AppModule) in a lazy loaded one? Services seems to work, but not pipes – adamfinstorp Sep 21 '17 at 09:31