Guides

NgModule Demo

App Module Demo

Launch Demo Application

Description

We will use a simple Angular application, Song List to examine how to use Augury's module list feature. The application includes a simple form for the user to search for songs and will use the public Spotify API to get the results and display the album art and song title in a list.

Architecture

The Song List application is composed of two modules. One, the root application module which is used to bootstrap and launch the application. The second is a single feature module that contains the components, services, and pipes relevant for the searching and displaying of song data. Lets take a look at how this is organized:

Module Breakdown

The Songs Module is a feature module, which exports a component, SongsComponent that can be used throughtout the application. This is used in the AppComponent, which is our root component that will bootstrap the application.

The architecture of the Song Module is pretty straightforward. The SongsComponent contains the search field in its template, and has the SongService injected as a dependency. When the user searches for a term, the SongService is invoked to perform a GET request, which returns an array of results. This dataset is passed to the SongListComponent which will use the SongPipe to display the album art, song title, and the name of the artist.

Now that we understand the basic architecture of the Song List application, lets open Augury and explore the Module List feature.

Opening Augury

To use Augury, lets open up DevTools with the following shortcut:

Ctrl + Shift + I (Cmd + Opt + I on Mac)

When the DevTools panel opens, select the Augury tab located on the far right. By default the Component Tree tab will be open. To the far right, there will be a tab labeled NgModules. This is the section we want so click on it to open it up!

Root Module

Module List AppModule

The Module List feature lists all the modules in the application. The first module in this list is the AppModule. Here we can see what imports, exports, providers, declarations, and providers in declarations are found in the module. Our AppModule imports the BrowserModule (used for bootstrapping), HttpModule (used for performing network requests) and our feature module, SongsModule. Since the AppModule contains the application's root component it doesn't need to export anything. The AppModule has a single Provider, LoggerService which is a simple service used for logging messages. Finally, the AppModule has one component, AppComponent which is the root component of our application and as such is the bootstrap entry point.

To see where Augury gets this information from, open up app.module.ts and take a look at the AppModule source code:

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

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

import { Logger } from './services/logger.service';

import { SongsModule } from './modules/songs.module';

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

As you can see, the information that Augury has picked out and displayed in the Module List comes directly from the NgModule definition.

Declarations include the components, directives, and pipes used by the module.

Imports are the other modules and their exported declarations that we want to be made available in this module.

Providers list the services used by this module. Any service included as a provider in the NgModel definition becomes available to any other module in the hierarchical dependency injection system. For example, the SongsModule includes the SongService as a provider, since AppModule has imported the SongsModule it can now use the SongService. If we omitted the SongService from the NgModel definition of the SongsModel and instead opted to include the SongService in the provider definition of the SongsComponent, the AppModule will not have access to the SongService. Dependency Injection is a topic outside the scope of this guide, but you can read more about it on the Angular 2 Documentation

So now that we've taken a look at the root module of our application, let us continue on to the other modules in Augury's module list.

Library Modules

Angular comes with a bunch of essential modules that we use for creating forms, making HTTP requests, and specifying what environment our application should run under. In the root module we imported the BrowserModule and HttpModule.

Module List 2

Since library modules are static, there really isn't much in the way of useful information here, particularly for this application. However there may come a time where you need to debug a certain library module and given the complexity and size of such modules getting a breakdown of its providers, exports and declarations may come in handy.

Feature Module

Finally, lets take a look at our feature module, SongsModule.

Module List 3

As we can see, the SongsModule uses three library modules. FormsModule and ReactiveFormsModule are used for building the search form, and CommonModule contains many of the common directives used in the applications templates.

Feature modules can be thought of as extending the functionality of an application by introducing a new feature. Since this is a feature module, it will be imported by another module (in this case the root module). However not all declarations from the SongsModule need to be used elsewhere. The SongListComponent and SongPipe are really only used within the SongsModule, so we export our main container for this module, SongsComponent.

The SongsModule also uses the SongService as seen under the provider section.

Let us take a look at the definition of the Song Module, open up src/app/modules/songs.module.ts:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

import { SongPipe } from '../pipes/song.pipe';
import { SongListComponent } from '../components/song-list/song-list.component';
import { SongsComponent } from '../components/songs/songs.component';

import { SongService } from '../services/songs.service';

@NgModule({
  imports: [CommonModule, FormsModule, ReactiveFormsModule],
  declarations: [SongListComponent, SongsComponent, SongPipe],
  providers: [SongService],
  exports: [SongsComponent]
})
export class SongsModule {}

Just like our root module, Augury has picked out the imports, declarations, providers and exports information from our module definition.

One important thing to take note of is that Module List does not include any resources (pipes, providers, directives, components, etc) that a module may use, just the resources defined by NgModule. For example, open up the SongListComponent (components/song-list/song-list.component.ts), here is what it should look like:

export class SongListComponent {
  @Input() songs: any;

  ngOnChanges() {
    if (this.songs) {
      this.logger.info('song list changed!');
    }
  }

  constructor(private logger: Logger) {
  }
}

The SongListComponent uses the Logger service via dependency injection that has been made available from the AppModule. Since the AppModule is at the root of the application's dependency injection hierarchy, the SongListComponent can use any of its providers. However, since the Logger service is defined in the AppModule, Augury will list this service under the providers section of the AppModule. It is not included in the section of the SongsModule, even though the SongsModule uses this service. To get a better sense of dependency injection throughout our application we should use Augury's Dependency Injection Tree.

Let us see what happens when we remove the SongService from the provider list in the SongModule definition and move it to the provider list in the SongsComponent component definition. Here is what SongsComponent will look like:

@Component({
  selector: 'my-songs',
  styleUrls: ['./songs.component.css'],
  providers: [SongService],
  template: `
      <h2>Songs</h2>
    <form [formGroup]="searchForm">
        <input
            #searchInput
            class="search"
            formControlName="artist"
            debounce="400"
            placeholder="Search for artist"
            (keyup)="search()" />
    </form>
    <hr />
    <song-list [songs]="songs"></song-list>
  `
})
export class SongsComponent { ...

Augury has picked up what we've done and has noticed that the SongService is no longer a provider from the module level, but instead from the component level. As we can see below, SongService is now under the ProvidersInDeclarations section.

Module List 4

This comes in handy as Auguary is able to pick up and list all services that a module provides, whether or not they are included in the module providers, or via specific component providers.