Angular

This post will be the first in a series of articles covering the Angular 9 front-end web framework. Future articles will build upon the project created in this post, so it would be a good idea to keep a reference to this project.

This post is not meant to be a primer on the Angular framework. Instead it will describe the steps that we take when creating a new Angular project. In the article I will assume you already have a somewhat working knowledge of Angular. The finished product created in this post is located in our GitHub repository:
https://github.com/stratmanmedia/angular-starter-template/tree/angular-9

Creating the project

After updating the Angular CLI to version 9.x, we create a new project.

ng new angular-starter-template --routing

The –routing flag tells the CLI to create the AppRoutingModule, which will let us configure routing in our application.

The project file structure

Before we continue building our application, we must understand the file structure. We designed this project file structure to allow for lazy loading of modules and maintain developer sanity when adding features. Below is a basic example of the file structure.

- e2e
+ src
  + app
    + core
      + auth
      + data
      + guards
      + interceptors
      - core.module.ts
    + modules
      + guest-layout
        + components
          + guest-layout
            - guest-layout.component.html
            - guest-layout.component.css
            - guest-layout.component.ts
        - guest-layout-routing.module.ts
        - guest-layout.module.ts
      + start
        + components
        + pages
          + start
            - start.component.html
            - start.component.css
            - start.component.ts
        - start-routing.module.ts
        - start.module.ts
    + shared
      + components
      + directives
      + models
      + pipes
      + utilities
    - app-routing.module.ts
    - app.component.html
    - app.component.css
    - app.component.ts
    - app.module.ts
  + assets
    + css
    + html
    + js
    + locale
  + environments
    - environment.prod.ts
    - environment.ts

The application is separated into three areas: core, modules, and shared. We will discuss each area in detail.

Core

The core folder contains the CoreModule file. The CoreModule is a central place to import the services and other code that is included under the core folder. We then import the CoreModule into the AppModule in order to create a separation of concerns.

The purpose of the core folder is to contain the services and other code which pertain to the operations of a front-end web application. In the example above we have auth, data, guards, and interceptors. These are just some examples of features that relate to the core application and are loaded with the application. Classes contained in the core folder are intended to be singletons across the application, and are imported into the CoreModule.

Modules

The modules folder is where we place all of our feature or content modules that we want lazy loaded. In the example, we have a GuestLayoutModule which contains our layout and a StartModule which contains components and pages. What we call “pages” are technically Angular components, but it helps us distinguish a component which is a whole page versus a component that is part of a page. We will go into further detail about these modules later in this post.

Shared

In the shared folder is where we place components or other classes that can be used across modules. Most often we store data structure models here, but when we need to create directives and pipes, this is an appropriate location to store them.

Create the CoreModule

Our next step is to create the CoreModule. This module does not require routing, so using the terminal we navigate into the project folder and enter the following CLI command to create it.

ng g m core

The CLI will create the CoreModule class for us and there is nothing else we need to do for this example. In future posts we will add services to this module.

Create the StartModule

Our work in the CoreModule is complete, so now we move onto the content modules. We will begin with the end of the routing chain, which for this example is the start page. This is the page displayed when navigating to the root path. To create the StartModule, we use the following command:

ng g m modules/start --routing

Notice we have included routing in this module. We will configure routing later in this example.

Create the StartComponent

Before we can configure routing, we must create a component to route to. For our root path we will display the StartComponent. To create this component we use the CLI:

ng g c modules/start/pages/start

This component will be described as a page, so we create it under the pages directory. The CLI should automatically declare the component in the StartModule, but it would be best to confirm this. We don’t need to make any changes to this component for this example. It will simply display the default text to prove that it works.

Configure the StartRoutingModule

We are now ready to wire up our first route. As stated above, the root path will route to our StartComponent, so we must add that to our route configuration in StartRoutingModule:

const routes: Routes = [
  { path: '', component: StartComponent }
];

At this point our StartModule should know to send guests to the StartComponent when navigating to the root path.

Create the GuestLayoutModule

The GuestLayoutModule is where the excitement begins. When we were new to Angular, lazy loading took some time wrap our heads around. Once we put it into practice it was much easier to understand how all the pieces work together. The GuestLayoutModule is simple and only needs to include routing, so we create it with the following CLI command:

ng g m modules/guest-layout --routing

The GuestLayoutModule does not need modification for this example.

Create the GuestLayoutComponent

Our application will lazy load the layout depending on whether the user is logged in or is a guest. For this example, we will only create the guest layout, but the user layout will be created in much the same way. We will create the user layout in a future post. For the guest layout, we use the following CLI command:

 ng g c modules/guest-layout/components/guest-layout

Our component is fully created by the CLI, but we will need to customize the layout displayed to guests. For this example we will simply add some text to prove that our layout component works. Also, we need to include a router-outlet in the component to allow routing to the appropriate page. Our component HTML will look like this:

<p>guest-layout works!</p>
<router-outlet></router-outlet>

Generating the component with the CLI also declares the GuestLayoutComponent inside the GuestLayoutModule. So, the GuestLayoutModule decorator should now look like this:

@NgModule({
  declarations: [
    GuestLayoutComponent
  ],
  imports: [
    CommonModule,
    GuestLayoutRoutingModule
  ]
})

Configure the GuestLayoutRoutingModule

We will need to modify the GuestLayoutRoutingModule to configure routing. We want the root path to load the StartModule, which will then route to the StartComponent. To do this, we lazy load the module in the route configuration like this:

const routes: Routes = [
  {
    path: '',
    component: GuestLayoutComponent,
    children: [
      {
        path: '',
        loadChildren: () => import('../start/start.module').then(m => m.StartModule)
      }
    ]
  }
];

Notice that in addition to adding the GuestLayoutComponent, we add child routes to the root path. For now the only child is the same root path, but this leaves the route configuration more easily extensible when we add modules. In the child route instead of using the component method to pass a component to load we use the loadChildren method to pass the module to load using an anonymous function. The import function accepts the relative path to the file containing the module we want to load.

Modify the AppComponent

Our core and content modules are complete, so it is time to connect them to the app. Our AppComponent will only need the router-outlet directive and some text to prove that the component is working, so we remove everything in app.component.html except for:

<p>app-component works!</p>
<router-outlet></router-outlet>

Modify the AppModule

In order to use the Angular router-outlet directive, we must import the RouterModule into the AppModule. We also need to import our CoreModule into the AppModule. We usually have more success inserting modules above the routing module, so our AppModule decorator should look like this:

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

Modify the AppRoutingModule

The last thing we need to do to finish our example application is wire up the routing in the AppRoutingModule. We want the root path to load the GuestLayoutModule, which will then complete our route to the StartComponent that we created earlier. To do this, we configure our routes like so:

const routes: Routes = [
  {
    path: '',
    loadChildren: () => import('./modules/guest-layout/guest-layout.module').then(m => m.GuestLayoutModule)
  }
];

This lazy loading configuration is more simple than our configuration in the GuestLayoutModule. We use the loadChildren method again, this time to lazy load the GuestLayoutModule.

Build and Run the Application

We can now build and run our application using the CLI to make sure it works as planned. I like to run the build command first then serve, but just running the serve command works fine as well.

ng build
ng serve -o

If the application is working as expected, you should see something similar to the following screenshot in your browser.

Conclusion

Hopefully this information can provide other organizations with a helpful and intuitive file structure for Angular framework applications. The entire example application can be found on our GitHub repository at:
https://github.com/stratmanmedia/angular-starter-template/tree/angular-9

Future posts will build on this example and I will link them in this post.

Categories:

Comments are closed