Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases now! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Angular Projects
Angular Projects

Angular Projects: Build modern web apps by exploring Angular 12 with 10 different projects and cutting-edge technologies , Second Edition

eBook
$27.98 $39.99
Paperback
$49.99
Subscription
Free Trial
Renews at $19.99p/m

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Product feature icon AI Assistant (beta) to help accelerate your learning
Table of content icon View table of contents Preview book icon Preview Book

Angular Projects

Chapter 2: Building an SPA Application with Scully and Angular Router

Angular applications follow the Single Page Application (SPA) architecture, where different views of the web page can be activated according to the browser URL. Any changes to that URL can be intercepted by the Angular router and translated to routes that can activate a particular Angular component.

Scully is a popular static website generator that is based on the Jamstack architecture. It can cooperate nicely with the Angular router to prerender the content of an Angular application according to each route.

In this chapter, we are going to combine Angular and Scully to create a personal blog. The following topics are going to be covered:

  • Setting up routing in an Angular application
  • Creating the basic layout of our blog
  • Configuring routing for our application
  • Adding blog capabilities with Scully
  • Displaying blog posts on the home page

Essential background theory and context

In the old days of web development, client-side applications were highly coupled with the underlying server infrastructure. Much machinery was involved when we wanted to visit the page of a website using a URL. The browser would send the requested URL to the server, and the server should respond with a matching HTML file for that URL. This was a complicated process that would result in delays and varying round-trip times.

Modern web applications eliminate these problems using the SPA architecture. A client needs to request a single HTML file only once from the server. Any subsequent changes to the URL of the browser are handled internally from the client infrastructure. In Angular, the router is responsible for intercepting in-app URL requests and handling them according to a defined route configuration.

Jamstack is a hot, emerging technology that allows us to create fast and secure web applications. It can be used for any application type, ranging from an e-commerce website to a Software as a Service (SaaS) web application or even a personal blog. The architecture of Jamstack is based on the following pillars:

  • Performance: Pages are generated and prerendered during production, eliminating the need to wait for content to load.
  • Scaling: Content is static files that can be served from anywhere, even from a Content Delivery Network (CDN) provider that improves the performance of the application.
  • Security: The serverless nature of server-side processes and the fact that content is already static eliminates potential attacks that target server infrastructures.

Scully is the first static website generator for Angular that embraces the Jamstack approach. It essentially generates pages of the Angular application during build time to be immediately available when requested.

Project overview

In this project, we will build a personal blog using the Angular framework and enhance it with Jamstack characteristics using the Scully site generator. Initially, we will scaffold a new Angular application and enable it for routing. We will then create the basic layout of our application by adding some barebone components. As soon as we have a working Angular application, we will add blog support to it using Scully. We will then create some blog posts using Markdown files and display them on the home page of our application.

Build time: 1 hour.

Getting started

The following software tools are required to complete this project:

Setting up routing in an Angular application

We will kick off our project by creating a new Angular application from scratch. Execute the following command of the Angular CLI in a terminal window to create a new Angular application:

ng new my-blog --routing --style=scss

We use the ng new command to create a new Angular application, passing the following options:

  • my-blog: The name of the Angular application that we want to create. The Angular CLI will create a my-blog folder in the path where we execute the command.

    Important Note

    Every command that we run in the terminal window should be run inside this folder.

  • --routing: Enables routing in the Angular application.
  • --style=scss: Configures the Angular application to use the SCSS stylesheet format when working with CSS styles.

When we enable routing in an Angular application, the Angular CLI imports several artifacts from the @angular/router npm package in our application:

  • It creates the app-routing.module.ts file, which is the main routing module of our application:
    import { NgModule } from '@angular/core';
    import { RouterModule, Routes } from '@angular/router';
    const routes: Routes = [];
    @NgModule({
      imports: [RouterModule.forRoot(routes)],
      exports: [RouterModule]
    })
    export class AppRoutingModule { }
  • It imports AppRoutingModule into the main module of our application, app.module.ts:
    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { AppRoutingModule } from './app-routing.module';
    import { AppComponent } from './app.component';
    @NgModule({
      declarations: [
        AppComponent
      ],
      imports: [
        BrowserModule,
        AppRoutingModule
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }

We configured our application to use the SCSS stylesheet format. Instead of creating the styles of our application manually, we will use the Bootstrap CSS library:

  1. Execute the following command in a terminal window to install Bootstrap:
    npm install bootstrap

    We use the npm executable to install the bootstrap package from the npm registry in the previous command.

  2. Add the following import statement at the top of the styles.scss file that exists in the src folder of our Angular application:
    @import "~bootstrap/scss/bootstrap";

    The styles.scss file contains CSS styles that are applied globally in our application. In the previous snippet, we import all the styles from the Bootstrap library into our application. The @import CSS rule accepts the absolute path of the bootstrap.scss file as an option, without adding the .scss extension. The ~ character represents the node_modules folder of our Angular application.

    Important Note

    The node_modules folder contains all the npm packages and libraries that our application needs, either during development or runtime.

In the following section, we will learn how to create the basic layout of our blog by creating components, such as the header and the footer.

Creating the basic layout of our blog

A blog typically has a header containing all the primary website links and a footer containing copyright information and other useful links. In the world of Angular, both can be represented as separate components.

The header component is used only once since it is added when our application starts up, and it is always rendered as the main menu of the website. In Angular, we typically create a module, named core by convention, to keep such components or services central to our application. To create the module, we use the generate command of the Angular CLI:

ng generate module core

The preceding command will create the module in the src\app\core folder of our application. To create the header component, we will use the same command, passing a different set of options:

ng generate component header --path=src/app/core --module=core --export

The previous command will create all necessary component files inside the src\app\core\header folder:

Figure 2.1 – Header component

Figure 2.1 – Header component

It will also declare HeaderComponent in the core module and add it to its exports property so that other modules can use it:

core.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HeaderComponent } from './header/header.component';
@NgModule({
  declarations: [
    HeaderComponent
  ],
  imports: [
    CommonModule
  ],
  exports: [
    HeaderComponent
  ]
})
export class CoreModule { }

The header component should display the main links of our blog. Open the header.component.html template file of the header component and replace its content with the following snippet:

header.component.html

<nav class="navbar navbar-expand navbar-light bg-light">
  <div class="container-fluid">
    <a class="navbar-brand">Angular Projects</a>
    <ul class="navbar-nav me-auto">
      <li class="nav-item">
        <a class="nav-link">Articles</a>
      </li>
      <li class="nav-item">
        <a class="nav-link">Contact</a>
      </li>
    </ul>
  </div>
</nav>

The footer component can be used more than once in an Angular application. Currently, we want to display it on the main page of our application. In the future, we may want to have it also on a login page that will be available for blog visitors. In such a case, the footer component should be reusable. When we want to group components that will be reused throughout our application, we typically create a module named shared by convention. Use the Angular CLI generate command to create the shared module:

ng generate module shared

The previous command will create the shared module in the src\app\shared folder. The footer component can now be created using the following command:

ng generate component footer --path=src/app/shared --module=shared --export

The previous command will create all necessary files of the footer component inside the src\app\shared\footer folder. It will also add FooterComponent in the declarations and exports properties of the shared module:

shared.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FooterComponent } from './footer/footer.component';
@NgModule({
  declarations: [
    FooterComponent
  ],
  imports: [
    CommonModule
  ],
  exports: [
    FooterComponent
  ]
})
export class SharedModule { }

The content of the footer component should contain copyright information about our blog. Let's see how to add this information to our component:

  1. Open the footer.component.ts TypeScript class file of the footer component. Add a currentDate property in the FooterComponent class and initialize it to a new Date object:
    export class FooterComponent implements OnInit {
      currentDate = new Date();
      
      constructor() { }
      ngOnInit(): void {
      }
    }
  2. Open the footer.component.html template file of the footer component and replace its content with the following:
    <nav class="navbar fixed-bottom navbar-light bg-light">
      <div class="container-fluid">
        <p>Copyright @{{currentDate | date: 'y'}}. All
          Rights Reserved</p>
      </div>
    </nav>

    The preceding code uses interpolation to display the value of the currentDate property on the screen. It also uses the built-in date pipe to display only the year of the current date.

    Important Note

    Pipes are a built-in feature of the Angular framework that apply transformations on the view representation of a component property. The underlying value of the property remains intact.

We have already created the essential components of our blog. Now it is time to display them on the screen:

  1. Open the main module of the application, the app.module.ts file, and add CoreModule and SharedModule into the imports property of the @NgModule decorator:
    @NgModule({
      declarations: [
        AppComponent
      ],
      imports: [
        BrowserModule,
        AppRoutingModule,
        CoreModule,
        SharedModule
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
  2. Add the appropriate import statements at the top of the file for each module:
    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { AppRoutingModule } from './app-routing.module';
    import { AppComponent } from './app.component';
    import { CoreModule } from './core/core.module';
    import { SharedModule } from './shared/shared.module';
  3. Open the app.component.html template file of the main component and replace its content with the following HTML snippet:
    <app-header></app-header>
    <app-footer></app-footer>

    We added the header and the footer component in the preceding snippet by using their CSS selectors.

If we run the serve command of the Angular CLI to preview the application, we should get the following:

Figure 2.2 – Basic layout

Figure 2.2 – Basic layout

We have already completed the basic layout of our blog application, and it looks great! But the header contains two additional links that we have not covered yet. We will learn how to use routing for activating those links in the following section.

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.

Adding blog capabilities with Scully

Our application currently does not have any specific logic regarding blog posts. It is a typical Angular application that uses routing. However, by adding a routing configuration, we have established the foundation for adding blog support using Scully.

Important Note

Scully needs at least one route defined in an Angular application to work correctly.

First, we need to install Scully in our application.

Installing the Scully library

We will use the add command of the Angular CLI to install Scully in our Angular application:

ng add @scullyio/init

The preceding command downloads and installs all the necessary npm packages for Scully to work correctly in our Angular application. It also modifies the structure of the Angular project to accommodate its specific needs.

It imports ScullyLibModule in our main application module:

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ContactModule } from './contact/contact.module';
import { CoreModule } from './core/core.module';
import { SharedModule } from './shared/shared.module';
import { ScullyLibModule } from '@scullyio/ng-lib';
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    CoreModule,
    SharedModule,
    ContactModule,
    ScullyLibModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

ScullyLibModule is the main module of the Scully library that contains various Angular services and directives that Scully will need.

It also creates a configuration file for the Scully library in the root folder of the Angular CLI workspace:

scully.my-blog.config.ts

import { ScullyConfig } from '@scullyio/scully';
export const config: ScullyConfig = {
  projectRoot: "./src",
  projectName: "my-blog",
  outDir: './dist/static',
  routes: {
  }
};

The configuration file contains information about our Angular application that Scully will need along the way:

  • projectRoot: The path containing the source code of the Angular application
  • projectName: The name of the Angular application
  • outDir: The output path of the Scully generated files

    Important Note

    The Scully output path must be different from the path that the Angular CLI outputs the bundle of your Angular application. The latter can be configured from the angular.json file.

  • routes: It contains the route configuration that will be used for accessing our blog posts. Scully will populate it automatically, as we will see in the following section.

Since we have installed Scully successfully in our Angular application, we can now configure it to initialize our blog.

Initializing our blog page

Scully provides a specific Angular CLI schematic for initializing an Angular application, such as a blog, by using Markdown (.md) files:

ng generate @scullyio/init:markdown

The previous command will start the configuration process of our blog by going through a list of questions (default values are shown inside parentheses):

  1. Type posts as the name of the blog module:
    Figure 2.5 – Blog module name

    Figure 2.5 – Blog module name

    This will create a new Angular module named posts.

  2. Leave the slug choice empty, and press Enter to accept the default value:
    Figure 2.6 – Markdown slug

    Figure 2.6 – Markdown slug

    The slug is a unique identifier for each post, and it is defined in the route configuration object of the module.

  3. Enter mdfiles as the path that Scully will use to store our actual blog post files:
    Figure 2.7 – Markdown files path

    Figure 2.7 – Markdown files path

    This will create an mdfiles folder inside the root path of our Angular CLI project. By default, it will also create a blog post for our convenience. We will learn how to create our own in the Displaying blog data on the home page section.

  4. Type posts as the name of the route for accessing our blog posts:
Figure 2.8 – Blog route name

Figure 2.8 – Blog route name

The name of the route is the path property of the route configuration object that will be created.

Scully performs various actions upon executing the preceding commands, including the creation of the routing configuration of the posts module:

posts-routing.module.ts

import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {PostsComponent} from './posts.component';
const routes: Routes = [
  {
    path: ':id',
    component: PostsComponent,
  },
  {
    path: '**',
    component: PostsComponent,
  }
];
@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
})
export class PostsRoutingModule {}

path for the first route is set to :id and activates PostsComponent. The colon character indicates that id is a route parameter. The id parameter is related to the slug property defined earlier in the Scully configuration. Scully works by creating one route for each blog post that we create. It uses the route configuration of the posts module and the main application module to construct the routes property in the Scully configuration file:

routes: {
  '/posts/:id': {
    type: 'contentFolder',
    id: {
      folder: "./mdfiles"
    }
  },
}

PostsComponent is the Angular component that is used to render the details of each blog post. The template file of the component can be further customized according to your needs:

posts.component.html

<h3>ScullyIo content</h3>
<hr>
<!-- This is where Scully will inject the static HTML -->
<scully-content></scully-content>
<hr>
<h4>End of content</h4>

You can customize all content in the previous template file except the <scully-content></scully-content> line, which is used internally by Scully.

At this point, we have completed the installation and configuration of Scully in our Angular application. It is now time for the final part of the project! In the next section, we will put Angular and Scully to cooperate and display blog posts in our Angular application.

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.

Summary

In this chapter, we learned how to combine the Angular framework with the Scully library to create a personal blog.

We saw how Angular uses the built-in router package to enhance web applications with in-app navigation. We also learned how to organize an Angular application into modules and how to navigate through these.

We introduced Jamstack to our Angular application using the Scully library and saw how easy it is to convert our application into a prerendered blog. We used the Scully interface to create some blog posts and display them on the screen.

In the following chapter, we will investigate another exciting feature of the Angular framework, forms. We are going to learn how to use them and build an issue-tracking system.

Practice questions

Let's take a look at a few practice questions:

  1. Which library do we use for routing in an Angular application?
  2. How do we add routing capabilities in an HTML anchor element?
  3. Which Angular pipe do we use for date formatting?
  4. What is the purpose of the assets folder in an Angular CLI application?
  5. Which route property do we use for lazily loading a module?
  6. Which Angular CLI command we use for installing Scully?
  7. Which service do we use for fetching Scully routes?
  8. What is the property binding?
  9. Which Angular directive do we use for iterating over an array in HTML?
  10. What is the difference between a standard Angular application and a Scully one?

Further reading

Here are some links to build upon what we learned in this chapter:

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Explore Angular's capabilities for building applications across different platforms
  • Combine popular web technologies with Angular such as monorepo, Jamstack, and PWA
  • Build your own libraries and schematics using Angular CDK and Angular CLI

Description

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.

Who is this book for?

This book is for developers with beginner-level Angular experience who want to become proficient in using essential tools and dealing with the various use cases they may encounter in Angular. Beginner-level knowledge of web application development and basic experience working with ES6 or TypeScript is essential before you dive in.

What you will learn

  • Set up Angular applications using Angular CLI and Nx Console
  • Create a personal blog with Jamstack and SPA techniques
  • Build desktop applications with Angular and Electron
  • Enhance user experience (UX) in offline mode with PWA techniques
  • Make web pages SEO-friendly with server-side rendering
  • Create a monorepo application using Nx tools and NgRx for state management
  • Focus on mobile application development using Ionic
  • Develop custom schematics by extending Angular CLI
Estimated delivery fee Deliver to Ukraine

Economy delivery 10 - 13 business days

$6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Jul 05, 2021
Length: 344 pages
Edition : 2nd
Language : English
ISBN-13 : 9781800205260
Vendor :
Google
Languages :
Tools :

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Product feature icon AI Assistant (beta) to help accelerate your learning
Estimated delivery fee Deliver to Ukraine

Economy delivery 10 - 13 business days

$6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Publication date : Jul 05, 2021
Length: 344 pages
Edition : 2nd
Language : English
ISBN-13 : 9781800205260
Vendor :
Google
Languages :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
$199.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts
$279.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total $ 168.97
Angular for Enterprise-Ready Web Applications
$79.99
Angular Cookbook
$38.99
Angular Projects
$49.99
Total $ 168.97 Stars icon

Table of Contents

11 Chapters
Chapter 1: Creating Your First Web Application in Angular Chevron down icon Chevron up icon
Chapter 2: Building an SPA Application with Scully and Angular Router Chevron down icon Chevron up icon
Chapter 3: Building an Issue Tracking System using Reactive Forms Chevron down icon Chevron up icon
Chapter 4: Building a PWA Weather Application Using Angular Service Worker Chevron down icon Chevron up icon
Chapter 5: Building a WYSIWYG Editor for the Desktop using Electron Chevron down icon Chevron up icon
Chapter 6: Building a Mobile Photo Geotagging Application Using Capacitor and 3D Maps Chevron down icon Chevron up icon
Chapter 7: Building an SSR Application for a GitHub Portfolio Using Angular Chevron down icon Chevron up icon
Chapter 8: Building an Enterprise Portal Using Nx Monorepo Tools and NgRx Chevron down icon Chevron up icon
Chapter 9: Building a Component UI Library Using Angular CLI and Angular CDK Chevron down icon Chevron up icon
Chapter 10: Customizing Angular CLI Commands Using Schematics Chevron down icon Chevron up icon
Other Books You May Enjoy Chevron down icon Chevron up icon

Customer reviews

Most Recent
Rating distribution
Full star icon Full star icon Full star icon Full star icon Half star icon 4.5
(18 Ratings)
5 star 72.2%
4 star 16.7%
3 star 5.6%
2 star 0%
1 star 5.6%
Filter icon Filter
Most Recent

Filter reviews by




Thota Nageswara rao May 17, 2023
Full star icon Full star icon Full star icon Empty star icon Empty star icon 3
Very good & easy unstand
Amazon Verified review Amazon
Luis Ceballos Feb 08, 2023
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Angular comes out with new versions every 6 months so even tho this book is just a few years old, there were a lot of dependencies and code updates that needed to be done, however, I was able to get all of the projects running with Angular 15. Some of the libraries are also outdated. There is a github repo that has some updates to account for this (for example, the firebase configurations are no longer accurate but the github repo has the correct ones).Because of this, I would say this isn't completely beginner friendly unless you're okay with doing some debugging and coding yourself to get things running.
Amazon Verified review Amazon
dreedski Jan 09, 2023
Full star icon Full star icon Full star icon Full star icon Full star icon 5
I recently purchased Angular Projects: Build Modern Web Apps by Exploring Angular 12 with 10 Different Projects and have been thoroughly enjoying it. The book is laid out in an easy-to-understand way, with clear explanations and helpful examples. As someone new to Angular, I appreciate the step-by-step approach of the projects, which have allowed me to gradually build my skills and confidence. And, as a bonus, the author even managed to sneak in a few jokes here and there to keep things light and fun. All in all, I highly recommend this book for anyone looking to dive into Angular and build some awesome modern web apps. So far, it's been a real "angular" of a good time!
Amazon Verified review Amazon
VBJoe Aug 02, 2022
Full star icon Empty star icon Empty star icon Empty star icon Empty star icon 1
Just bought the book. It's only a year or less out of production but it's already out of date. Don't bother doing the examples because they won't work!!!! Not Happy!!! Waste of valuable money!!!
Amazon Verified review Amazon
Novyastech Feb 16, 2022
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Excellent tour through best practices in Angular development. In addition, I'm learning about key Angular patterns, tools, and technologies. There's so much content that I'm sure it will be a great reference for a long time.The built in patterns in Angular automatically inform and mirror best practices for server side.This book covers a gambit of topics and technologies allowing a junior developer to easily pick it up and rapidly improve their skills. If you are a senior developer that has working in older angular versions or wants to get into it I would highly recommend this bookI find it refreshing to be able to follow a similar train of thought between server and front end. Great book for experienced developers looking to dive into Angular applications.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela