Creating Feature Modules in Angular

Angular is a modular framework that allows developers to organize their application into smaller, cohesive units called modules. While the AppModule serves as the root module, Angular also supports feature modules to encapsulate functionality, manage routing, and maintain separation of concerns. Creating feature modules improves scalability, maintainability, and allows for lazy loading.

This article provides a comprehensive guide to creating feature modules using Angular CLI, organizing components and services, and configuring routing with a real-world example such as UserModule or AdminModule.


What is a Feature Module?

A feature module is an Angular module that groups related components, services, directives, and pipes. Unlike the root AppModule, feature modules focus on specific functionality or domain logic. For example:

  • UserModule – Handles user registration, profile management, and authentication.
  • AdminModule – Handles administrative tasks like dashboard, user management, and reporting.

Benefits of Feature Modules

  1. Encapsulation – Components, services, and directives specific to a feature are contained within the module.
  2. Reusability – Modules can be imported into multiple applications if needed.
  3. Lazy Loading – Load feature modules on demand to improve performance.
  4. Maintainability – Easier to manage a large application by dividing it into smaller functional units.

Step 1: Creating a Feature Module Using Angular CLI

Angular CLI makes it simple to generate a feature module. The basic command is:

ng generate module feature-name

For example, to create a UserModule:

ng generate module user

Explanation:

  • This creates a folder user/ with user.module.ts.
  • The module is automatically decorated with @NgModule.
  • Initially, it contains an empty declarations array and no routing configuration.

Creating a Module with Routing

Feature modules often need their own routing. Use the --routing flag:

ng generate module user --routing

Generated files:

user/
├── user.module.ts
└── user-routing.module.ts
  • user.module.ts – Declares and imports the module’s components and other dependencies.
  • user-routing.module.ts – Contains the module-specific routes.

Step 2: Organizing Components Within the Feature Module

After creating a feature module, the next step is to add components that belong to the module.

Creating Components Using CLI

ng generate component user/components/user-list
ng generate component user/components/user-detail

Folder Structure Example:

user/
├── components/
│   ├── user-list/
│   │   └── user-list.component.ts
│   └── user-detail/
│       └── user-detail.component.ts
├── services/
│   └── user.service.ts
├── user.module.ts
└── user-routing.module.ts

Explanation:

  • Components related to users are placed under components/.
  • Services specific to user functionality go under services/.
  • The module file declares and exports components that may be used within the module.

Declaring Components in the Module

// user.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { UserRoutingModule } from './user-routing.module';
import { UserListComponent } from './components/user-list/user-list.component';
import { UserDetailComponent } from './components/user-detail/user-detail.component';

@NgModule({
  declarations: [
UserListComponent,
UserDetailComponent
], imports: [
CommonModule,
FormsModule,
UserRoutingModule
] }) export class UserModule { }

Explanation:

  • declarations – Lists all components, directives, and pipes used in this module.
  • imports – Includes Angular common modules (CommonModule, FormsModule) and routing module.

Step 3: Organizing Services Within the Feature Module

Services encapsulate business logic and data handling. Feature module services can be provided in the module or globally using providedIn: 'root'.

Example: User Service

// user/services/user.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export interface User {
  id: number;
  name: string;
  email: string;
}

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private apiUrl = 'https://jsonplaceholder.typicode.com/users';

  constructor(private http: HttpClient) {}

  getUsers(): Observable<User[]> {
return this.http.get&lt;User&#91;]&gt;(this.apiUrl);
} getUserById(id: number): Observable<User> {
return this.http.get&lt;User&gt;(${this.apiUrl}/${id});
} }

Explanation:

  • The service is global (providedIn: 'root') but could also be provided only in UserModule.
  • It contains methods to fetch user data from an API.

Step 4: Configuring Feature Module Routing

Routing allows navigation between components within a feature module. The user-routing.module.ts file defines module-specific routes.

Example User Routing

// user-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { UserListComponent } from './components/user-list/user-list.component';
import { UserDetailComponent } from './components/user-detail/user-detail.component';

const routes: Routes = [
  { path: '', component: UserListComponent },
  { path: ':id', component: UserDetailComponent }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class UserRoutingModule { }

Explanation:

  • RouterModule.forChild(routes) – Registers routes specific to the UserModule.
  • path: '' – Default route for the module, displaying the user list.
  • path: ':id' – Dynamic route to view user details.

Step 5: Importing Feature Module in App Module

To use the feature module in the main application, import it into AppModule. For eager loading, do:

// app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { UserModule } from './user/user.module';
import { HttpClientModule } from '@angular/common/http';

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

For lazy loading, configure routes in AppRoutingModule:

// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  {
path: 'users',
loadChildren: () =&gt; import('./user/user.module').then(m =&gt; m.UserModule)
}, { path: '', redirectTo: '/users', pathMatch: 'full' } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }

Explanation:

  • Lazy loading ensures UserModule is loaded only when the user navigates to /users.
  • Improves initial load performance of the application.

Step 6: Real-World Example: UserModule

User List Component

// user-list.component.ts
import { Component, OnInit } from '@angular/core';
import { UserService, User } from '../../services/user.service';

@Component({
  selector: 'app-user-list',
  template: `
&lt;h2&gt;User List&lt;/h2&gt;
&lt;ul&gt;
  &lt;li *ngFor="let user of users"&gt;
    &lt;a &#91;routerLink]="&#91;user.id]"&gt;{{ user.name }}&lt;/a&gt;
  &lt;/li&gt;
&lt;/ul&gt;
` }) export class UserListComponent implements OnInit { users: User[] = []; constructor(private userService: UserService) {} ngOnInit(): void {
this.userService.getUsers().subscribe(data =&gt; this.users = data);
} }

User Detail Component

// user-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { UserService, User } from '../../services/user.service';

@Component({
  selector: 'app-user-detail',
  template: `
&lt;h2&gt;User Detail&lt;/h2&gt;
&lt;div *ngIf="user"&gt;
  &lt;p&gt;Name: {{ user.name }}&lt;/p&gt;
  &lt;p&gt;Email: {{ user.email }}&lt;/p&gt;
&lt;/div&gt;
` }) export class UserDetailComponent implements OnInit { user!: User; constructor(private route: ActivatedRoute, private userService: UserService) {} ngOnInit(): void {
const id = Number(this.route.snapshot.paramMap.get('id'));
this.userService.getUserById(id).subscribe(data =&gt; this.user = data);
} }

Explanation:

  • UserListComponent fetches and displays a list of users.
  • Clicking a user navigates to UserDetailComponent.
  • UserDetailComponent retrieves the id from the route and fetches the user details.

Step 7: Organizing Feature Module Files

A clean file structure for UserModule:

user/
├── components/
│   ├── user-list/
│   │   └── user-list.component.ts
│   └── user-detail/
│       └── user-detail.component.ts
├── services/
│   └── user.service.ts
├── user.module.ts
└── user-routing.module.ts

Explanation:

  • Components are under components/.
  • Services under services/.
  • Routing separated in user-routing.module.ts for clarity.

Best Practices for Feature Modules

  1. Encapsulate Related Functionality – Keep components, services, and routing related to a feature together.
  2. Use Routing Module – Separate routing logic into a dedicated module for clarity.
  3. Prefer Lazy Loading – Load feature modules only when needed.
  4. Declare Only Feature-Specific Components – Avoid importing global components unnecessarily.
  5. Organize Folder Structure – Use components/, services/, and pipes/ folders to keep modules clean.
  6. Provide Services Carefully – Decide between providedIn: 'root' or module-level service provisioning.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *