Angular ~ Module - rohit120582sharma/Documentation GitHub Wiki

Table of contents

  • Introduction
    • A module is a container or you can say it’s a logical grouping of components and other services. Turn the class into an Angular module just by using the NgModule decorator.
  • Root module
    • The RootModule contains code that will be used to instantiate the app and load some core functionality. Every Angular app has only one root module which is bootstrapped to launch the application.
  • Shared module
    • The SharedModule should contain common components/pipes/directives/services that you want to reuse in your application. Always use the forRoot syntax when exporting services from the shared module.
  • Feature module
    • It defines a particular feature of an application and packages functionality belong to the feature. It helps to make root module leaner and easier only.
    • It is platform independent. That's why only import the CommonModule there, which only exports common directives and pipes.
    • Each feature module needs to be imported into our simplified root module.
  • Lazy loaded Feature module
    • Modules that are lazily loaded will only be loaded when the user navigates to their routes. They help us decrease the startup time.

Introduction

In Angular, a module is a mechanism to group components, directives, pipes and services that are related, in such a way that can be combined with other modules to create an application.

A module can export or hide components, directives, pipes, and modules. The exported elements are meant to be used by other modules, while the ones that are not exported (hidden) are just used inside the module itself and cannot be directly accessed by other modules of our application.

Modules are a great way to organize an application and extend it with capabilities from external libraries. Many Angular libraries are modules (such as FormsModule, HttpClientModule, and RouterModule).

There are two types of modules, root modules and feature modules. If the module is importing the BrowserModule then it's a root module, if instead is importing the CommonModule then it is a feature module.

Every Angular app has at least one module class, the root module. You bootstrap that module to launch the application.


Anatomy of an Angular Module

To create a module we need to use import NgModule from @angular/core. An NgModule is a class adorned with the @NgModule decorator function. This helps us to define module directives.

@NgModule takes a metadata object that do the following:

  • declarations

    • Declare which components, directives, and pipes belong to the module.
  • imports

    • Import other modules with the components, directives, and pipes needed by the components in this module.
    • For example, BrowserModule re-exports CommonModule, which makes available the built-in NgIf and NgFor directives. FormsModule provides directive functionality like ngModel, expressions and so on.
    • BrowserModule, FormsModule, HttpClientModule, RouterModule, and BrowserAnimationsModule are examples of commonly imported modules.
  • exports

    • If you want to export members of the module so that they can be used in component templates of other modules, these members would go in the exports array.
    • Make some of those classes public so that other component templates can use them.
  • bootstrap

    • This section defines the first component which will run. For example we can have HomeComponent or RootComponent.
  • providers

    • This is where injectables go. Things like services, route guards and http interceptors are example of injectables that should be part of the providers array.
    • Provide services at the application level that any application component can use.
  • entryComponents

    • This is for components that can’t be found by the Angular compiler during compilation time because they are not referenced anywhere in component templates. It’s common for routed components not to be referenced at all in templates, but Angular takes care of adding those to entryComponents automatically, so there’s no need to add those yourself.
    • Components that should go into entryComponents are not that common. A good example would be Angular Material dialogs, because they are created dynamically, and the Angular compiler wouldn’t know about them otherwise.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from "@angular/forms"
import { HomeComponent } from '../Component/HomeComponent';
import { CustomerComponent } from '../Component/CustomerComponent';

@NgModule({
	imports: [BrowserModule, FormsModule],
	providers: [],
	declarations: [HomeComponent, CustomerComponent],
	bootstrap: [HomeComponent]
})
export class MainModule{
}

Types of Module

Root Module

The RootModule contains code that will be used to instantiate the app and load some core functionality. Every Angular app has only one root module which is bootstrapped to launch the application.

It imports BrowserModule which is a built-in module that exports basic directives, pipes, services and CommonModule that helps with rendering an Angular application in the browser.

These services are coupling our root module with a particular platform (the browser).

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule  } from '@angular/platform-browser';
// modules
import { SharedModule } from './shared.module';
import { AppRoutingModule } from './app-routing.module';
// components
import { AppComponent } from './app.component';
import { EagerComponent } from './eager.component';

@NgModule({
	declarations: [AppComponent, EagerComponent],
	providers: [],
	imports: [BrowserModule, SharedModule.forRoot(), AppRoutingModule],
	exports: [],
	bootstrap: [AppComponent]
})
export class AppModule {
}

app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule, PreloadAllModules } from '@angular/router';
// components
import { EagerComponent } from './eager.component';

const APP_ROUTES: Routes = [
	{ path: 'eager', component: EagerComponent },
	{ path: 'lazy', loadChildren: './lazy.module#LazyModule' },
	{ path: '', redirectTo: 'eager', pathMatch: 'full' }
];

@NgModule({
	imports: [RouterModule.forRoot(APP_ROUTES, {enableTracing:true, preloadingStrategy:PreloadAllModules})],
	exports: [RouterModule]
})
export class AppRoutingModule {
}

app.component.ts

import { Component } from '@angular/core';

@Component({
	selector: 'app-root',
	template: `
		<h1>My App</h1>
		<nav>
			<a [routerLink]="['/eager']">Eager</a>
			<a [routerLink]=['/lazy']>Lazy</a>
		</nav>
		<router-outlet></router-outlet>
	`
})
export class AppComponent {
}

eager.component.ts

import { Component } from '@angular/core';
// services
import { CounterService } from './counter.service';

@Component({
	template: `
		<p appHighlight>Eager Component</p>
		<button (click)="increaseCounter()">Increase Counter</button>
		<p>Counter: {{ _counterService.counter }}</p>
	`
})
export class EagerComponent {
	constructor(private _counterService: CounterService){
	}
	increaseCounter(){
		this._counterService.counter += 1;
	}
}

Shared Module

The SharedModule should contain common components/pipes/directives/services that you want to reuse across your application.

Note the following:

  • It may import the CommonModule and FormsModule because the module's component needs respective directives.
  • It declares and exports the utility pipe, directive, and component classes.
  • It re-exports the CommonModule and FormsModule. By re-exporting CommonModule and FormsModule, any other module that imports this SharedModule, gets access to directives like NgIf and NgFor from CommonModule and [(ngModel)] from FormsModule.
  • To have only one instance of a shared service across the root, an eagerly or lazy loaded module.
    • Instead of defining the service in the providers property, create a static method called forRoot that exports the service along with the module itself.
    • With this setup, import this module in the root module calling the forRoot method to register the module and the service. This ensures that only a single instance of the service exists at the root level.
    • Import the module in lazy module normally. Calling forRoot in feature module can register the service again in a different level of the DI tree.

shared.module.ts

import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
// services
import { CounterService } from './counter.service';
// components, directives
import { HighlightDirective } from './highlight.directive';

@NgModule({
	declarations: [HighlightDirective],
	providers: [],
	imports: [CommonModule, FormsModule],
	exports: [CommonModule, FormsModule, HighlightDirective]
})
export class SharedModule {
	static forRoot(): ModuleWithProviders {
		return {
			ngModule: SharedModule,
			providers: [CounterService]
		};
	}
}

counter.service.ts

import { Injectable } from '@angular/core';

@Injectable()
export class CounterService {
	counter: number = 0;
}

highlight.directive.ts

import { Directive, ElementRef, Renderer2 } from '@angular/core';

@Directive({
	selector: '[appHighlight]'
})
export class HighlightDirective {
	constructor(private _renderer: Renderer2, private _el: ElementRef){
		_renderer.setElementStyle(el.nativeElement, 'backgroundColor', 'blue');
		_renderer.setElementStyle(el.nativeElement, 'color', 'gray');
	}
}

Lazy Loaded Feature Module

Modules that are lazily loaded will only be loaded when the user navigates to their routes. In other words, this module is only loaded if a route leading to this module is visited.

With lazy loading, our application does not need to load everything at once. So lazy loading modules help us decrease the startup time.

The lazy loaded module creates its own branch on the Dependency Injection (DI) tree. This means that it's possible to have services that belong to a lazy loaded module, that are not accessible by the root module or any other eagerly loaded module of our application.

There's a few important things to notice here:

  • We use the property loadChildren instead of component in routes configuration.
  • We pass a string instead of a symbol to avoid loading the module eagerly.
  • We define not only the path to the module but the name of the class as well.

lazy.module.ts

import { NgModule } from '@angular/core';
// modules
import { SharedModule } from './shared.module';
import { LazyRoutingModule } from './lazy-routing.module';
// services
import { LazyCounterService } from './lazy-counter.service';
// components
import { LazyComponent } from './lazy.component';

@NgModule({
	declarations: [LazyComponent],
	providers: [LazyCounterService],
	imports: [SharedModule, LazyRoutingModule],
	exports: []
})
export class LazyModule {
}

lazy-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
// components
import { LazyComponent } from './lazy.component';

// Routes configuration
const LAZY_ROUTES: Routes = [
	{ path: '', component: LazyComponent }
];

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

lazy-counter.service.ts

import { Injectable } from '@angular/core';

@Injectable()
export class LazyCounterService {
	counter = 0;
}

lazy.component.ts

import { Component } from '@angular/core';
// services
import { CounterService } from './counter.service';
import { LazyCounterService } from './lazy-counter.service';

@Component({
	template: `
		<p appHighlight>Lazy Component</p>
		<button (click)="increaseCounter()">Increase Counter</button>
		<p>Counter: {{ _counterService.counter }}</p>
		<p>Lazy Counter: {{ _lazyCounterService.counter }}</p>
	`
})
export class LazyComponent {
	constructor(private _counterService: CounterService, private _lazyCounterService: LazyCounterService){
	}
	increaseCounter(){
		this._counterService.counter += 1;
		this._lazyCounterService.counter += 1;
	}
}
⚠️ **GitHub.com Fallback** ⚠️