Angular2: Referencing configurable module from other modules

687 views Asked by At

My main AppModule has recently grown significantly so I want to cut it into multiple smaller modules. As the first step, I took a number of login / registration components and placed them in LoginModule. There is one problem, though. Components that I've placed in LoginModule depends on another module that is declared and configured in the main AppModule. This required module accepts a number of configuration parameters and of course, I'd like to have it configured in one single place.

As an example, let's take IonicModule but this question is Angular2-generic.

My main AppModule imports IonicModule and LoginModule as well as provides a number of parameters to the IonicModule.

imports: [
  IonicModule.forRoot(MyApp, {
    mode: 'ios',
    tabsPlacement: 'bottom'
  }),
  LoginModule
]

Because all components in LoginModule depends on IonicModule I need to import IonicModule there as well. What should I do with configuration parameters, though? I'd like to avoid copy-pastying them from AppModule.

Is imports: [IonicModule] in this case enough? Or do I need to extract configuration parameters somewhere and inject them every time I refer IonicModule ?

All other modules that I will extract, will need to have IonicModule imported as well.

@NgModule({
  imports: [IonicModule],  
  declarations: [
    (...)
  ],
  entryComponents: [
    (...) 
  ],
  providers: (...)
})
export class LoginModule {}
2

There are 2 answers

0
Suraj Rao On

You could take a look at shared modules section here

Theoretically,you could set the common dependancy modules in one module , export from there and import that. That way you could still set the parameters in one place. However , I havent tried doing this with a module that takes the app component in forRoot.

@NgModule({ imports: [IonicModule.forRoot(),//other deps with parameters], 
declarations: [ (...) ], entryComponents: [ (...) ], 
providers: (...) ,
exports:[//export deps]})
 export class commonModule {}

Hope it helps

1
adriancarriger On

Core Module

What you're looking for is to create a core module which is recommended by the official Angular Style Guide.

Here is how you can refactor your app to meet the style guide, keep all of your config in one place, and create a very organized and lean app:

1. Create a Core Module

To reduce root folder clutter create a Core Module that we import once when the app starts and never import anywhere else.

Steps:

  • create an app/core folder
  • move root services and components from app/ to app/core (e.g. IonicModule)
  • create a CoreModule class to own the core material

Example:

import {
  ModuleWithProviders, NgModule,
  Optional, SkipSelf }       from '@angular/core';
import { CommonModule }      from '@angular/common';
import { IonicModule } from 'ionic-angular';

import { ExampleComponent }    from './example.component';
import { ExampleService }       from './example.service';
@NgModule({
  imports:      [
    CommonModule,
    IonicModule.forRoot(MyApp, {
      mode: 'ios',
      tabsPlacement: 'bottom'
    })
  ],
  declarations: [ ExampleComponent ],
  exports:      [ ExampleComponent ]
})
export class CoreModule {
}
  • Remember: Any module that you need to be available app-wide that uses a forRoot() method only needs to be imported once into the CoreModule. After doing this, it will be available to every module in your app.

2. Add Providers to CoreModule (forRoot)

Put this in the CoreModule class:

static forRoot(): ModuleWithProviders {
  return {
    ngModule: CoreModule,
    providers: [
      ExampleService
    ]
  };
}

This is the only place you should put app-wide singleton providers. Do not place them in a SharedModule.

3. Import the CoreModule into the AppModule

Add CoreModule.forRoot() to your AppModule imports like this:

imports: [
  AppRoutingModule,      
  BrowserModule,
  CoreModule.forRoot()
],

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.

4. Prevent reimport of CoreModule (optional, but recommended)

At this point everything should work, but it's a best practice to prevent reimporting the CoreModule. This is especially helpful when working with others because another developer may not be fully aware of how a CoreModule should be used.

Add this to your CoreModule's class:

constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
  throwIfAlreadyLoaded(parentModule, 'CoreModule');
}

Further considerations

  • Lazy-loaded routes Consider creating lazy-loaded routes. This way the initial load of your app can be faster by only loading what is required by the current route.
  • SharedModule Consider creating a SharedModule that does not include app-wide services. You can put components in here that will be required by certain routes, but not all routes.
  • Why? The purpose of this is that if you load a lazy-loaded route that does not require a component of the shared service it will not download if that is the first route requested when the app first loads. This will speed up your initial app loading time. Of course, if a route is called that needs it, then the component will be available as expected.