2
votes

I'm writing a PWA in Angular that needs to be ready to go offline from a single page load. The documentation for the service worker life cycle specifies that the worker is installed but not active immediately: https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle

This is by design and can be overruled by calling clients.claim().

The problem with this is that Angular loads the service worker after the whole app has been loaded so claim() has nothing left to claim. That means the page has to be reloaded and then all the calls are cached (which works in my case) but that seems like a poor user experience.

I see and have tried the following solutions but have gotten stuck on all of them:

  • Listen to the install event on the service worker in app.component.ts so that I can wait to do my calls that need to be cached until after claim() has been called. SwUpdate knows nothing about this event (only updates) and while the following could be an option, it's pointless because the service worker has not been loaded so navigator.serviceworker is null: navigator.serviceWorker.controller.addEventListener('statechange', (stateChangeEvent: Event) => {})
  • Add this to the service worker: self.addEventListener('install', (event) => {}) but when testing, I need to tick "update on reload" and so each pageload is going to have an install event, meaning I need to have some way to have it happen only once ever to prevent getting in an infinite loop of reloads. I tried doing this with the localstorage but the service worker has no access to that. Apparently it has access to IndexedDB but I still need to learn how that works and this whole "reload the page on first load" strategy doesn't sit right with me
  • Ensure the service worker js is loaded by Angular before anything else. Of course, the install is async so that would mean race conditions with my app. I haven't been able to find how to influence this load order. I tried placing ServiceWorkerModule.register('/custom-sw.js') first in app.module.ts but that made no difference.
1
Apologies, after further digging, I've deleted my answer. Are you using the latest version of the @angular/service-worker? - Remy
the ngsw-worker calls clients.claim() within a generator, when 'activate' is complete - Remy
I am using the latest version, yes. I'm currently using a service worker generated with workbox. Within the service worker itself, I can choose when claim() is called so that's not an issue. The question is that Angular installs it with ServiceWorkerModule.register('/custom-sw.js') but it still ends up being loaded last. If that could be done first (in the head or something), that could provide more control. - Martijn Van Loocke

1 Answers

0
votes

I believe you might be missing the call to skipWaiting() (link).

skipWaiting() when called from the install event will move right on to the activate event without the user reloading the page.

clients.claim() in the activate event will claim any clients available at that time.

If you do not call skipWaiting(), the activate event is not fired until next reload of the page.