Author avatar

Chris Parker

Declaring Components in an Angular Module

Chris Parker

  • Mar 27, 2019
  • 9 Min read
  • 923 Views
  • Mar 27, 2019
  • 9 Min read
  • 923 Views
Client-side Frameworks
Angular

Introduction

An Angular module can be thought of as a declaration of all the things that would be used/needed in any section of our application. Let us have a look at what's there inside of the AppModule. AppModule is generated by the Angular CLI and it contains everything required for a working application shell:

1
2
3
4
5
6
7
8
9
10
11
12
//app.module.ts
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {AppComponent} from './app.component';

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

Here, the use of @NgModule annotation actually defines our module.

Additionally, a module is essentially a declaration of all the things which will be used in a section of our app., AppModule is also the section where we are declaring the entire application.

Let us look into the details of what's inside the AppModule. Here we tell our entire app what it needs to use. To begin with, we are declaring our AppComponent. Every component in our app needs to be declared inside an Angular module for us to use it. We have also imported BrowserModule. The browser module is actually the ng module for our browser, as per Angular’s official documentation. So, we can think of that as the middleman between the browser and our application. We can list components, directives, and pipes, which are part of the module, in the "declarations" array. We can import other modules by adding them to the "imports" array. We can list down all the services which are part of our module in the "providers" array. The bootstrap property provides a list of components that should be used as entry points for our app. Most likely, it would have only one element in the array, which is the root component of our app. It tells the bootstrapping method as to which component it should bootstrap ( AppComponent in the above example).

So, now we can see that we have declared all the required things for our application using our AppModule. Over a period of time, as our app grows, we may require additional things to be declared. But, at a high-level, this is how a basic app would have its modules defined.

Why Do We Need Angular Modules?

Well, Angular modules allow us to define a context for compiling our templates. Like when Angular parses HTML templates, it essentially looks up a particular list of components/directives/pipes. Each HTML tag is actually compared to this list to identify if any component should be applied and checks similarly for each attribute. Now, how does Angular know which components/directives/pipes it should look for while parsing the HTML?

This is where Angular modules come in. They are responsible for providing this information in a single place.

So, the following can be said about Angular modules: 1. These are necessary to parse templates, both in the case of Just-In-Time or Ahead-Of-Time Compilation. 2. They act as documentation for grouping together related functionality.

How Different Are Angular Modules from ES6 Modules?

Angular Modules are very different from ES6 modules. An ES6 module is standardization of typical Javascript module which the JS community has been using for several years. They allow you to wrap private details inside a closure and only exposes the public API.

An Angular Module is mainly used to provide compilation context for the template, but it also defines a public API for a subset of functionality plus helps with dependency injection configuration of our app.

What Is a Root Module?

Each app will have only one root module. Also each component, directive, and pipe would be associated with only a single module.

Below is an example of an app's root module;

1
2
3
4
5
6
7
8
9
10
11
12
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent, MyCustomComponent, MyCustomDirective, MyCustomPipe],
  imports: [BrowserModule],
  providers: [MyUserService, MyOtherService],
  bootstrap: [AppComponent]
})
export class AppModule {
}
javascript

There are several things which identify the above as being a root module:

  1. The root module follows the conventional naming of AppModule.
  2. The root module for web applications typically imports BrowserModule, which provides browser-specific renderers and also installs core directives like ngIf, ngFor, ngSwitch, etc.
  3. The "bootstrap" property is used above, which provides a list of components that should be used as bootstrap entry points for our app. Ideally, this would contain only the root component of our application.

Use of Features, Shared Modules, and Core Modules

Depending on the size and complexity of our app, we may be able to just use AppModule for all our declarations. However, for larger apps, it is recommended to use core, shared, and feature modules. This will give us a proper separation of concerns and also help us manage scalability as the app grows. Let us look at each type of module in detail:

Feature Module A feature module mostly extends our global application and is a module in which all of our content will be encapsulated inside a single area. Ideally, applications should have multiple feature modules. We can think of them as mini standalone apps which are part of the entire application. A feature would represent what we can call a “section”. It would usually contain a root component which it exports and will be used by a parent module. The rest of the parts of that feature would be contained inside this root component.

Shared Modules As our app grows, we will see the need for having a shared module which can represent commonly used services. Thus, shared modules are useful for pieces of your app which will be used across multiple areas/features of our app. If any component can be reused within multiple features, we can declare it inside a shared module. Services and Pipes are the most common usages for shared modules. Shared modules do not always follow the “section” idea which we had discussed. Instead, they can be used to share common parts to fill out feature module “sections”.

Core Module We can use the core module to separate the configuration layer from the rest of our application. To do so, we can declare all of our features and shared modules inside our CoreModule and then provide the CoreModule to the AppModule. For anything which might be used across all feature modules, we can just declare it in the CoreModule. Thus, the Core Module can be thought of as the parent feature module for all of our content which will be added to our application.

Set up the Module Structure in Our App

Let us try to demo this through a practical application. We'll set up our app such that we can use the Angular module structure which should make things clear. First, we'll generate the core module, core component, and then a shared module using the below commands: ng generate module core ng generate component core

If we set upthe module first, a folder will get created with our core module. Then, when the component is generated, it would get placed inside the same folder and will be declared in the module by our CLI.

ng g module shared We are not planning to do anything with our SharedModule yet, but we'll generate that for future usage.

Next, we have to inform our AppModule about the CoreModule. So, we'll import the CoreModule inside AppModule and then add it to the "imports" array. This is how AppModule should look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import {BrowserModule} from '@angular/platform-browser';
import {CoreModule} from './core/core.module';
import {NgModule} from '@angular/core';
import {AppComponent} from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    CoreModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}
javascript

Now we'll open app.component.html and replace the content with my-app-core. Also “my-app-core” is the selector for our core component. So, essentially, we are just asking our AppComponent to serve CoreComponent. If we run ng serve and hit localhost:4200, we will observe an error in the console saying my-app-core is not a known element and nothing gets displayed on the page. This implies that our application is not aware of any component with the selector “my-app-core”. Let us analyze why that is the case. We declared CoreComponent inside our CoreModule, but we did not expose that to our app. To do that, we'll need to add CoreComponent to the "exports" array inside our CoreModule. So, our CoreModule should look something like below:

1
2
3
4
5
6
7
8
9
10
import {NgModule} from '@angular/core';
import {CoreComponent} from './core.component';
import {CommonModule} from '@angular/common';

@NgModule({
  imports: [CommonModule],
  declarations: [CoreComponent],
  exports: [CoreComponent]
})
export class CoreModule {}
javascript

Now, when we run ng serve again and verify in the browser, we would see “My core component is working!”, which represents the content/template of our CoreComponent.

Conclusion

Thus, we saw the use of Angular Modules which allow us to group the overall functionality of our app into logical parts. Modules can also be useful for enabling ahead of time compilation and for lazy-loading.

3