a blog for those who code

Monday 8 April 2019

Understanding Bits and Pieces of Modules in Angular

In simple terms Module is a place to declare or register each and every elements you create in Angular like Components, Directives, Pipes, Services etc, which makes up your app and group them together. The main idea behind registering elements in Modules is to tell Angular what your app is consists of, which components you use, which services you require etc.

Every Angular App should have at-least one Angular Module i.e. AppModule. The Angular CLI creates that for you whenever you create a new app. A simple App Modules (app.module.ts) looks like :

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent],
  exports: []
})

export class AppModule { }

All the Angular Modules are marked by the @NgModule decorator which takes a metadata object to identify module's own components, directives or pipes as well as importing other modules to use it in its own component. Since App Module is for whole app then the metadata definition is :

declarations - We define which components, directives or pipes our app uses
imports - We define which other modules our app uses
providers - We define which services we use in our app
bootstrap - References the component to bootstrap the application with i.e. entry point
exports - Enables Angular Module to export some of its components, directives and pipes to other modules.

Why Pipes are Useful in Angular and How to Create Custom Pipes


Note: Declarations Array is Private to the Component i.e. local scope whereas Providers Array are of global scope. That means all the component, directives and pipes defined in the declarations will only be used inside the component whereas services defined in the providers array is available and injectable anywhere in your app.

When to Create Custom Module


Lets say your app is divided into a lot of features and every features have its own set of component, directives etc. Then it's a good practice to create a Feature Module which consists of only those component/directives which is used by that feature. Custom Module can also be used to speed up your application by doing Lazy Loading of that module i.e. loading when required.

When to use [routerLink] or routerLink in Angular


Custom Routing Module


A very common module which most of the developers create is a Routing Module which holds all the routes of the application:

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

const routes: Routes = [
  // Your Routes array
];

@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule],
    providers: []
})

export class RootRoutingModule { }

And then we can inject this in imports array of the main module or at the feature module.

forRoot and forChild


As you can see above we have used RouterModule.forRoot(routes) which gives us the Router Service and Directives such as RouterOutlet and routerLink. Thus when we use the RouterModule on the App we should use forRoot() which gets the Router Service and to keep it Singleton (one instance of service) all the feature modules should import RouterModule.forChild() which does not instantiate another Router.

Shared Modules


Let's say you have a Directive which will be used by 2 or more feature module, then you can add that directive in a Shared Module. First thing to note that any component or directive can only be declare once, means at-least once. Now to use it in multiple modules you need to import it from some module which has declared it. That's why the idea behind having Shared Module is that you declare the directive and then export it as shown below.

@NgModule({
    declarations: [MyDirective],
    exports: [CommonModule, MyDirective],
})

export class SharedModule { }

Lazy Loading Modules


When you load an application for a user, you don't want to download all your code on the first visit because might be user will not navigate to all the features of your app. This is where Lazy Loading will come into picture where you load the modules only when required instead of loading all at once on the startup of the app for the user.

Understanding Dependency Injection in Angular


To load the modules lazily, you need to change your routing like below :

const routes: Routes = [
  {
    path: 'first'
    loadChildren: './first/first.module#FirstModule'
  },
  {
    path: 'second'
    loadChildren: './second/second.module#SecondModule'
  }
];

In the above code the paths are the routes for two modules First and Second, and we are lazy loading the routes using loadChildren by providing a relative path to the module with the module's class name after the #.

Note : If you have a service injected in a Lazy Loaded Module then it will create an instance of that service and not use the global instance of the Service whereas for a normal module the same instance of the service is used.

Also if you have a Shared Module and you have a service injected in it and you are using it in both normal module and lazy loaded module then two instances of the same Service will be created, once which will be attached to the root i.e. global whereas one instance which will be used only by the Lazy Loaded Module. So you should not provide services in Shared Module if you are using that module in Lazy Loaded Modules.
Career Category (English)728x90

No comments:

Post a Comment