Progressive Web App with Angular 10

15/09/20 dannyVersion Française

What are we going to do ?

We will turn our Standard Web Application into a PWA (Progressive Web App)
We will use the Angular version 10.1.1 javascript framework.

This is Step 8 of our  Angular guide which will allow us to obtain a PWA Web Application.
We will use an existing project whose characteristics are

  • Generated with Angular CLI
  • Routing
  • Lazy Loading
  • Framework CSS Bootstrap
  • Server Side Rendering
  • HttpClient
  • Transfer State

All created sources 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, the global annual sales of smartphones exceed one billion units.

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

More than 50% of Internet searches are now done on mobile in France.

Android and iOS operating systems are written in different programming languages.
IOS applications work on Objective-C / Swift, while Android applications 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 still manage registration on the app store.
In all cases users must go to a download platform to install an app.

In 2015, Frances Berriman and Google engineer Alex Russell propose the term "progressive web apps"

A PWA looks like a regular 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 memory


Initialisation

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


We will use the command angular-cli
ng add

# Ajout de la librairie pwa
ng add @angular/pwa --project angular-starter

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

  • modify the package.json file (in this example we will update dependencies and descriptors)
  • edit angular.json file
  • creating the ngsw-config.json file
  • creating the src/manifest.webmanifest file
  • edit src/index.html file
  • edit src/app.browser.module.ts file
  • icons creationsrc/app/assets/icons
package.json
  "dependencies": {
    "@angular/animations": "10.1.1",
    "@angular/common": "10.1.1",
    "@angular/compiler": "10.1.1",
    "@angular/core": "10.1.1",
    "@angular/forms": "10.1.1",
    "@angular/platform-browser": "10.1.1",
    "@angular/platform-browser-dynamic": "10.1.1",
    "@angular/platform-server": "10.1.1",
    "@angular/router": "10.1.1",
    "@angular/service-worker": "10.1.1",
    "@fortawesome/fontawesome-free": "5.14.0",
    "@nguniversal/express-engine": "10.1.0",
    "bootstrap": "4.5.2",
    "express": "4.17.1",
    "jquery": "3.5.1",
    "rxjs": "6.6.3",
    "tslib": "2.0.1",
    "zone.js": "0.11.1"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "0.1001.1",
    "@angular/cli": "10.1.1",
    "@angular/compiler-cli": "10.1.1",
    "@nguniversal/builders": "10.1.0",
    "@types/express": "4.17.8",
    "@types/node": "14.10.1",
    "@types/jasmine": "3.5.14",
    "@types/jasminewd2": "2.0.8",
    "codelyzer": "6.0.0",
    "jasmine-core": "3.6.0",
    "jasmine-spec-reporter": "5.0.2",
    "karma": "5.2.2",
    "karma-chrome-launcher": "3.1.0",
    "karma-coverage-istanbul-reporter": "3.0.3",
    "karma-jasmine": "4.0.1",
    "karma-jasmine-html-reporter": "1.5.4",
    "protractor": "7.0.0",
    "ts-node": "9.0.0",
    "tslint": "6.1.3",
    "typescript": "4.0.2"
  }
angular.json
{

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

........


"configurations": {
    "production": {
      "fileReplacements": [
        {
          "replace": "src/environments/environment.ts",
          "with": "src/environments/environment.prod.ts"
        }
      ],
      "optimization": true,
      "outputHashing": "all",
      "sourceMap": false,
      "extractCss": true,
      "namedChunks": false,
      "extractLicenses": true,
      "vendorChunk": false,
      "buildOptimizer": true,
      "budgets": [
        {
          "type": "initial",
          "maximumWarning": "2mb",
          "maximumError": "5mb"
        },
        {
          "type": "anyComponentStyle",
          "maximumWarning": "6kb",
          "maximumError": "10kb"
        }
      ],
      "serviceWorker": true,
      "ngswConfigPath": "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/**",
          "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
        ]
      }
    }
  ]
}
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"
    },
    {
      "src": "assets/icons/icon-96x96.png",
      "sizes": "96x96",
      "type": "image/png"
    },
    {
      "src": "assets/icons/icon-128x128.png",
      "sizes": "128x128",
      "type": "image/png"
    },
    {
      "src": "assets/icons/icon-144x144.png",
      "sizes": "144x144",
      "type": "image/png"
    },
    {
      "src": "assets/icons/icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png"
    },
    {
      "src": "assets/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "assets/icons/icon-384x384.png",
      "sizes": "384x384",
      "type": "image/png"
    },
    {
      "src": "assets/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}
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">
  <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.browser.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser';

import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '../environments/environment';

@NgModule({
  imports: [
    AppModule,
    BrowserModule.withServerTransition({ appId: 'angular-starter' }),
    BrowserTransferStateModule,
    ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production })
  ],
  bootstrap: [AppComponent],
})
export class AppBrowserModule { }

Conclusion

Nous allons tester le fonctionnement de la PWA

The npm run start script executes the ng serve command
ng serve does not work with service workers

To test, you must use an http server.

Les scripts suivants pemettent donc de tester notre pwa

  • npm run build:ssr
  • npm run serve:ssr
  • http://localhost:4000/

 

All you have to do is open Chrome development tools with Ctrl + Maj + J
And run a test of the Lighthouse tool

  • Audits
  • Run audits