angularservice-workerangular-pwa

Angular PWA with custom offline page


In an Angular (8) app I'd like to add a custom offline page (just a plain simple html-file to begin with). I have set up my app as a PWA (using @angular/pwa and configured everything so that it at least works smoothly while being online).

However, I've had a hard time making updates available for PWA users. So, after many hours of try and error I decided to exclude index.html from the ngsw-config.json. This has -of course- the effect that index.html gets loaded every single time (not so bad, 'cause it's so small). If there are any updates index.html links to different JS-files and these files get loaded immediately. So, as I said before, the PWA works just as I like it to be.

Now I want to display an offline.html when the user starts the PWA being offline. So I've add offline.html to ngsw-config.json and I've created a custom Service Worker including the official ngsw-worker.js:

importScripts('./ngsw-worker.js');

I'm also using this custom service worker instead of the official one:

ServiceWorkerModule.register('./custom-worker.js', { enabled: true, registrationStrategy: registrationStrategy })

So far, everything still works as expected. Behavior is just like before. Now I wanted to include the offline behavior in my custom worker:

importScripts('./ngsw-worker.js');
self.addEventListener('fetch', function(event) {
    return event.respondWith(
      caches.match(event.request)
      .then(function(response) {
        let requestToCache = event.request.clone();

        return fetch(requestToCache).then().catch(error => {
          // Check if the user is offline first and is trying to navigate to a web page
          if (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html')) {
            // Return the offline page
            return caches.match("offline.html");
          }
        });
      })
    );
  });

This script comes from: https://stackoverflow.com/a/56738140/4653997 Unfortunately this is the part that doesn't work at all. For now I'm pretty much stuck. I have no idea what do next. I thought service workers get executed whether index.html can get loaded or not.

Any help would be appreciated.


Solution

  • I've got it working!

    In the end it was a relatively simple fetch event listener I had to add to my custom service worker:

    // listen to every fetch event
    self.addEventListener('fetch', function (event) {
        const request = event.request;
        
        // filter for html document fetches (should only result in one single fetch) --> index.html
        if (request.method === "GET" && request.destination === "document") {
    
            // only intercept if there was a problem fetching index.html
            event.respondWith(
                fetch(request).catch(function (error) {
                    console.error("[onfetch] Failed. Serving cached offline fallback", error);
    
                    // return offline page from cache instead
                    return caches.match("/assets/offline.html");
                }));
        }
    });
    
    // use all the magic of the Angular Service Worker
    importScripts('./ngsw-worker.js');