Book Image

Angular Projects - Second Edition

By : Aristeidis Bampakos
Book Image

Angular Projects - Second Edition

By: Aristeidis Bampakos

Overview of this book

Packed with practical advice and detailed recipes, this updated second edition of Angular Projects will teach you everything you need to know to build efficient and optimized web applications using Angular. Among the things you’ll learn in this book are the essential features of the framework, which you’ll master by creating ten different real-world web applications. Each application will demonstrate how to integrate Angular with a different library and tool. As you advance, you’ll familiarize yourself with implementing popular technologies, such as Angular Router, Scully, Electron, Angular service worker, Nx monorepo tools, NgRx, and more while building an issue tracking system. You’ll also work on a PWA weather application, a mobile photo geotagging application, a component UI library, and many other exciting projects. In the later chapters, you’ll get to grips with customizing Angular CLI commands using schematics. By the end of this book, you will have the skills you need to be able to build Angular apps using a variety of different technologies according to your or your client’s needs.
Table of Contents (12 chapters)

Configuring routing for our application

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:

  1. Create a module that will be the home for our contact feature:
    ng generate module contact
  2. 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.

  3. 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:

  1. 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.

  2. 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.

  3. 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:

  1. 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.

  2. 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

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:

  1. 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>
  2. Run ng serve and use your favorite browser to preview your application.
  3. Open the developer tools of your browser, click on the Articles link and inspect the Network tab:
Figure 2.4 – Lazy loading Angular module

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.