Creating a Progressive Web App (PWA) service to include all features Angular
Progressive Web Apps is the future of web apps, it brings a lot of capabilities to make our life easier and making it a lot closer to a native mobile app. It is not a native app but it does give you the offline capabilities, installation, updates.
I have been doing PWA for a few months and loving it, I would like to build the service that includes all PWA features and use it on my components. HaveĀ a look below to see the implementation, it includes
- a check for network status (see if you are offline on online)
- to check if the app is installed or not, you can use this to show a prompt to end-user, asking them to install it.
- option to automatically check for updates, you can wan the user that there is a new version and you can refresh the browser to get new updates. this can be really helpful if the app is never closed and running on KIOSK mode.
import { ApplicationRef, Injectable } from '@angular/core'; import { SwUpdate } from '@angular/service-worker'; import { Observable, Subject } from 'rxjs'; import { concat, interval } from 'rxjs'; import { first } from 'rxjs/operators'; export enum NetworkStatus { ONLINE = 'online', OFFLINE = 'offline' } export enum InstalledStatus { INSTALLED = 'appinstalled', BEFORE_INSTALL = 'beforeinstallprompt' } @Injectable({ providedIn: 'root' }) // @ts-ignore export class PwaService { offline: boolean; installEvent: any; public installed: Subject<any> = new Subject(); constructor(private swUpdate: SwUpdate, private appRef: ApplicationRef) { this.subscribeNetworkStatus(); this.subscribeInstalledStatus(); this.subscribeApplicationUpdates(); this.registerAutoUpdateCheck(); } subscribeNetworkStatus() { window.addEventListener(NetworkStatus.ONLINE, this.onNetworkStatusChange.bind(this)); window.addEventListener(NetworkStatus.OFFLINE, this.onNetworkStatusChange.bind(this)); } subscribeInstalledStatus() { window.addEventListener(InstalledStatus.BEFORE_INSTALL, event => { this.installEvent = event; this.installed.next(event); }); } subscribeApplicationUpdates() { this.swUpdate.available.subscribe(event => { const r = confirm('New version available, do you wish to update ?'); if (r) { window.location.reload(); } }); } registerAutoUpdateCheck() { const appIsStable$ = this.appRef.isStable.pipe(first(isStable => isStable === true)); const everySixHours$ = interval(6 * 60 * 60 * 1000); const everySixHoursOnceAppIsStable$ = concat(appIsStable$, everySixHours$); everySixHoursOnceAppIsStable$.subscribe(() => this.swUpdate.checkForUpdate()); } onNetworkStatusChange() { this.offline = !navigator.onLine; } install() { this.installEvent.prompt(); } }
in your component, you can use it like this
import { Component } from '@angular/core'; import { PwaService } from '@services/pwa.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) // @ts-ignore export class AppComponent implements OnInit, OnDestroy { constructor(pwa: PwaService) { // if you import your service as a public then you can use it like below, else you will have to set a variable } }
you html can be like this
<main role="main" class="container-fluid"> <div *ngIf="pwa.offline">offline</div> <button class="github-star-badge" *ngIf="pwa.installed | async" (click)="pwa.install()"> Install </button> <router-outlet></router-outlet> </main>