Saturday 2 February 2019

Creating feature routing module in angular

Creating feature routing module in angular


In this topic we will discuss  
  • Creating a separate routing module for a feature module. 
  • RouterModule forRoot vs forChild

At the moment, all our application routes, including the EMPLOYEE feature module routes like LIST, CREATE, EDIT are present in AppRoutingModule. This module is in app-routing.module.ts file. 

For separation of concerns and ease of maintenance, here is what we want to do. 
  • Include all the application level routes like HOME, EMPTY PATH & WILD CARD routes in the AppRoutingModule
  • Include all the EMPLOYEE feature module routes like LIST, CREATE & EDIT in a separate EmployeeRoutingModule
Creating Employee feature routing module:
Step 1 : In the "employee" folder create a new file with name employee-routing.module.ts. The following is the common naming convention used when creating a separate routing module for a feature module.

Feature routing module file name convention
featureModule-routing.module.ts

Feature routing module class name convention
FeatureModuleRoutingModule

For example if your feature module name is employee, then the  
  • Routing Module File Name is employee-routing.module.ts
  • Routing Module Class Name is EmployeeRoutingModule
Step 2 : Copy and paste the following code in employee-routing.module.ts file. The code is commented and self-explanatory. 

import { NgModule } from '@angular/core';
// Import RouterModule & Routes type
import { RouterModule, Routes } from '@angular/router';

// Import all the components that we will be referencing in the route definitions
import { CreateEmployeeComponent } from './create-employee.component';
import { ListEmployeesComponent } from './list-employees.component';

// Define the routes
const appRoutes: Routes = [
  { path: 'list', component: ListEmployeesComponent },
  { path: 'create', component: CreateEmployeeComponent },
  { path: 'edit/:id', component: CreateEmployeeComponent },
];

// In a feature module forChild() method must be used to register routes
// Export RouterModule, so the it's directives like RouterLink, RouterOutlet
// are available to the EmployeeModule that imports this module
@NgModule({
  imports: [ RouterModule.forChild(appRoutes) ],
  exports: [ RouterModule ]
})
export class EmployeeRoutingModule { }

RouterModule forRoot vs forChild

forRoot() method registers the specified routes. It also creates an instance of the Router service and registers it with the angular's dependency injector.

forChild() method on the other hand only registers the additional specified routes and tells angular to reuse the Router service instance that forRoot has created.

Angular services are singletons. So, to ensure that, there is only one instance of Router service, forRoot() method should be called only once in the main application routing module. 

In all the feature routing modules forChild() method should be used to register the additional routes. When the forChild() method is called, Angular Router knows it has to only register the additional specified routes and not to re-register the Angular Router service.

Step 3 : Import EmployeeRoutingModule into EmployeeModule 

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';

// Import the EmployeeRoutingModule
import { EmployeeRoutingModule } from './employee-routing.module';

import { CreateEmployeeComponent } from './create-employee.component';
import { ListEmployeesComponent } from './list-employees.component';

@NgModule({
  imports: [
    CommonModule,
    ReactiveFormsModule,
    // Add EmployeeRoutingModule to the imports array
    EmployeeRoutingModule
  ],
  declarations: [
    CreateEmployeeComponent,
    ListEmployeesComponent
  ]
})
export class EmployeeModule { }

Step 4 : Remove the EMPLOYEE feature module routes from AppRoutingModule (app-routing.module.ts) 

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { HomeComponent } from './home.component';
import { PageNotFoundComponent } from './page-not-found.component';

const appRoutes: Routes = [
  { path: 'home', component: HomeComponent },
  { path: '', redirectTo: '/home', pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent }
];

@NgModule({
  imports: [ RouterModule.forRoot(appRoutes) ],
  exports: [ RouterModule ]
})
export class AppRoutingModule { }

All angular feature modules should be imported before AppRoutingModule 

At this point if you launch the application, except the HOME route, all the routes (LIST, CREATE, EDIT) displays the PageNotFoundComponent template. 

When you comment the following WILD CARD route in the AppRoutingModule, the rest of the routes (LIST, CREATE, EDIT) work. If you uncomment the WILD CARD route, the EMPLOYEE feature module routes stop working again. 

const appRoutes: Routes = [
  { path: 'home', component: HomeComponent },
  { path: '', redirectTo: '/home', pathMatch: 'full' },
  // { path: '**', component: PageNotFoundComponent }
];

Why are the EMPLOYEE feature module routes (LIST, CREATE, EDIT) not working
This is because of the order in which AppRoutingModule and EmployeeModule are imported in the root module AppModule. Notice at the moment, AppRoutingModule is imported before EmployeeModule in the AppModule. 

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    PageNotFoundComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    EmployeeModule
  ],
  providers: [EmployeeService],
  bootstrap: [AppComponent]
})
export class AppModule { }

Because AppRoutingModule is imported before EmployeeModule, the routes will be ordered as shown below. Notice, the WILD CARD route is before all the EMPLOYEE feature module routes. Because of this we will never be able to get to the LIST,CREATE and EDIT routes. 

{ path: 'home', component: HomeComponent },
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: '**', component: PageNotFoundComponent },
{ path: 'list', component: ListEmployeesComponent },
{ path: 'create', component: CreateEmployeeComponent },
{ path: 'edit/:id', component: CreateEmployeeComponent },

To fix this all we have to do is, change the import order of the modules in the AppModule. Import EmployeeModule before AppRoutingModule as shown below. 

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    PageNotFoundComponent
  ],
  imports: [
    BrowserModule,
    EmployeeModule,
    AppRoutingModule,
    HttpClientModule,
  ],
  providers: [EmployeeService],
  bootstrap: [AppComponent]
})
export class AppModule { }

With the above change the routes will be ordered as shown below and all our EMPLOYEE feature module routes work as expected. For this reason all feature modules should be imported before AppRoutingModule

{ path: 'list', component: ListEmployeesComponent },
{ path: 'create', component: CreateEmployeeComponent },
{ path: 'edit/:id', component: CreateEmployeeComponent },
{ path: 'home', component: HomeComponent },
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: '**', component: PageNotFoundComponent },

No comments:

Post a Comment