56

I'm having a hard time integrating angulars service worker into my application. I followed the guide and it works so far. I can create a shortcut on my homescreen and launch into my app. The problem is that my app somehow doesn't update. If I change the name of a button, build the app and put it onto my server the app still shows the old version until I hit F5 (restarting the app doesn't help either).

I tried to put the following code into my ngOnInot of my app but it didn't help

ngOnInit() {
if (this._SwUpdate.isEnabled) {

  setInterval( () => {
    this._SwUpdate.checkForUpdate().then(() => console.log('checking for updates'));
  }, this.updateInterval);

  this._SwUpdate.available.subscribe(() => {

    console.log('update found');

    this._SwUpdate.activateUpdate().then(() => {
      console.log('updated');
      window.location.reload();
    });

  });

}

}

The app is running on my apache2 linux machine. Is my apache caching something or why doesn't my app realize that there is a new version?

Thanks in advance for your help :)

Edit:

My ngsw-config.json

{
  "index": "/index.html",
  "assetGroups": [{
    "name": "roomPlan",
    "installMode": "prefetch",
    "resources": {
      "files": [
        "/index.html",
        "/*.css",
        "/*.js"
      ]
    }
  }, {
    "name": "assets",
    "installMode": "lazy",
    "updateMode": "prefetch",
    "resources": {
      "files": [
        "/assets/**"
      ]
    }
  }]
}

Edit 2:

It works if I run the app local using "http-server" but when I copy the files over to my apache it doesn't detect the update. In the networking tab I can see that the interval works, the app gets a new "ngsw.json" from the server every 3 seconds. If I update my app I can see that there are new hash values inside of the response for "ngsw.json". After that the browser loads the new "index.html" and "main.***.js" from my server but the app doesn't apply the new version. According to my code it should say "update found" but nothing happens.

1
  • Angular docs are the best resource for this, pleaser refer this page Commented Jun 4, 2020 at 18:32

7 Answers 7

61

You will probably need to tell the service worker to check the server for updates, I usually use a service for this:

export class UpdateService {

  constructor(public updates: SwUpdate) {
    if (updates.isEnabled) {
      interval(6 * 60 * 60).subscribe(() => updates.checkForUpdate()
        .then(() => console.log('checking for updates')));
    }
  }

  public checkForUpdates(): void {
    this.updates.available.subscribe(event => this.promptUser());
  }

  private promptUser(): void {
    console.log('updating to new version');
    this.updates.activateUpdate().then(() => document.location.reload()); 
  }

In your app-component.ts:

  constructor(private sw: UpdateService) {
    // check the service worker for updates
    this.sw.checkForUpdates();
  }

For whatever reason, Angular sometimes does not register the service worker properly. So you can modify `main.ts` :

Replace:

platformBrowserDynamic().bootstrapModule(AppModule);

With:

platformBrowserDynamic().bootstrapModule(AppModule).then(() => {
  if ('serviceWorker' in navigator && environment.production) {
    navigator.serviceWorker.register('ngsw-worker.js');
  }
}).catch(err => console.log(err));
Sign up to request clarification or add additional context in comments.

31 Comments

Just that I understand SwUpdate right... I go into my app, write "hello world" in any component, ng build --prod my app, copy it to my apache2 and the service worker should refresh it... right?
That's right yes, check the debug log at yourdomain.com/ngsw/state and see if there is anything in the Task queue
I just completely reseted my test system and set up a new apache server.. After applying your fix in the main.ts everything works now, thanks for helping me out
Service worker is not registered sometimes only because you have setInterval and app is never stable from the beginning because it is triggering ngZOne change detection. To avoid this run all intervals outside angular.
@BhaumikThakkar please see the docs here: angular.io/guide/…
|
13

I found the following info in the angular docs: https://angular.io/guide/service-worker-devops#debugging-the-angular-service-worker

Summary: There's a useful endpoint on angular sites that shows the service worker state:

http://site-url/ngsw/state

Append /ngsw/state to your sites address. If there is something wrong with the service it should show there.

Comments

8

After every other solution on the stack didn't work, I've started debugging angular's ngsw-worker.js script. I've traced the updating logic and ended up in the "PrefetchAssetGroup" method. I've inserted logging every time method got called and then I realized that it is constantly trying to cache my favicon.ico. I've checked the location of the favicon.ico in my ngsw.json and realized that favicon is not located there. Once I've placed the icon on that location everything started working fine.

Comments

0

Making changes to your application you need to this method activateUpdate(

import { SwUpdate } from '@angular/service-worker';

export class AppComponent implements OnInit {
  constructor(private swUpdate: SwUpdate) { }
  ngOninit() {
    this.swUpdate.available.subscribe(event => {
      console.log('A newer version is now available. Refresh the page now to update the cache');
    });
    this.swUpdate.checkForUpdate()
  }
}

Check this Link:https://angular.io/guide/service-worker-getting-started

7 Comments

I did as you said (kept an empty tab open and restarted the http-server after making changes) but the service worker didn't update to the new version, I had to completely close the browser and start it again to get latest version. Is there maybe something wrong with my ngsw-config.json?
Still doesn't work, the SwUpdate.available.subscribe never gets triggered but I don't know why. I put a console.log in it but I can never see it. If I build my app and place it on my server the service worker doesn't react to it at all
sorry can you add this._swUpdate.checkForUpdate() inside your if condtion
I'm using an interval which checks for updates every 10sec but it doesn't react to the new version (I updated my code sample)
I'm not sure if I'm allowed to share the whole code because it's an internal project. Which files could I provide to help?
|
0

I've been using the solution in the accepted answer from Michael Doye for some time, but it stopped working when I upgraded to Angular 14.

The solution below is derived from comment by Vytautas Pranskunas

Run interval outside Angular using NgZone

    constructor(public swUpdate: SwUpdate, private ngZone: NgZone) {

    if (swUpdate.isEnabled) {
      this.ngZone.runOutsideAngular(() =>
        interval(1000 * 10).subscribe(val => {
          swUpdate
            .checkForUpdate()
            .then(_ => console.log('SW Checking for updates'));
        })
      );
    }

(Checking every 10 seconds for test purposes - increase the interval for production)

Comments

0

I wrote this code for trigger new version Maybe helpful for you,

ngOnInit(): void {
    this.updates.versionUpdates
      .pipe(
        filter((evt): evt is VersionReadyEvent => evt.type === 'VERSION_READY'),
        switchMap(() =>
          this.snackBar
            .open('New version available', 'Update now')
            .afterDismissed()
        ),
        filter((result) => result.dismissedByAction),
        map(() => this.updates.activateUpdate().then(() => location.reload()))
      )
      .subscribe();
  }

Comments

-4

To check if newer a version of Angular is available, you can call below method from any component, or just copy the IsNewerVersionAvailable method in app.component.

export class DataService {
    
    constructor(private http: HttpClient, private swUpdate: SwUpdate) { }

    private available: boolean = false;
 
    IsNewerVersionAvailable() {
        if (this.swUpdate.isEnabled) {
            this.swUpdate.available.subscribe(() => {
                this.available = true;
            });
            console.log(this.available);
        }
        return this.available;
    }    
}

Comments