Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Register own Service Worker on Angular alongside its own worker

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
like image 787
Salketer Avatar asked Jan 09 '20 07:01

Salketer


People also ask

How do you by pass the service worker for a particular request?

To bypass the service worker, set ngsw-bypass as a request header, or as a query parameter. The value of the header or query parameter is ignored and can be empty or omitted.

Why ng serve does not work with service workers?

You can't serve your Angular project with service worker via ng serve , as the documentation for Service Workers state that it requires https. The only way to run it without https/on a server, is to use ng build and run the http-server locally to test your project.

What is serviceWorker in Angular?

Adding a service worker to an Angular application is one of the steps for turning an application into a Progressive Web App (also known as a PWA). At its simplest, a service worker is a script that runs in the web browser and manages caching for an application. Service workers function as a network proxy.

What is NGSW worker?

What is the ngsw. json file? This is the runtime configuration file, that the Angular Service worker will use. This file is built based on the ngsw-config. json file, and contains all the information needed by the Angular Service Worker to know at runtime about which files it needs to cache, and when.


Video Answer


1 Answers

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.

app.module.ts

@NgModule({
  //...
  imports: [
    ServiceWorkerModule.register('sw-master.js', {
      enabled: environment.production
    }),
    //...
  ],
  //...
});

angular.json

...
"assets": [
  ...
  "src/manifest.json",
  "src/sw-custom.js",
  "src/sw-master.js"
],...

sw-master.js

importScripts('./ngsw-worker.js'); // generated by Angular build process
importScripts('./sw-custom.js');

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));
    }
  });
}());
like image 156
Adam Dunkerley Avatar answered Sep 27 '22 16:09

Adam Dunkerley