Building Progressive Web Application (PWA) with Angular

Written by rodrigokamada | Published 2022/01/05
Tech Story Tags: angular | pwa | web-development | ghactions | progressive-web-apps | programming | angular-application | angular-development | web-monetization

TLDRProgressive Web Application (PWA) is a hybrid application built with common WEB technologies such as HTML, CSS, and JavaScript. Using the tools: Git, Node.js and npm, Visual Studio Code, and Node.JS are required to use the tools. Create the application with the Angular base structure using the.@angular/cli` with the route file and the SCSS style format. Add the Bootstrap CSS framework to an application to an Angular application. Use the tool to set up the service worker to create a service worker.via the TL;DR App

Introduction

AngularĀ is a development platform for building WEB, mobile and desktop applications using HTML, CSS and TypeScript (JavaScript). Currently, Angular is at version 14 and Google is the main maintainer of the project.

Progressive Web Application (PWA) is a hybrid application built with common WEB technologies such as HTML, CSS and JavaScript, that is, it works on computers and mobile phones compatible with the main browsers with the usability of a native mobile application.

@angular/pwaĀ is a library with Angular service worker support that provides a user experience as if the application were designed to run on your operating system and hardware.

Prerequisites

Before you start, you need to install and configure the tools:

Getting started

Create the Angular application

1.Ā Let's create the application with the Angular base structure using theĀ @angular/cliĀ with the route file and the SCSS style format.

ng new angular-pwa --routing true --style scss
CREATE angular-pwa/README.md (1056 bytes)
CREATE angular-pwa/.editorconfig (274 bytes)
CREATE angular-pwa/.gitignore (620 bytes)
CREATE angular-pwa/angular.json (3237 bytes)
CREATE angular-pwa/package.json (1075 bytes)
CREATE angular-pwa/tsconfig.json (863 bytes)
CREATE angular-pwa/.browserslistrc (600 bytes)
CREATE angular-pwa/karma.conf.js (1428 bytes)
CREATE angular-pwa/tsconfig.app.json (287 bytes)
CREATE angular-pwa/tsconfig.spec.json (333 bytes)
CREATE angular-pwa/.vscode/extensions.json (130 bytes)
CREATE angular-pwa/.vscode/launch.json (474 bytes)
CREATE angular-pwa/.vscode/tasks.json (938 bytes)
CREATE angular-pwa/src/favicon.ico (948 bytes)
CREATE angular-pwa/src/index.html (296 bytes)
CREATE angular-pwa/src/main.ts (372 bytes)
CREATE angular-pwa/src/polyfills.ts (2338 bytes)
CREATE angular-pwa/src/styles.scss (80 bytes)
CREATE angular-pwa/src/test.ts (745 bytes)
CREATE angular-pwa/src/assets/.gitkeep (0 bytes)
CREATE angular-pwa/src/environments/environment.prod.ts (51 bytes)
CREATE angular-pwa/src/environments/environment.ts (658 bytes)
CREATE angular-pwa/src/app/app-routing.module.ts (245 bytes)
CREATE angular-pwa/src/app/app.module.ts (393 bytes)
CREATE angular-pwa/src/app/app.component.scss (0 bytes)
CREATE angular-pwa/src/app/app.component.html (23364 bytes)
CREATE angular-pwa/src/app/app.component.spec.ts (1088 bytes)
CREATE angular-pwa/src/app/app.component.ts (216 bytes)
āœ” Packages installed successfully.
    Successfully initialized git.

2.Ā Install and configure the Bootstrap CSS framework. Do steps 2 and 3 of the postĀ Adding the Bootstrap CSS framework to an Angular application.

3.Ā Add theĀ @angular/pwdĀ library to set up the Angular service worker.

ng add @angular/pwa
ℹ Using package manager: npm
āœ” Found compatible package version: @angular/[email protected].
āœ” Package information loaded.

The package @angular/[email protected] will be installed and executed.
Would you like to proceed? Yes
āœ” Package successfully installed.
CREATE ngsw-config.json (631 bytes)
CREATE src/manifest.webmanifest (1346 bytes)
CREATE src/assets/icons/icon-128x128.png (1253 bytes)
CREATE src/assets/icons/icon-144x144.png (1394 bytes)
CREATE src/assets/icons/icon-152x152.png (1427 bytes)
CREATE src/assets/icons/icon-192x192.png (1790 bytes)
CREATE src/assets/icons/icon-384x384.png (3557 bytes)
CREATE src/assets/icons/icon-512x512.png (5008 bytes)
CREATE src/assets/icons/icon-72x72.png (792 bytes)
CREATE src/assets/icons/icon-96x96.png (958 bytes)
UPDATE angular.json (3621 bytes)
UPDATE package.json (1615 bytes)
UPDATE src/app/app.module.ts (804 bytes)
UPDATE src/index.html (509 bytes)
āœ” Packages installed successfully.

Note:

The filesĀ ngsw-config.json,Ā src/manifest.webmanifestĀ and icons were added and the filesĀ angular.json,Ā package.json,Ā src/app/app.module.tsĀ andĀ src/index.htmlĀ were changed to the application.

  • ngsw-config.json:Ā Service workerĀ configuration file allows you to configure cache strategy and files and other configurations.
  • src/manifest.webmanifest: Application configuration file orĀ WEB app manifestĀ file allows you to configure the name, colors, icons and other configurations.
  • angular.json: The service worker configuration was added.
  • package.json: TheĀ @angular/service-workerĀ library was added.
  • src/app/app.module.ts: The service worker configuration was added.
  • src/index.html: Manifest file configuration and theme color was added.

4.Ā Remove the contents of theĀ AppComponentĀ class from theĀ src/app/app.component.tsĀ file. Create theĀ updateOnlineStatusĀ method to check the browser connection status as below.

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {

  isOnline: boolean;

  constructor() {
    this.isOnline = false;
  }

  public ngOnInit(): void {
    this.updateOnlineStatus();

    window.addEventListener('online',  this.updateOnlineStatus.bind(this));
    window.addEventListener('offline', this.updateOnlineStatus.bind(this));
  }

  private updateOnlineStatus(): void {
    this.isOnline = window.navigator.onLine;
    console.info(`isOnline=[${this.isOnline}]`);
  }

}

5.Ā Remove the contents of theĀ src/app/app.component.htmlĀ file. Add the HTML code to display the browser connection status as below.

<div class="container-fluid py-3">
  <h1>Angular Progressive Web Application (PWA)</h1>

  <div class="row my-5">
    <div class="col text-end">
      Status:
    </div>
    <div class="col">
      <span class="badge bg-success" *ngIf="isOnline">Online</span>
      <span class="badge bg-danger" *ngIf="!isOnline">Offline</span>
    </div>
  </div>
</div>

6.Ā Run the application with the command below.

npm start

> [email protected] start
> ng serve

āœ” Browser application bundle generation complete.

Initial Chunk Files   | Names         |  Raw Size
vendor.js             | vendor        |   2.05 MB | 
styles.css, styles.js | styles        | 486.85 kB | 
polyfills.js          | polyfills     | 339.20 kB | 
scripts.js            | scripts       |  76.33 kB | 
main.js               | main          |  10.71 kB | 
runtime.js            | runtime       |   6.86 kB | 

                      | Initial Total |   2.95 MB

Build at: 2022-01-01T17:33:35.241Z - Hash: 1e50e703667ef1c0 - Time: 3557ms

** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **


āœ” Compiled successfully.

7.Ā Access the URLĀ http://localhost:4200/Ā and check if the application is working.

8.Ā Change the contents of theĀ AppComponentĀ class from theĀ src/app/app.component.tsĀ file. Import theĀ SwUpdateĀ service and create theĀ updateVersionĀ andĀ closeVersionĀ methods to check for available updates for the application as below.

import { Component, OnInit } from '@angular/core';
import { SwUpdate, VersionReadyEvent } from '@angular/service-worker';
import { filter, map } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {

  isOnline: boolean;
  modalVersion: boolean;

  constructor(private swUpdate: SwUpdate) {
    this.isOnline = false;
    this.modalVersion = false;
  }

  public ngOnInit(): void {
    this.updateOnlineStatus();

    window.addEventListener('online',  this.updateOnlineStatus.bind(this));
    window.addEventListener('offline', this.updateOnlineStatus.bind(this));

    if (this.swUpdate.isEnabled) {
      this.swUpdate.versionUpdates.pipe(
        filter((evt: any): evt is VersionReadyEvent => evt.type === 'VERSION_READY'),
        map((evt: any) => {
          console.info(`currentVersion=[${evt.currentVersion} | latestVersion=[${evt.latestVersion}]`);
          this.modalVersion = true;
        }),
      );
    }
  }

  private updateOnlineStatus(): void {
    this.isOnline = window.navigator.onLine;
    console.info(`isOnline=[${this.isOnline}]`);
  }

  public updateVersion(): void {
    this.modalVersion = false;
    window.location.reload();
  }

  public closeVersion(): void {
    this.modalVersion = false;
  }

}

9.Ā Change the contents of theĀ src/app/app.component.htmlĀ file. Add the HTML code to display if there are updates available for the application as below.

<div class="container-fluid py-3">
  <h1>Angular Progressive Web Application (PWA)</h1>

  <div class="row my-5">
    <div class="col text-end">
      Status:
    </div>
    <div class="col">
      <span class="badge bg-success" *ngIf="isOnline">Online</span>
      <span class="badge bg-danger" *ngIf="!isOnline">Offline</span>
    </div>
  </div>
</div>

<div class="w-100 position-absolute top-0" *ngIf="modalVersion">
  <div class="alert alert-secondary m-2">
    <button type="button" class="btn-close position-absolute top-0 end-0 m-1" aria-label="Close" (click)="closeVersion()"></button>
    A new version of this app is available. <a href="" (click)="updateVersion()">Update now</a>
  </div>
</div>

10.Ā Access the URLĀ http://localhost:4200/Ā and check if the application is working.

11.Ā Install theĀ @angular/cdkĀ library.

npm install @angular/cdk

12.Ā Change the contents of theĀ AppComponentĀ class from theĀ src/app/app.component.tsĀ file. Import theĀ PlatformĀ service and create theĀ loadModalPwa,Ā addToHomeScreenĀ andĀ closePwaĀ methods to check the operational system and the browser and display how to add the application as below.

import { Component, OnInit } from '@angular/core';
import { Platform } from '@angular/cdk/platform';
import { SwUpdate, VersionReadyEvent } from '@angular/service-worker';
import { filter, map } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {

  isOnline: boolean;
  modalVersion: boolean;
  modalPwaEvent: any;
  modalPwaPlatform: string|undefined;

  constructor(private platform: Platform,
              private swUpdate: SwUpdate) {
    this.isOnline = false;
    this.modalVersion = false;
  }

  public ngOnInit(): void {
    this.updateOnlineStatus();

    window.addEventListener('online',  this.updateOnlineStatus.bind(this));
    window.addEventListener('offline', this.updateOnlineStatus.bind(this));

    if (this.swUpdate.isEnabled) {
      this.swUpdate.versionUpdates.pipe(
        filter((evt: any): evt is VersionReadyEvent => evt.type === 'VERSION_READY'),
        map((evt: any) => {
          console.info(`currentVersion=[${evt.currentVersion} | latestVersion=[${evt.latestVersion}]`);
          this.modalVersion = true;
        }),
      );
    }

    this.loadModalPwa();
  }

  private updateOnlineStatus(): void {
    this.isOnline = window.navigator.onLine;
    console.info(`isOnline=[${this.isOnline}]`);
  }

  public updateVersion(): void {
    this.modalVersion = false;
    window.location.reload();
  }

  public closeVersion(): void {
    this.modalVersion = false;
  }

  private loadModalPwa(): void {
    if (this.platform.ANDROID) {
      window.addEventListener('beforeinstallprompt', (event: any) => {
        event.preventDefault();
        this.modalPwaEvent = event;
        this.modalPwaPlatform = 'ANDROID';
      });
    }

    if (this.platform.IOS && this.platform.SAFARI) {
      const isInStandaloneMode = ('standalone' in window.navigator) && ((<any>window.navigator)['standalone']);
      if (!isInStandaloneMode) {
        this.modalPwaPlatform = 'IOS';
      }
    }
  }

  public addToHomeScreen(): void {
    this.modalPwaEvent.prompt();
    this.modalPwaPlatform = undefined;
  }

  public closePwa(): void {
    this.modalPwaPlatform = undefined;
  }

}

13.Ā Change the contents of theĀ src/app/app.component.htmlĀ file. Add the HTML code to display how to add the application as below.

<div class="container-fluid py-3">
  <h1>Angular Progressive Web Application (PWA)</h1>

  <div class="row my-5">
    <div class="col text-end">
      Status:
    </div>
    <div class="col">
      <span class="badge bg-success" *ngIf="isOnline">Online</span>
      <span class="badge bg-danger" *ngIf="!isOnline">Offline</span>
    </div>
  </div>
</div>

<div class="w-100 position-absolute top-0" *ngIf="modalVersion">
  <div class="alert alert-secondary m-2">
    <button type="button" class="btn-close position-absolute top-0 end-0 m-1" aria-label="Close" (click)="closeVersion()"></button>
    A new version of this app is available. <a href="" (click)="updateVersion()">Update now</a>
  </div>
</div>

<div class="w-100 position-absolute bottom-0" *ngIf="modalPwaPlatform === 'ANDROID' || modalPwaPlatform === 'IOS'">
  <div class="alert alert-secondary m-2">
    <button type="button" class="btn-close position-absolute top-0 end-0 m-1" aria-label="Close" (click)="closePwa()"></button>
    <!-- Android -->
    <div *ngIf="modalPwaPlatform === 'ANDROID'" (click)="addToHomeScreen()">
      Add this WEB app to home screen
    </div>
    <!-- iOS with Safari -->
    <div *ngIf="modalPwaPlatform === 'IOS'">
      To install this WEB app on your device, tap the "Menu" button
      <img src="https://res.cloudinary.com/rodrigokamada/image/upload/v1641089482/Blog/angular-pwa/safari_action_button_38x50.png" class="ios-menu m-0" />
      and then "Add to home screen" button
      <i class="bi bi-plus-square"></i>
    </div>
  </div>
</div>

14.Ā Add the style in theĀ src/app/app.component.scssĀ file as below.

.ios-menu {
  width: 14px;
}

15.Ā Ready! Access the URLĀ http://localhost:4200/Ā and check if the application is working. See the application working onĀ GitHub PagesĀ andĀ Stackblitz.

15.1.Ā Android version

Modal to add to home screen

Confirmation to add to home screen

15.2.Ā iOS version

Modal to add to home screen

Menu to add to home screen

The application repository is available atĀ https://github.com/rodrigokamada/angular-pwa.

This tutorial was posted on myĀ blogĀ in Portuguese.


Written by rodrigokamada | šŸ‘Øā€šŸ’» Software Developer | āœļø Technical Content Creator | šŸ¤ Open Source Contributor | šŸŽ™ļø Speaker | šŸ™Œ Ambassador
Published by HackerNoon on 2022/01/05