Progressive Web App avec Angular 18

Mis à jour : 14/06/2024 danny


Nous allons faire de notre Application Web standard une PWA (Progressive Web App )

Nous utiliserons le framework javascript Angular version 18.0.2

Progressive Web App avec Angular

Qu'allons nous faire ?


Il s'agit de l'étape 7 de notre guide Angular qui nous permettra d'obtenir une Application Web de type PWA.Le projet Angular de base que nous utiliserons dispose déjà des caractéristiques suivantes

  • Généré avec Angular CLI
  • Le Routing
  • Le Lazy Loading
  • Le framework CSS Bootstrap
  • La gestion des Modules
  • Server Side Rendering​​​​​​​

Tous les sources créés sont indiqués en fin de tutoriel.

L' application est à l'adresse suivante 


Avant de commencer

2008 App Store est un magasin d'applications distribué par Apple sur les appareils mobiles fonctionnant sous iOS. 
2012 Google Play Store est un magasin d'applications en ligne créé par Google. 

2014, les ventes mondiales annuelles de smartphones dépassent le milliard d'unités.

2017 deux systèmes d’exploitation pour mobile se partage le marché mondial des smartphones.
Android 85.1 %  et IOS 14.8%

Plus de 50% des recherches internet sont dorénavant effectuées sur mobile en France. 

Les systèmes d’exploitation Android et iOS sont écrits dans des langages de programmation différents.
Les applications iOS fonctionnent ainsi sur Objective-C / Swift, tandis que les applications Android fonctionnent sur Java.

Si vous voulez développez une application mobile , vous pouvez soit développer une version spécifique pour chaque plateforme ou utiliser un outil cross plateform qui vous permettra de n'avoir qu'un seul source pour les deux OS.
Il faudra néammoins gèrer l'inscription sur les app store.
Dans tous les cas les utilisateurs doivent aller sur une plateforme de téléchargement pour installer une appli.

En 2015, Frances Berriman et l’ingénieur de Google Alex Russell proposent le terme de  "progressive web apps" 

Une PWA se consulte comme un site web classique, depuis une URL sécurisée mais permet une expérience utilisateur similaire à celle d'une application mobile, sans les contraintes de cette dernière (soumission aux App-Stores, utilisation importante de la mémoire de l'appareil…).


Initialisation

Nous utiliserons la documentation Angular pour appliquer cette technique.
https://angular.io/guide/service-worker-intro

Nous utiliserons la commande angular-cli
ng add

# Ajout de la librairie pwa
ng add @angular/pwa

Angular-cli a procédé automatiquement à un certain nombre de modifications sur notre projet.

  • modification du fichier package.json ( dans cet exemple nous mettrons à jour les dépendances et les descripteurs)
  • modification du fichier angular.json
  • création du fichier ngsw-config.json
  • création du fichier src/manifest.webmanifest
  • modification du fichier src/index.html
  • modification du fichier src/app.config.ts
  • création des icônes src/app/assets/icons
    Les icônes créées automatiquement ne fonctionnent pas lors du déploiement​​​​​​​.
    ​​​​​​​Récupérez celles qui sont disponibles sur le repo final.
     
package.json
  "dependencies": {
    "@angular/animations": "18.0.2",
    "@angular/common": "18.0.2",
    "@angular/compiler": "18.0.2",
    "@angular/core": "18.0.2",
    "@angular/forms": "18.0.2",
    "@angular/platform-browser": "18.0.2",
    "@angular/platform-browser-dynamic": "18.0.2",
    "@angular/platform-server": "18.0.2",
    "@angular/router": "18.0.2",
    "@angular/service-worker": "18.0.2",
    "@angular/ssr": "18.0.3",
    "@fortawesome/fontawesome-free": "6.5.2",
    "bootstrap": "5.3.3",
    "express": "4.19.2",
    "rxjs": "7.8.1",
    "tslib": "2.6.3",
    "zone.js": "0.14.7"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "18.0.3",
    "@angular/cli": "18.0.3",
    "@angular/compiler-cli": "18.0.2",
    "@types/express": "4.17.21",
    "@types/jasmine": "5.1.4",
    "@types/node": "20.14.2",
    "angular-eslint": "18.0.1",
    "eslint": "9.4.0",
    "jasmine-core": "5.1.2",
    "karma": "6.4.3",
    "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.4.5",
    "typescript-eslint": "8.0.0-alpha.20"
  }
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

Nous allons tester le fonctionnement de la PWA

Les scripts suivants permettent de le faire

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

 

Il ne reste qu'à ouvrir les outils de développement Chrome avec Ctrl + Maj + J
Et executer un test de l'outil Lighthouse

  • Audits
  • Run audits

Code source

Le code source utilisé en début de tutoriel est disponible sur github
https://github.com/ganatan/angular-ssr

Le code source obtenu à la fin de ce tutoriel est disponible sur github
https://github.com/ganatan/angular-react-pwa


Les étapes suivantes vous permettront d'obtenir une application prototype

La dernière étape permet d'obtenir un exemple d'application


Le code source de cette application finale est disponible sur GitHub
https://github.com/ganatan/angular-app