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)

Displaying blog posts on the home page

We would like our users to see the list of available blog posts as soon as they land on our blog website. According to the default route path that we have defined, ArticlesComponent is the landing page of our blog. Scully provides ScullyRoutesService, an Angular service that we can use in our components to get information about the routes that it will create according to the blog posts. Let's put this service in action on our landing page:

  1. Navigate to the articles.component.ts TypeScript class file.
  2. Import ScullyRoute and ScullyRoutesService from the @scullyio/ng-lib package:
    import { ScullyRoute, ScullyRoutesService } from '@scullyio/ng-lib';
  3. Inject ScullyRoutesService in the constructor of the ArticlesComponent class:
    constructor(private scullyService: ScullyRoutesService) { }
  4. Create a component property of ScullyRoute array type:
    posts: ScullyRoute[] = [];
  5. Edit the ngOnInit method of the component and add the following code:
    ngOnInit(): void {
      this.scullyService.available$.subscribe(posts => {
        this.posts = posts.filter(post => post.title);
      });
    }
  6. Open the articles.component.html file and add the following HTML code:
    <div class="list-group mt-3">
      <a *ngFor="let post of posts"
        [routerLink]="post.route" class="list-group-item
          list-group-item-action">
        <div class="d-flex w-100 justify-content-between">
          <h5 class="mb-1">{{post.title}}</h5>
        </div>
        <p class="mb-1">{{post.description}}</p>
      </a>
    </div>

There are many Angular techniques involved in the previous steps, so let's break them down piece by piece.

When we want to use an Angular service in a component, we just need to ask for it from the Angular framework. How? By adding it as a property in the constructor of the component. The component does not need to know anything about how the service is implemented.

The ngOnInit method is part of the OnInit interface, which is implemented by our component. It is called by the Angular framework when a component is initialized and provides us with a hook to add custom logic to be executed.

Tip

Angular services that provide initialization logic to a component should be called inside the ngOnInit method and not in the constructor because it is easier to provide mocks about those services when unit testing the component.

The available$ property of ScullyRoutesService is called an observable. To retrieve its value, we need to subscribe to it. The returned posts variable will contain all the available routes that were generated from Scully. Scully is run against all routes of our Angular application. To avoid displaying routes other than those related to blog posts, such as the contact route, we filter out the results from the available$ property.

When we subscribe to an observable, we need to unsubscribe from it when our component no longer exists. Otherwise, we may experience memory leaks in our Angular application. Let's see how we can accomplish this task using another life cycle hook of the component called ngOnDestroy:

  1. Declare a private routeSub property of the Subscription type in the ArticlesComponent class. Subscription can be imported from the rxjs npm package.
  2. Set the returned value of the available$ observable to the routeSub property.
  3. Add the OnDestroy interface to the list of implemented interfaces of the component. OnDestroy can be imported from the @angular/core npm package. It is executed when the component is destroyed, and it is not rendered on the screen anymore.
  4. Implement the ngOnDestroy method and call the unsubscribe method of the routeSub property in the body of the method.

The resulting TypeScript file of the component should look like the following:

articles.component.ts

import { Component, OnInit, OnDestroy } from '@angular/core';
import { ScullyRoute, ScullyRoutesService } from '@scullyio/ng-lib';
import { Subscription } from 'rxjs';
@Component({
  selector: 'app-articles',
  templateUrl: './articles.component.html',
  styleUrls: ['./articles.component.scss']
})
export class ArticlesComponent implements OnInit, OnDestroy {
  posts: ScullyRoute[] = [];
  private routeSub: Subscription | undefined;
  constructor(private scullyService: ScullyRoutesService) { }
  ngOnInit(): void {
    this.routeSub =
      this.scullyService.available$.subscribe(posts => {
      this.posts = posts.filter(post => post.title);
    });
  }
  ngOnDestroy(): void {
    this.routeSub?.unsubscribe();
  }
}

In the template of our component, we use the *ngFor Angular built-in directive to iterate over the posts array inside HTML. We can then access each item of the array using the post template reference variable and use interpolation to display title and description.

Finally, we add a routerLink directive to each anchor element to navigate to the respective blog post when clicked. Notice that routerLink is surrounded by []. The [] syntax is called property binding, and we use it when we want to bind the property of an HTML element to a variable. In our case, we bind the routerLink directive to the route property of the post template reference variable.

Now that we have finally completed all the pieces of the puzzle, we can see our blog website in action:

  1. Run the build command of the Angular CLI to build our Angular application:
    ng build
  2. Execute the following npm command to build Scully and generate our blog routes:
    npm run scully

    The preceding command will create a scully-routes.json file inside the src\assets folder. It contains the routes of our Angular application and is needed from the Scully runtime.

    Tip

    Running the Scully executable for the first time will prompt you to collect anonymous errors to improve its services.

  3. Run the following npm command to serve our blog:
    npm run scully:serve

    The preceding command will start two web servers: one that contains the static prerendered version of our website built using Scully and another that is the Angular live version of our application:

Figure 2.9 – Serving our application

Figure 2.9 – Serving our application

If we open our browser and navigate to http://localhost:1668, we will not see any blog posts. Why is that?

A blog post created with Scully is not returned in the available$ property of ScullyRoutesService unless we publish it. To publish a blog post, we do the following:

  1. Navigate to the mdfiles folder that Scully created and open the only .md file that you will find. The name and contents may vary from your file because it is based on the creation date from Scully:
    ---
    title: 2020-11-15-posts
    description: 'blog description'
    published: false
    slugs:
        - ___UNPUBLISHED___khm71wkh_hIzSmrBDHceuWrDrqqTnY8qCwvurkxdT
    ---
    # 2020-11-15-posts

    Scully has defined a set of properties between the closing and ending --- lines at the top of the file representing metadata about the blog post. You can also add your own as key-value pairs.

  2. Delete the slugs property and set the published property to true:
    ---
    title: 2020-11-15-posts
    description: 'blog description'
    published: true
    ---
    # 2020-11-15-posts

    Tip

    If you do not want to publish a post manually, Scully supports the automatic publishing of blog posts. You can use the reserved publishDate property in the metadata to define the date you want to publish the blog post, and Scully will do the rest.

  3. Run the following command to force Scully to regenerate the routes of our application:
    npm run scully

    We need to execute the previous command every time we make a change in our blog-related files.

  4. Execute the npm run scully:serve command and navigate to preview the generated website.

We can now see one blog post, the default one that was created when we installed Scully. Let's create another one:

  1. Run the following generate command of the Angular CLI:
    ng generate @scullyio/init:post --name="Angular and Scully"

    In the preceding command, we use the @scullyio/init:post schematic, passing the name of the post that we want to create as an option.

  2. Set the target folder for the new blog post to mdfiles:
    Figure 2.10 – New blog post target folder

    Figure 2.10 – New blog post target folder

  3. Scully will create a Markdown file named angular-and-scully.md inside the specified folder. Open that file and update its content to be the same as the following:
    ---
    title: 'Angular and Scully'
    description: 'How to build a blog with Angular and Scully'
    published: true
    ---
    # Angular and Scully
    Angular is a robust JavaScript framework that we can use to build excellent and performant web applications.
    Scully is a popular static website generator that empowers the Angular framework with Jamstack characteristics.
    You can find more about them in the following links:
     - https://angular.io
     - https://scully.io
     - https://www.jamstack.org
  4. Run npm run scully to create a route for the newly created blog post. Scully will also update the scully-routes.json file with the new route.

If we preview our application now, it should look like the following:

Figure 2.11 – List of blog posts

Figure 2.11 – List of blog posts

If we click on one of the blog items, we will navigate to the selected blog post. The content that is currently shown on the screen is a prerendered version of the blog post route:

Figure 2.12 – Blog post details

Figure 2.12 – Blog post details

To verify that, navigate to the dist folder of your Angular project, where you will find two folders:

  • my-blog: This contains the Angular live version of our application. When we execute the ng build Angular CLI command, it builds our application and outputs bundle files in this folder.
  • static: This contains a prerendered version of our Angular application generated from Scully when we run the npm run scully command.

If we navigate to the static folder, we will see that Scully has created one folder for each route of our Angular application. Each folder contains an index.html file, which represents the component that is activated from that route. The contents of the index.html file are auto-generated from Scully, and it is the result as if we run our application live and navigate to that component.

Now you can take your Angular application, upload it to the CDN or web server of your choice, and you will have your blog ready in no time! All you will have to do then will be to exercise your writing skills to create excellent blog content.