Francesco Sessa

Angular PWA: installazione e configurazione

Le PWA, ovvero le Progressive Web Apps, sono uno strumento moderno da cui molti siti web possono trarre vantaggio, sia in termini di tempi di caricamento che per l’interazione con funzionalità native. Nel presente articolo andremo a creare una piccola PWA di test utilizzando Angular.

Cos’è una PWA?

Una PWA è un tipo di applicazione scritta utilizzando, di base, HTML, Javascript e CSS, e distribuita sul web, che può essere eseguita su qualunque dispositivo (o quasi) che utilizza un browser con funzionalità standard in termini di compatibilità di motori JS.

Le Progressive Web Apps fanno largo uso dei service workers, o meglio, delle loro API messe a disposizione dai browser, al fine di ottenere meccanismi di caching, accesso a funzionalità native e, inoltre, fanno si che l’utente abbia la percezione di stare interagendo con un’applicazione.

Creiamo una PWA in Angular

Il primo passo da compiere è la creazione di un’applicazione Angular, utilizzando il classico comando

ng new angular-pwa-test

Per quel che dobbiamo fare può essere opportuno installare già in questa fase il routing di Angular; infine, durante il wizard di creazione dell’applicazione, a meno di casi particolari, seleziono SCSS come motore per gli stili.

NOTA: Per poter utilizzare la CLI di Angular è necessario predisporre preventivamente l’ambiente di sviluppo. Consiglio sempre di installare nvm [node version manager] per poter gestire diverse versioni di node sulla stessa macchina e quindi poter gestire anche differenti versioni di Angular CLI [ma anche Cordova, Ionic, etc etc]

L’esecuzione del comando precedente, quindi, va a creare l’applicazione Angular.

ng new
Esecuzione del comando per la generazione di un’applicazione Angular

Una volta creata l’applicazione dobbiamo procedere ad installare @angular/pwa, uno schematic che, tra le altre cose, installa @angular/service-worker come dipendenza, genera dei files per la configurazione della cache e per il manifest della web app.

npx -p @angular/cli@14.0.3 ng add @angular/pwa@14.2.10

NOTA: Per poter utilizzare la CLI di Angular è necessario predisporre preventivamente l’ambiente di sviluppo. Consiglio sempre di installare nvm [node version manager] per poter gestire diverse versioni di node sulla stessa macchina e quindi poter gestire anche differenti versioni di Angular CLI [ma anche Cordova, Ionic, etc etc]

Il comando precedente ha avuto il seguente output.

ng add @angular/pwa
Esecuzione del comando ng add @angular/pwa

Dall’output dell’esecuzione si comprende che, oltre ad installare alcune dipendenze, è stato creato il file ngsw-config.json, il manifest della webapp, alcune icone e, infine sono stati aggiornati angular.jsonpackage.jsonindex.html e app.module.ts.

Il file ngsw-config.json contiene, in formato JSON, la configurazione per la generazione del service worker. In esso vanno configurati l’entry point HTML dell’applicativo e la modalità di caching per le risorse [es. gli assets].

ngsw-config.json
Configurazione di default contenuta nel file ngsw-config.json

Il file manifest.webmanifest contiene, sostanzialmente, le informazioni su come dovrà apparire la nostra Progressive Web App quando verrà lanciata: è il file nel quale poter impostare, ad esempio, il logo e i colori di background.

manifest.webmanifest
Contenuto di default del file manifest.webmanifest

Molto importanti sono inoltre le modifiche apportate al file angular.json. Tali modifiche infatti, sono necessarie per indicare che dovrà essere abilitato il service worker la cui configurazione è il file ngsw-config.json.

angular.json
Modifiche apportate al file angular.json

Infine all’interno del modulo dell’applicazione viene importato il ServiceWorkerModule e registrato il service worker ngsw-config.js in ambiente di produzione.

app.module.ts
Modifiche apportate al file app.module.ts all’aggiunta della pwa

Esecuzione della PWA

Normalmente un’applicazione Angular viene eseguita lanciando il comando ng serve, ma in questo caso, il server interno di Angular non risulta essere sufficiente. Inoltre c’è da tenere in considerazione che, qualora non si voglia/possa utilizzare HTTPS il service worker verrà registrato solamente su localhost.

NOTA: Per poter utilizzare la CLI di Angular è necessario predisporre preventivamente l’ambiente di sviluppo. Consiglio sempre di installare nvm [node version manager] per poter gestire diverse versioni di node sulla stessa macchina e quindi poter gestire anche differenti versioni di Angular CLI [ma anche Cordova, Ionic, etc etc]

A questo punto si può procedere effettuando la build dell’applicazione, ricordando che il service worker viene abilitato per la sola modalità di prod, pertanto dobbiamo utilizzare il comando

ng build --configuration production

Il comando ha generato, come di consueto, nella cartella dist/angular-pwa-test la versione dell’applicazione che dovremo andare ad utilizzare. Per fare ciò entriamo da terminale all’interno di questa cartella e lanciamo il comando

http-server

Questo comando farà partire un web server per servire i files contenuti nella cartella corrente, sulla porta 8080. E’ uno strumento abbastanza utile e potente che, con delle opportune configurazioni, permette di servire anche in HTTPS oppure di abilitare il CORS. Qualora dovessimo avere un errore per mancanza dell’applicativo possiamo procedere alla sua installazione con il comando npm i -g http-server.

Accedendo alla pagina http://localhost:8080 vediamo che l’applicazione è correttamente partita.

angular pwa 1
Prima esecuzione

Utilizzando gli strumenti di ispezione messi a disposizione dal browser, accedendo alle risorse dell’applicazione quindi al path Application>ServiceWorkers vediamo come il service worker ngsw-worker.js è stato correttamente caricato.

caricamento del service worker

Per dimostrare che sia funzionante possiamo anche ricaricare la pagina e verificare negli strumenti di rete che le risorse non vengono richieste al server ma gestite direttamente dal service worker.

caching delle risorse

Come suggerito dalla documentazione ufficiale possiamo provare a creare un disservizio di rete e verificare il comportamento della nostra PWA. Per fare questo possiamo modificare il valore della select accanto alla label “Disable cache” dal valore No Throttling al valore Offline, in questo modo al caricamento della pagina l’applicazione non avrà accesso ad internet. Cosa accade alla nostra applicazione? Nel caso di un’applicazione normale il browser mostrerà un messaggio per indicare che si è disconnessi dalla rete, mentre nel caso del service worker l’applicazione verrà caricata normalmente.

esecuzione offline con service worker

Visualizzazione dello stato nell’applicazione

Cerchiamo di effettuare delle piccole modifiche al codice della nostra applicazione per interagire con il service worker. Per fare questo dobbiamo andare a modificare il componente (l’unico attualmente) contenuto all’interno della nostra applicazione.

Modifichiamo il file app.component.ts andando ad inserire una variabile che indichi se il sistema è online che verrà inizializzata ad un valore di default; procediamo anche a implementare un metodo che, accedendo alla proprietà window.navigator.onLine va ad aggiornare questa variabile. Infine procediamo a metterci in ascolto su due eventi che chiameranno il metodo per l’aggiornamento dello stato.

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}]`);
  }

}

Andiamo ora a modificare la parte html per contenuto nel file app.component.html per mostrare lo stato.

<div class="container-fluid py-3">
  <h1>Angular PWA Test</h1>

  <div class="row">
    <div class="col text-end">
      Status:
    </div>
    <div class="col">
      <span class="badge bg-success" *ngIf="isOnline; else offlineBlock">Online</span>
      <ng-template #offlineBlock>
        <span class="badge bg-danger" >Offline</span>
      </ng-template>
    </div>
  </div>
</div>

Possiamo procedere ad una nuova build e rilanciare il server per verificare il nuovo comportamento. Per evitare di dover rilanciare i comandi manualmente possiamo aggiungere la seguente riga nella sezione scripts del file package.json

"pwa": "ng build --configuration production && http-server ./dist/angular-pwa-test -p 8080"

Possiamo così lanciare il comando npm run pwa e vediamo come l’esecuzione della PWA mostra nell’interfaccia lo stato corrente.

PWA aggiornamento status online
PWA aggiornamento status offline

Caching delle chiamate API

La nostra PWA è quindi correttamente funzionante e quindi possiamo concentrarci sull’utilizzare le funzionalità messe a disposizione dai service workers.

Per poter testare il caching delle chiamate API prima di tutto andiamo ad inserire nella nostra applicazione di test due pulsanti che serviranno per effettuare due distinte chiamate API a dei servizi mock. Per fare questo iniziamo con il creare un service con il comando

ng generate service api

All’interno del service andiamo ad implementare i due metodi che effettueranno le due chiamate API; chiaramente per effettuare questa operazione sarà necessario importare nell’app.module il modulo HttpClientModule e utilizzarlo all’interno del nostro service. Per semplicità all’interno dell’environment ho anche aggiunto un baseUrl a cui puntare per poter eventualmente modificare il puntamento da servizi mock a servizi reali.

api service
Codice dell’API service

Nel app.component.ts possiamo implementare due metodi che andranno a chiamare i servizi.

test1Perform() {
    this.service.test1().subscribe(x => console.log(x));
  }
  test2Perform() {
    this.service.test2().subscribe(x => console.log(x));
  }
Facebook
Twitter
LinkedIn

Approfondisci

Articoli correlati