Contribute to help us improve!

Are there edge cases or problems that we didn't consider? Is there a technical pitfall that we should add? Did we miss a comma in a sentence?

If you have any input for us, we would love to hear from you and appreciate every contribution. Our goal is to learn from projects for projects such that nobody has to reinvent the wheel.

Let's collect our experiences together to make room to explore the novel!

To contribute click on Contribute to this page on the toolbar.

Angular Progressive Web App

Progressive web applications (PWA) are web application that offer better user experience than the traditional ones. In general, they solve problems related with reliability and speed:

  • Reliability: PWA are stable. In this context stability means than even with slow connections or even with no network at all, the application still works. To achieve this, some basic resources like styles, fonts, requests, …​ are stored; due to this caching, it is not possible to assure that the content is always up-to-date.

  • Speed: When an users opens an application, he or she will expect it to load almost immediately (almost 53% of users abandon sites that take longer that 3 seconds, source: https://developers.google.com/web/progressive-web-apps/#fast).

PWA uses a script called service worker, which runs in background and essentially act as proxy between web app and network, intercepting requests and acting depending on the network conditions.

Assumptions

This guide assumes that you already have installed:

  • NodeJS

  • npm package manager

  • Angular CLI / Nx CLI

Sample Application

My Thai Star recommendation
Figure 1. Basic angular PWA.

To explain how to build PWA using angular, a basic application is going to be built. This app will be able to ask for resources and save in the cache in order to work even offline.

Step 1: Create a new project

This step can be completed with one simple command using the Angular CLI: ng new <name>, where <name> is the name for the app. In this case, the app is going to be named basic-ng-pwa. If you are using Nx CLI, you can use the command nx generate @nrwl/angular:app <name> in your Nx workspace. You can follow this guide if you want to get started with Nx workspace.

Step 2: Create a service

Web applications usually uses external resources, making necessary the addition of services which can get those resources. This application gets a dish from My Thai Star’s back-end and shows it. To do so, a new service is going to be created.

  • go to project folder: cd basic-ng-pwa. If using Nx, go to the root folder of the workspace.

  • run ng generate service data. For Nx CLI, specify the project name with --project flag. So the command becomes ng generate service data --project=basic-ng-pwa

  • Modify data.service.ts, environment.ts, environment.prod.ts

To retrieve data with this service, you have to import the module HttpClient and add it to the service’s constructor. Once added, use it to create a function getDishes() that sends HTTP request to My Thai Start’s back-end. The URL of the back-end can be stored as an environment variable MY_THAI_STAR_DISH.

data.service.ts

  ...
  import { HttpClient } from '@angular/common/http';
  import { MY_THAI_STAR_DISH } from '../environments/environment';
  ...

  export class DataService {
    constructor(private http: HttpClient) {}

    /* Get data from Back-end */
    getDishes() {
      return this.http.get(MY_THAI_STAR_DISH);
    }
    ...
  }

environments.ts

  ...
  export const MY_THAI_STAR_DISH =
  'https://mts-devonfw-core.cloud.okteto.net/api/services/rest/dishmanagement/v1/dish/1';
  ...

environments.prod.ts

  ...
  export const MY_THAI_STAR_DISH =
  'https://mts-devonfw-core.cloud.okteto.net/api/services/rest/dishmanagement/v1/dish/1';
  ...

Step 3: Use the service

The component AppComponent implements the interface OnInit and inside its method ngOnInit() the subscription to the services is done. When a dish arrives, it is saved and shown (app.component.html).

  ...
  import { DataService } from './data.service';
  export class AppComponent implements OnInit {
  dish: { name: string; description: string } = { name: '', description: ''};

  ...
  ngOnInit() {
    this.data
      .getDishes()
      .subscribe(
        (dishToday: { dish: { name: string; description: string } }) => {
          this.dish = {
            name: dishToday.dish.name,
            description: dishToday.dish.description,
          };
        },
      );
  }
}

Step 4: Structures, styles and updates

This step shows code interesting inside the sample app. The complete content can be found in devon4ts-samples.

index.html

To use the Montserrat font add the following link inside the head tag of the app’s index.html file.

  <link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet">

styles.scss

  body {
    ...
    font-family: 'Montserrat', sans-serif;
  }

app.component.ts

This file is also used to reload the app if there are any changes.

  • SwUpdate: This object comes inside the @angular/pwa package and it is used to detect changes and reload the page if needed.

  ...
  import { SwUpdate } from '@angular/service-worker';

  export class AppComponent implements OnInit {

  ...
    constructor(updates: SwUpdate, private data: DataService) {
      updates.available.subscribe((event) => {
        updates.activateUpdate().then(() => document.location.reload());
      });
    }
    ...
  }

Step 5: Make it Progressive.

Install Angular PWA package with ng add @angular/pwa --project=<name>. As before substitute name with basic-ng-pwa.

The above command completes the following actions:

  1. Adds the @angular/service-worker package to your project.

  2. Enables service worker build support in the CLI.

  3. Imports and registers the service worker in the app module.

  4. Updates the index.html file:

    • Includes a link to add the manifest.json file.

    • Adds meta tags for theme-color.

    • Installs icon files to support the installed Progressive Web App (PWA).

    • Creates the service worker configuration file called ngsw-config.json, which specifies the caching behaviors and other settings.

manifest.json

manifest.json is a file that allows to control how the app is displayed in places where native apps are displayed.

Fields

name: Name of the web application.

short_name: Short version of name.

theme_color: Default theme color for an application context.

background_color: Expected background color of the web application.

display: Preferred display mode.

scope: Navigation scope of this web application’s application context.

start_url: URL loaded when the user launches the web application.

icons: Array of icons that serve as representations of the web app.

Additional information can be found here.

ngsw-config.json

ngsw-config.json specifies which files and data URLs have to be cached and updated by the Angular service worker.

Fields

  • index: File that serves as index page to satisfy navigation requests.

  • assetGroups: Resources that are part of the app version that update along with the app.

    • name: Identifies the group.

    • installMode: How the resources are cached (pre-fetch or lazy).

    • updateMode: Caching behavior when a new version of the app is found (pre-fetch or lazy).

    • resources: Resources to cache. There are three groups.

      • files: Lists patterns that match files in the distribution directory.

      • urls: URL patterns matched at runtime.

  • dataGroups: UsefulIdentifies the group. for API requests.

    • name: Identifies the group.

    • urls: URL patterns matched at runtime.

    • version: Indicates that the resources being cached have been updated in a backwards-incompatible way.

    • cacheConfig: Policy by which matching requests will be cached

      • maxSize: The maximum number of entries, or responses, in the cache.

      • maxAge: How long responses are allowed to remain in the cache.

        • d: days. (5d = 5 days).

        • h: hours

        • m: minutes

        • s: seconds. (5m20s = 5 minutes and 20 seconds).

        • u: milliseconds

      • timeout: How long the Angular service worker will wait for the network to respond before using a cached response. Same dataformat as maxAge.

      • strategy: Caching strategies (performance or freshness).

  • navigationUrls: List of URLs that will be redirected to the index file.

Additional information can be found here.

Step 6: Configure the app

manifest.json

Default configuration.

 
 

ngsw-config.json

At assetGroups → resources → urls: In this field the google fonts API is added in order to use Montserrat font even without network.

  "urls": [
          "https://fonts.googleapis.com/**"
        ]

At the root of the json: A data group to cache API calls.

  {
    ...
    "dataGroups": [{
      "name": "mythaistar-dishes",
      "urls": [
        "https://mts-devonfw-core.cloud.okteto.net/api/services/rest/dishmanagement/v1/dish/1"
      ],
      "cacheConfig": {
        "maxSize": 100,
        "maxAge": "1h",
        "timeout": "10s",
        "strategy": "freshness"
      }
    }]
  }

Step 7: Check that your app is a PWA

To check if an app is a PWA lets compare its normal behavior against itself but built for production. Run in the project’s root folder the commands below:

ng build --prod to build the app using production settings.(nx build <name> --prod in Nx CLI)

npm install http-server to install an npm module that can serve your built application. Documentation here.

Go to the dist/basic-ng-pwa/ folder running cd dist/basic-ng-pwa. In an Nx workspace, the path will be dist/apps/basic-ng-pwa

http-server -o to serve your built app.

Http server running
Figure 2. Http server running on localhost:8081.

 

In another console instance run ng serve (or nx serve basic-ng-pwa for Nx) to open the common app (not built).

.Angular server running
Figure 3. Angular server running on localhost:4200.

 

The first difference can be found on Developer tools → application, here it is seen that the PWA application (left) has a service worker and the common (right) one does not.

Application comparison
Figure 4. Application service worker comparison.

 

If the "offline" box is checked, it will force a disconnection from network. In situations where users do not have connectivity or have a slow, one the PWA can still be accessed and used.

Online offline apps
Figure 5. Offline application.

 

Finally, browser extensions like Lighthouse can be used to test whether an application is progressive or not.

Lighthouse report
Figure 6. Lighthouse report.