Velidan Velidan - 1 month ago 31
TypeScript Question

Provide core singleto services module at Angular 2

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
) {
}

Answer

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]
    };
  }
}
Comments