I have an web application that needs to work offline. I want to create my service worker to handle the API caching/sync logic, and let the angular's service worker cache the application assets.
I got the Angular worker to register and work perfectly. But I could not get to register my worker.
I tried three methods:
//1. Does not seem to do anything. No worker built, no worker starter.
ServiceWorkerModule.register('./offline.worker', {enabled: environment.production});
//2. The worker does not get built, registering fails since the script does not exist.
navigator.serviceWorker.register('./offline.worker');
//3. The worker gets built, it even gets started, but is not registered as ServiceWorker.
new Worker('./offline.worker', {type: 'module'});
So, option 1 works fine using the docs on the angular service worker when we point to the ngsw js file. Maybe it is simply not made to work for our workers?
Option 2 is the documented/standard way to register service workers, maybe I should simply get my worker to be built? I do not know where to start to do that... The complete error I get is
Uncaught (in promise) TypeError: Failed to register a ServiceWorker for scope ('http://localhost:8080/') with script ('http://localhost:8080/offline.worker'): A bad HTTP response code (404) was received when fetching the script.
Option 3 gets everything done as I would have expected. My worker is compiled the ./offline.worker is even changed to the bundle created! That's really how I would have expected to get things going. Unfortunately the worker created like so is only a web worker, not a ServiceWorker. This means it does not get to handle FetchEvents...
For sake of completeness, here is my tsconfig.worker.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/worker",
"lib": [
"es2018",
"webworker"
],
"types": []
},
"include": [
"src/**/*.worker.ts"
],
"exclude": [
"test.ts",
"**/*.spec.ts"
]
}
And the commands used to launch angular in production mode since service workers do not worker in develop:
ng build --prod
http-server dist/app
We've added custom logic along-side the Angular Service Worker by using a simple intermediary script to load both the generated ngsw script and our own custom script. This plays well with the Angular build process.
@NgModule({
//...
imports: [
ServiceWorkerModule.register('sw-master.js', {
enabled: environment.production
}),
//...
],
//...
});
...
"assets": [
...
"src/manifest.json",
"src/sw-custom.js",
"src/sw-master.js"
],...
importScripts('./ngsw-worker.js'); // generated by Angular build process
importScripts('./sw-custom.js');
Just an example... we were making it so users could click on push notifications and be directed to a particular app view.
(function () {
'use strict';
self.addEventListener('notificationclick', (event) => {
event.notification.close();
if (clients.openWindow && event.notification.data.url) {
event.waitUntil(clients.openWindow(event.notification.data.url));
}
});
}());
Only one service worker per scope is allowed to be registered, so you can not register more than one service worker.
You can handle both API caching/sync and application assets caching in a single service worker file.