Angular Module Tutorial: Application Structure Using Modules
In my previous post we generated a project using the Angular CLI. In this post I am going to discuss the structure of an Angular application. Specifically focusing on using Angular modules to manage your code base. I will start by explaining what an Angular module is and how they work. Then I will begin to lay the ground work for our application’s structure.
Before we jump in, here are some links to other Angular Tutorials I’ve put together:
- Angular Tutorial: Getting started with the Angular CLI
- Angular Component Tutorial: Inputs, Outputs, and EventEmitters
- Angular Component Lifecycle
- Angular Router Tutorial: Setting Up Routing in Your Application
- Angular Module Tutorial: Application Structure Using Modules
- Using Bootstrap 4 with Angular
- Electron Tutorial: Getting started with Electron and Angular CLI
What is an Angular Module?
I like to think of an Angular module as a declaration of everything that will be used or needed in a section of the application. For example, let’s take a look at what is inside of our AppModule
. As you may recall, the AppModule
was generated for us by the CLI and contains everything we need for a working application shell:
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
So how does this Angular module work with my statement “a module is a declaration of everything that will be used or needed in a section of the application”? Well, first we need to decide what “section” of the application this is. Since it’s the AppModule
the section we are declaring here is the entire application.
So then what do we have declared for this section? What are we telling our entire app that it needs to use? Well, we are literally declaring our AppComponent
, which is necessary to use it. Every component in your application needs to be declared in an Angular module to be able to use it. Additionally, we have imported a BrowserModule
. The browser module is the “ng module for the browser” according to Angular’s documentation. It’s essentially the middleman between the browser and our app.
*If you are wondering about the bootstrap array, look in main.ts
and notice the AppModule
being provided in the application bootstrapping method. AppComponent
in that array is telling the bootstrapping method what component to bootstrap. You may will also notice that the platformBrowserDynamic
object is imported from the same place as our BrowserModule
. Had we not imported the BrowserModule
in our AppModule
we wouldn’t be able to use that object because our app wouldn’t know about it.
With that, you can see, we have declared our essentials for our application in our AppModule
. As the application grows, additional things may need to be declared but for now we have everything we need for this “section” (the whole app).
Angular Module Structure
When setting up modules in an Angular application, I like to keep this hierarchy picture in mind.
Child feature modules will hold the same hierarchy. If your able to maintain this structure your application will be much easier to maintain and separation of concerns will be easier to implement. Ideally, this tree will grow wider faster then it grows deeper, meaning your child feature modules will be independent of a parent module.
Feature, Shared, and Core Modules
Now depending on the size and complexity of the project, you may be able to just use the single AppModule
for all of your declarations. However, for larger applications, I encourage the use of core, shared and feature modules. Doing so will ensure proper separation of concerns, which will ease scalability as your application grows. I’ll try to briefly explain how this works by describing each type of module.
- Feature Modules
- A feature module is a module in which all of the content is going to be encapsulated inside of a single area.
- Applications should be made up of multiple feature modules.
- Think of a feature module as a mini stand alone application inside your full application.
- A feature would be what I mean by “section”, it usually has a root component that it exports and is used in a parent module. All the rest of the pieces of that feature will be put inside that root component.
- Shared Modules
- Use shared modules for pieces of your application that need to be used across multiple areas (features) of your application.
- If a component is going to be re-used in multiple features, declare it in a shared module.
- Services and Pipes are more commonly declared in shared modules.
- Shared modules don’t necessarily follow the “section” idea that I stated previously. Instead they provide a way to share common pieces to fill out feature module “sections”.
- Core Module
- I (and many others) prefer to use a core module as a way to separate the configuration layer of your application with the rest of your application.
- To do so, you declare all of your feature and shared modules in your
CoreModule
and just provide yourCoreModule
to yourAppModule
- For anything that needs to be used across all feature modules, declare it in the
CoreModule.
- Think of it as the parent feature module for all of the content you are going to add to your application.
Setting Up This Structure in our Application
I know that may not be totally clear, it’s a difficult concept to relay through writing. So I’ll try to demonstrate through practice. Let’s set up our application so that we can use this Angular module structure and hopefully that will make things clearer. I will create a new feature for every “example” I add to the repository. Before I start adding those examples, I have to set up the ground work to use this design.
First, let’s generate our core module, core component and a shared module:
ng g module core
ng g component core
- If you do the module first, a folder will be created with the core module. Then, when the component is generated, it will be placed in the same folder and be declared in the module by the CLI.
ng g module shared
- We aren’t going to do anything with our
SharedModule
yet but I like to get it in early for future use.
- We aren’t going to do anything with our
Next, we need to tell our AppModule
about our CoreModule
- Import the
CoreModule
inAppModule
and add it to the imports array - Your
AppModule
will now look like this
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { CoreModule } from './core/core.module'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, CoreModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
- Next open
app.component.html
and replace the current content withapp-core
- Reminder: “app-core” is the selector for the core component, so all we are doing is telling our
AppComponent
to serve upCoreComponent
- Reminder: “app-core” is the selector for the core component, so all we are doing is telling our
If you run ng serve
and navigate to localhost:4200
you will notice there is an error in the console “'app-core' is not a known element
” and nothing displayed on the page. This is telling us that our application isn’t aware of a component with a selector of “app-core”. Why is that? Well we declared CoreComponent
in our CoreModule
but we didn’t expose it our app. To do so, add the CoreComponent
to the exports array in our CoreModule
. Your CoreModule
should look like this:
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { CoreComponent } from './core.component'; @NgModule({ imports: [ CommonModule, ], declarations: [CoreComponent], exports: [CoreComponent] }) export class CoreModule { }
Now when you run ng serve
and check your browser, you will see “core works!”, which is the content in our template of our CoreComponent
.
**Using a single exported component and it’s selector is one way to set up Angular modules in an application. This method will allow a developer to create an entire application without using the router. In a later post we will set up routing for our application and will not need to use the selector or the exports array.
Summary
Lets take a look at the current hierarchy of our application.
From here we are ready to start adding features to our application. When adding features, you will follow the same pattern as we just did to add the CoreModule
but instead, for additional features, we will be telling our CoreModule
about our additionalFeatureModules
. Next thing we are going to do is set up routing in our application, meaning we won’t need to export our components for parent modules to use, instead we will use the router. As always, leave any notes, suggestions or questions in the comments section.
Other posts in this series: