The header component that we created in the previous section contains two links:
- Articles: Displays a list of blog articles
- Contact: Displays personal information about the blog owner
The previous links will also become the main features of our application. So, we need to create an Angular module for each one.
Tip
When you design your website and need to decide upon the Angular modules that you will use, check out the main menu of the website. Each link of the menu should be a different feature and, thus, a different Angular module.
By convention, Angular modules that contain functionality for a specific feature are called feature modules.
Creating the contact page
Let's begin by creating our contact feature first:
- Create a module that will be the home for our contact feature:
ng generate module contact
- Create a component that will be the main component of the contact module:
ng generate component contact --path=src/app/contact --module=contact --export --flat
We pass the --flat
option to the generate
command so that the Angular CLI will not create a separate folder for our component, as in previous cases. The contact component will be the only component in our module, so there is no point in having it separately.
- Open the
contact.component.html
file and add the following HTML content:<div class="card mx-auto text-center border-light" style="width: 18rem;">
<img src="assets/angular.png" class="card-img-top"
alt="Angular logo">
<div class="card-body">
<h5 class="card-title">Angular Projects</h5>
<p class="card-text">
A personal blog created with the Angular
framework and the Scully static site generator
</p>
<a href="https://angular.io/" target="_blank"
class="card-link">Angular</a>
<a href="https://scully.io/" target="_blank"
class="card-link">Scully</a>
</div>
</div>
In the preceding code, we used the angular.png
image, which you can find in the src\assets
folder of the project from the accompanying GitHub repository.
Tip
The assets
folder in an Angular CLI project is used for static content such as images, fonts, or JSON files.
We have already created our contact feature. The next step is to add it to the main page of our Angular application:
- Open the
app-routing.module.ts
file and add a new route configuration object in the routes
property:import { ContactComponent } from './contact/contact.component';
const routes: Routes = [
{ path: 'contact', component: ContactComponent }
];
The preceding code indicates that when the URL of the browser points to the contact
path, our application will activate and display ContactComponent
on the screen. The routes
property of a routing module contains the routing configuration of the respective feature module. It is an array of route configuration objects where each one defines the component class and the URL path that activates it.
- Add
ContactModule
in the imports
array of the @NgModule
decorator of AppModule
to be able to use it:@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
CoreModule,
SharedModule,
ContactModule
],
providers: [],
bootstrap: [AppComponent]
})
Do not forget to add the respective import
statement for ContactModule
at the top of the file.
- Routed components, just like
ContactComponent
, need a place where they can be loaded. Open the app.component.html
file and add the router-outlet
directive:<app-header></app-header>
<div class="container">
<router-outlet></router-outlet>
</div>
<app-footer></app-footer>
Now, we need to wire up the route configuration that we created with the actual link on the header component:
- Open the
header.component.html
file and add the routerLink
directive to the respective anchor HTML element:<li class="nav-item">
<a routerLink="/contact" routerLinkActive="active"
class="nav-link">Contact</a>
</li>
In the preceding snippet, the routerLink
directive points to the path
property of the route configuration object. We have also added the routerLinkActive
directive, which sets the active
class on the anchor element when the specific route is activated.
Important Note
Notice that the value of the routerLink
directive contains a leading /
, whereas the path
property of the route configuration object that we defined does not. According to the case, omitting the /
would give a different meaning to the route.
- The
routerLink
and routerLinkActive
directives are part of the Angular router package. We need to import RouterModule
in the core module to use them:import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HeaderComponent } from './header/header.component';
import { RouterModule } from '@angular/router';
@NgModule({
declarations: [
HeaderComponent
],
imports: [
CommonModule,
RouterModule
],
exports: [
HeaderComponent
]
})
export class CoreModule { }
We are now ready to preview our new contact page! If we run the application using ng serve
and click on the Contact link, we should see the following output:
Figure 2.3 – Contact page
In the following section, we will build the functionality for the Articles link of the header in our blog.
Adding the articles page
The feature that is responsible for displaying articles in our blog will be the articles module. It will also be the module that connects the dots between Angular and Scully. We will use the generate
command of the Angular CLI to create that module:
ng generate module articles --route=articles --module=app-routing
In the previous command, we pass some additional routing options:
--route
: Defines the URL path of our feature
--module
: Indicates the routing module that will define the route configuration object that activates our feature
The Angular CLI performs additional actions, instead of just creating the module, upon executing the command:
- It creates a routed component in the
src\app\articles
folder that will be activated by default from a route navigation object. It is the landing page of our feature, and it will display a list of blog posts, as we will see in the Displaying blog data on the home page section.
- It creates a routing module, named
articles-routing.module.ts
, that contains the routing configuration of the articles module.
- It adds a new route configuration object in the route configuration of the main application module that activates the articles module.
The articles-routing.module.ts
file contains the routing configuration for the articles module:
articles-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ArticlesComponent } from './articles.component';
const routes: Routes = [{ path: '', component: ArticlesComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ArticlesRoutingModule { }
It imports RouterModule
using the forChild
method to pass the routing configuration to the Angular router. If we take a look at the main routing module of the application, we will see that it follows a slightly different approach:
app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ContactComponent } from './contact/contact.component';
const routes: Routes = [
{ path: 'contact', component: ContactComponent },
{ path: 'articles', loadChildren: () =>
import('./articles/articles.module').then(m =>
m.ArticlesModule) }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
The forChild
method is used in feature modules, whereas the forRoot
method should be used only in the main application module.
The route configuration of the articles module contains only one route that activates ArticlesComponent
. The path of the route is set to an empty string to indicate that it is the default route of the routing module. It essentially means that ArticlesComponent
will be activated whenever that module is loaded. But how is the articles module loaded in our application?
The second route of the main routing module contains a route configuration object that does not activate a component but rather a module. It uses the loadChildren
method to load ArticlesModule
dynamically when navigation triggers the articles
path.
Important Note
The import
function in the loadChildren
property accepts the relative path of the TypeScript module file without the extension.
The previous approach is called lazy loading and improves the startup and the overall performance of an Angular application. It creates a separate bundle for each lazy-loaded module, which is loaded upon request, reducing the final bundle size and the memory consumption of your application. Let's wire up the new route to our header component:
- Open the
header.component.html
file and add the following routerLink
and routerLinkActive
directives to the Articles
anchor HTML element:<li class="nav-item">
<a routerLink="/articles" routerLinkActive="active"
class="nav-link">Articles</a>
</li>
- Run
ng serve
and use your favorite browser to preview your application.
- Open the developer tools of your browser, click on the Articles link and inspect the Network tab:
Figure 2.4 – Lazy loading Angular module
Among other requests, you should see one named articles-articles-module.js. It is the bundle of the lazy-loaded articles module that was loaded when you clicked on the Articles link.
We are now ready to convert our amazing Angular application into a professional blog website. Before we move on, let's add some additional routes to the app-routing.module.ts
file:
const routes: Routes = [
{ path: 'contact', component: ContactComponent },
{ path: 'articles', loadChildren: () =>
import('./articles/articles.module').then(m =>
m.ArticlesModule) },
{ path: '', pathMatch: 'full', redirectTo: 'articles' },
{ path: '**', redirectTo: 'articles' }
];
We added a default route to automatically redirect our blog users to the articles
path upon visiting the blog. Additionally, we created a new route configuration object with its path set to **
that also navigates to the articles
path. The **
syntax is called the wildcard route, and it is triggered when the router cannot match a requested URL with a defined route.
Tip
Define the most specific routes first and then add any generic ones such as the default and the wildcard routes. The Angular router parses the route configuration in the order that we define and follows a first match wins strategy to select one.
We have already enabled and configured routing in our Angular application. In the following section, we will establish the infrastructure needed to add blogging capabilities to our application.