Progressive Web App with Angular 17

Updated : 03/01/2024 danny


We are going to make our standard Web Application a PWA ( Progressive Web App )

We will use the Angular javascript framework version 17.0.8

Progressive Web App avec Angular

What are we going to do ?


This is step 7 of our Angular guide which will allow us to obtain a PWA type Web Application . The basic Angular project that we will use already has the following characteristics

  • Generated with Angular CLI
  • Routing
  • Lazy Loading
  • The Bootstrap CSS framework
  • Module management
  • Server Side Rendering

All the sources created are indicated at the end of the tutorial.

The application is at the following address


Before you start

2008 App Store is an application store distributed by Apple on mobile devices running iOS.
2012 Google Play Store is an online application store created by Google.

2014 , annual global sales of smartphones exceed one billion units.

2017 two mobile operating systems share the global smartphone market.
Android 85.1% and IOS 14.8%

More than 50% of internet searches are now carried out on mobile in France.

Android and iOS operating systems are written in different programming languages.
So iOS apps run on Objective-C/Swift , while Android apps run on Java .

If you want to develop a mobile application, you can either develop a specific version for each platform or use a cross platform tool that will allow you to have only one source for both OS.
It will nevertheless be necessary to manage registration on the app stores.
In all cases, users must go to a download platform to install an app.

In 2015, Frances Berriman and Google engineer Alex Russell proposed the term “progressive web apps”

A PWA can be consulted like a classic website, from a secure URL but allows a user experience similar to that of a mobile application, without the constraints of the latter (submission to App-Stores, significant use of the device's memory …).


Initialization

We will use the Angular documentation to apply this technique.
https://angular.io/guide/service-worker-intro

We will use the angular-cli command
ng add

# Adding the pwa library
ng add @angular/pwa

Angular-cli automatically made a number of changes to our project.

  • modification of the package.json file (in this example we will update the dependencies and the descriptors)
  • editing angular.json file
  • creating the ngsw-config.json file
  • creation of the src/manifest.webmanifest file
  • modifying the src/index.html file
  • modifying the src/app.module.ts file
  • creating icons src/app/assets/icons
    Auto-created icons do not work during deployment.
    ​​​​​​​Get the ones that are available on the final repo.
package.json
  "dependencies": {
    "@angular/animations": "17.0.8",
    "@angular/common": "17.0.8",
    "@angular/compiler": "17.0.8",
    "@angular/core": "17.0.8",
    "@angular/forms": "17.0.8",
    "@angular/platform-browser": "17.0.8",
    "@angular/platform-browser-dynamic": "17.0.8",
    "@angular/platform-server": "17.0.8",
    "@angular/router": "17.0.8",
    "@angular/service-worker": "17.0.8",
    "@angular/ssr": "17.0.9",
    "@fortawesome/fontawesome-free": "6.5.1",
    "bootstrap": "5.3.2",
    "express": "4.18.2",
    "rxjs": "7.8.1",
    "tslib": "2.6.2",
    "zone.js": "0.14.2"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "17.0.9",
    "@angular-eslint/builder": "17.1.1",
    "@angular-eslint/eslint-plugin": "17.1.1",
    "@angular-eslint/eslint-plugin-template": "17.1.1",
    "@angular-eslint/schematics": "17.1.1",
    "@angular-eslint/template-parser": "17.1.1",
    "@angular/cli": "17.0.9",
    "@angular/compiler-cli": "17.0.8",
    "@types/express": "4.17.21",
    "@types/jasmine": "5.1.4",
    "@types/node": "20.10.6",
    "@typescript-eslint/eslint-plugin": "6.17.0",
    "@typescript-eslint/parser": "6.17.0",
    "eslint": "8.56.0",
    "jasmine-core": "5.1.1",
    "karma": "6.4.2",
    "karma-chrome-launcher": "3.2.0",
    "karma-coverage": "2.2.1",
    "karma-jasmine": "5.1.0",
    "karma-jasmine-html-reporter": "2.1.0",
    "typescript": "5.2.2"
  }
angular.json
{

"assets": [
  "src/favicon.ico",
  "src/assets",
  "src/manifest.webmanifest"
],

........


            "production": {
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "1mb",
                  "maximumError": "1mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "4kb",
                  "maximumError": "4kb"
                }
              ],
              "outputHashing": "all",
              "serviceWorker": "ngsw-config.json"
            },
ngsw-config.json
{
  "$schema": "./node_modules/@angular/service-worker/config/schema.json",
  "index": "/index.html",
  "assetGroups": [
    {
      "name": "app",
      "installMode": "prefetch",
      "resources": {
        "files": [
          "/favicon.ico",
          "/index.html",
          "/manifest.webmanifest",
          "/*.css",
          "/*.js"
        ]
      }
    },
    {
      "name": "assets",
      "installMode": "lazy",
      "updateMode": "prefetch",
      "resources": {
        "files": [
          "/assets/**",
          "/media/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)"
        ]
      }
    }
  ]
}
src/manifest.webmanifest
{
  "name": "angular-starter",
  "short_name": "angular-starter",
  "theme_color": "#1976d2",
  "background_color": "#fafafa",
  "display": "standalone",
  "scope": "./",
  "start_url": "./",
  "icons": [
    {
      "src": "assets/icons/icon-72x72.png",
      "sizes": "72x72",
      "type": "image/png",
      "purpose": "maskable any"
    },
    {
      "src": "assets/icons/icon-96x96.png",
      "sizes": "96x96",
      "type": "image/png",
      "purpose": "maskable any"
    },
    {
      "src": "assets/icons/icon-128x128.png",
      "sizes": "128x128",
      "type": "image/png",
      "purpose": "maskable any"
    },
    {
      "src": "assets/icons/icon-144x144.png",
      "sizes": "144x144",
      "type": "image/png",
      "purpose": "maskable any"
    },
    {
      "src": "assets/icons/icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png",
      "purpose": "maskable any"
    },
    {
      "src": "assets/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "maskable any"
    },
    {
      "src": "assets/icons/icon-384x384.png",
      "sizes": "384x384",
      "type": "image/png",
      "purpose": "maskable any"
    },
    {
      "src": "assets/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "maskable any"
    }
  ]
}
src/index.html
<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>AngularStarter</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">

  <!-- Google tag (gtag.js) -->
  <script async="" src="https://www.googletagmanager.com/gtag/js?id=YOUR-ID"></script>
  <script>
    window.dataLayer = window.dataLayer || [];
    function gtag() { dataLayer.push(arguments); }
    gtag('js', new Date());

    gtag('config', 'YOUR-ID');
  </script>

  <link rel="manifest" href="manifest.webmanifest">
  <meta name="theme-color" content="#1976d2">
</head>

<body>
  <app-root></app-root>
  <noscript>Please enable JavaScript to continue using this application.</noscript>
</body>

</html>
src/app/app.config.ts
import { ApplicationConfig, isDevMode } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideClientHydration } from '@angular/platform-browser';
import { provideServiceWorker } from '@angular/service-worker';

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes), provideClientHydration(), provideServiceWorker('ngsw-worker.js', {
        enabled: !isDevMode(),
        registrationStrategy: 'registerWhenStable:30000'
    })]
};

Conclusion

We will test how the PWA works

The following scripts allow you to do this

  • npm run build
  • npm run serve:ssr:angular-starter
  • http://localhost:4000/

 

All that remains is to open Chrome Developer Tools with Ctrl + Shift + J
And run a test of the Lighthouse tool

  • Audits
  • Run audits

Source Code

The source code used at the beginning of the tutorial is available on github
https://github.com/ganatan/angular-ssr

The source code obtained at the end of this tutorial is available on github
https://github.com/ganatan/angular-react-pwa


The following steps will allow you to obtain a prototype application

The last step provides an example application


The source code of this final application is available on GitHub
https://github.com/ganatan/angular-app