13

I am following angular's best practice in order to make PWA. After making production build (ng build --prod --aot), I am also running the service worker from dist, on localhost: http-server -p 8080 -c-1 dist When I am trying to sync the worker with my Angular, using:

navigator.serviceWorker.ready.then(function(swRegistration) {

            console.log('swReady');
});

Nothing happens, and seems that SW is not communicating with Angular. Working with a remote server (uploading dist) does work. So seems that the problem is dist not working with ng serve. What am I doing wrong?

Yuvals
  • 3,094
  • 5
  • 32
  • 60
  • do the ng build files not work on your localhost? Or are you only referring to the files from `ng serve`? – wentjun Apr 29 '19 at 14:16
  • they do. ng serve is working, but not communicating with the service worker – Yuvals Apr 29 '19 at 14:21
  • service workers don't work on `ng serve`, use any other server, https://angular.io/guide/service-worker-getting-started#serving-with-http-server – Ashish Ranjan Apr 29 '19 at 14:23
  • 3
    understood and have read the guide. The problem is - it makes it impossible to test without building and uploading every times. Is there no other way to develop locally and test without building? – Yuvals Apr 29 '19 at 14:27
  • You're running `ng build` and then hosting it with `http-server`, I understand how `ng serve` matters. Can you explain how it's being used? Also, can you post your `angular.json` file? Specifically the "configurations" -> "production" section – Vlad274 Apr 29 '19 at 14:44

8 Answers8

32

It seems that currently we cannot use service worker with ng serve --prod. However we can make a workaround.

  1. We build the project ng build --prod

  2. From the dist location we take the ngsw-worker.js and ngsw.json files and copy them to the src folder.

  3. We modify our angular.json file in order to serve them. We find the property "projects": {"[my-project-name]": {... "architect": {... "build": {... "options": {... "assets": [... and there we add these two items – "src/ngsw-worker.js", "src/ngsw.json".

  4. We serve – ng serve --prod.

I have reached till that point. The browser says that the SW is activated and running. The only consideration now is that if we change something in the SW, we need to rebuild again and make the same steps. But I believe we can develop more rapidly.

Good luck!

Anton Mitsev
  • 602
  • 9
  • 15
  • --prod now exits with a message saying the flag is deprecated and suggests using `--configuration production` that fails for me because `Error: Expected to find an ngsw-config.json configuration file in the C:\Users\username\project\` Using workbox and a sw.js file. – Rin and Len Feb 14 '22 at 12:51
  • You can also try creating symbolic links to keep a "dynamic" copy of those files so you don't have to worry about manually updating them. – M Muller Mar 14 '22 at 13:58
7

Note: this is based on Angular under Ionic. For plain Angular some paths are different (e.g. www -> dist) and 'ng' command should be used instead of 'ionic', so adjust accordingly.

Step 0. Add @angular/pwa, it will create service worker that is used in production (see any other instructions for that).

Step 1. Enable service worker use in debug build. Usually SW is enabled only for production, e.g. in file src/app/app.module.ts, one of two implementations, change 'enabled' to true (or can alternatively add property 'useServiceWorker' to 'environment.ts' and 'environment.production.ts' files and set them both to 'true', later when debugging is done, will need to change to 'false' only the setting in environment.ts file):

  imports: [ ...
-    ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
+    ServiceWorkerModule.register('ngsw-worker.js', { enabled: true }),
  ],

or

  imports: [ ...
    ServiceWorkerModule.register('ngsw-worker.js'),
  ],
  providers: [ ...
-    { provide: SwRegistrationOptions, useFactory: () => ({ enabled: environment.production }) },
+    { provide: SwRegistrationOptions, useFactory: () => ({ enabled: true }) },

Step 2. Add some hooks to the 'scripts' in package.json file. They are aimed at ensuring that all file hashes are correct in 'ngsw.json' file and SW could load files properly:

{
    "scripts": { ...
+        "ionic:serve:before": "...(anything already there)... && echo {} > src/ngsw.json && cp www/ngsw.json src/ngsw.json",
+        "ionic:build:after":"...(anything already there)... && npm run ngsw:rebuild",
+        "postbuild": "...(anything already there)... && npm run ngsw:rebuild",
+        "ngsw:rebuild": "ngsw-config www ngsw-config.json && cp www/ngsw.json src/ngsw.json",

For ionic serve to pick that file, it adds a blank file src/ngsw.json in pre-build step and tries to copy calculated hashes from www folder. If 'ionic serve' fails, make sure to run 'ionic build' first. I know this step is iffy, but there are no better hooks e.g. in 'ng serve' - there are open feature requests for that.

Since 'src/ngsw.json' is created/generated, it should not be under source control/git, so add line '/src/ngsw.json' to '.gitignore' file.

Step 3. Add service worker source and config to your debug build. Add lines to file angular.json that would copy 'ngsw-worker.js' and 'ngsw.json' to www/ (production build does it automatically, this will copy it during debug build):

{
  "projects": {
    "app": {
      "architect": {
        "build": {
          "options": {
            "assets": [
+              {
+                "glob": "ngsw-worker.js",
+                "input": "node_modules/@angular/service-worker",
+                "output": "."
+              },
+              "src/ngsw.json",
...

Step 4. Build debug version (ensures current hashes in ngsw.json) and then serve with HTTPS. 'ionic serve' has --ssl and --external options (--external allows opening app on e.g. a phone):

ionic build
ionic ssl generate ;# creates SSL certs in .ionic/ssl/: cert.pem and key.pem
ionic serve --external --ssl

Another option is to use any appropriate HTTP server, which can do SSL and proxy for single-page apps (SPA). Package 'http-server' does not work well when proxy and ssl options are mixed, so use e.g. 'local-web-server' instead:

npm install -g local-web-server
ws -p 8100 --cert .ionic/ssl/cert.pem --key ,ionic/ssl/key.pem --https --spa index.html --directory www

Step 5. Add certificate to your browser CA list. Use the following steps for Chrome (or google it for other browsers):

https://www.nullalo.com/en/chrome-how-to-install-self-signed-ssl-certificates/

Done! Open Chrome and navigate to https://localhost:8100, see console for loaded Service Worker. If there are any errors in console, retrace prior steps - frameworks change rapidly and something might be already different.

iva2k
  • 450
  • 4
  • 9
  • Thanks for the explanation - works great for development with live reload :). Just one question: Why not use `{"glob": "ngsw.json", "input": "www", "output": "."}` instead of `"src/ngsw.json"` in `angular.json`? (This seems to work just as well and avoids adding copy scripts to `package.json` and any extra gitignores) – lentschi Dec 27 '21 at 13:10
5

With Chrome, you can enable a flag for treating a specific host as if it is a secure origin, allowing service workers to work:

./chrome --unsafely-treat-insecure-origin-as-secure=http://your.insecure.site:8080

To launch chrome from the terminal, you do need to know the executable location. This will be system dependent. For MacOs:

open /Applications/Google\ Chrome.app/ --args  --unsafely-treat-insecure-origin-as-secure=http://your.insecure.site:8080
TmKVU
  • 2,910
  • 2
  • 16
  • 30
3

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.

Because ng serve does not work with service workers, you must use a separate HTTP server to test your project locally.

wentjun
  • 40,384
  • 10
  • 95
  • 107
  • I am running http-server locally, but the problem is the `ng serve`. – Yuvals Apr 29 '19 at 14:28
  • 1
    As the documentation has stated, Angular's service worker is not configured to work on `ng serve`. The only way is to build the project everytime you need to test it. :( – wentjun Apr 29 '19 at 14:30
  • will this allow me to run ng serve with local HTTP server? – Yuvals Apr 30 '19 at 06:33
  • Sorry, could you please rephrase your question? What do you mean by run ng serve with local HTTP server? ng serve by default already uses localhost, which is your local server! – wentjun Apr 30 '19 at 07:54
  • @Yuvals Localhost is considered a secure origin for development reasons. See the question [I get an error message about "Only secure origins are allowed". Why?](https://www.chromium.org/blink/serviceworker/service-worker-faq) – TmKVU Apr 30 '19 at 08:06
  • @TmKVU Yes I know. As explained on my answer, you can run your built Angular files (created by running the command, `ng build`), followed by using `http-server` to run the application on localhost! – wentjun Apr 30 '19 at 08:08
  • so back to base 1 - as you stated, no way to run `ng serve` and `service-worker` on localhost? The only option would be `local build` and `service worker` ? how is it possible to compile and debug? build it every time? does not make much sense as the build time is very long – Yuvals Apr 30 '19 at 08:35
  • It has been so far... has anyone tried to use `ng serve --ssl`? – Anton Mitsev Oct 09 '20 at 08:37
  • 1
    "it requires https" - it only requires a secure context. Localhost is treated as one per default and then there is the `unsafely-treat-insecure-origin-as-secure` for developers. Therefore I suggest you rephrase your answer as the actual problem is just the way `ng serve` works (or better: does not work). – Jey DWork Mar 21 '21 at 20:42
2

Firstly, make sure in your angular.json file the following is present: "ServiceWorker": true

      "configurations": {
        "production": {
          "serviceWorker": true,
          "fileReplacements":
            ...

Second, install http-server and run

http-server -p 8080 -c-1 dist/app

Third, your service workers might not work on ip-addresses. Therefore, you should used the following instead:

http://localhost:8080/

Source: Getting an Angular PWA service worker installed on localhost

Stef Van Looveren
  • 302
  • 1
  • 5
  • 15
1

According to Angular 14.2 docs, you can now debug and test your service worker in the browser using "ng serve" rather than configuring an http server. Here is what you have to do assuming you already have PWA package added to the project and you've updated to angular v14.2:

  • Set "serviceWorker": true in angular.json
  • Enable service worker in app.module.ts
  • Change "registrationStrategy" to "registerImmediately" (Not necessary step) but just to see the results faster.
0

If you want to use other browsers that don't support TimKVU answer then the solution is to use ngrok (https://ngrok.com)

It sets up a secure tunnel to whatever server you are using. You run it with:

ngrok http 4200

Simply connect to the domain shown on screen.

PeterS
  • 2,818
  • 23
  • 36
0

According to the Angular 14.2 release notes (released on 8/25), ng serve now has Service Worker support, which is outstanding, because it means you get live-reload while developing your Service Worker. The Angular docs don't yet seem to be updated to talk more about this.

EDIT: The following command works to use ng serve with a Service Worker:

ng serve --configuration=production

This assumes that a Service Worker has already been configured for the project. If not, first run the following to add it:

ng add @angular/pwa
JWess
  • 602
  • 7
  • 17