Photo by Tinh Khuong on Unsplash

Getting Started with Angular PWAs -- Part 2

Posted on April 10, 2019

Written by Preston Lamb

angular mobile progressive web app

In part one of this Progressive Web App series, we went over how to create a PWA with Angular and how to begin setting up the service worker to cache files and API requests. If you haven't read it, and have no experience with Angular PWAs, you may want to read that before continuing with part two.

Notifying of Updates

Okay, so let's first start with notifying the users of your application that there is an update for them. We saw in the last article that the updates are done in the background, and then after a refresh the new application files are used. But how would the users know that there's an update and they should refresh the application? Well, once again, Angular has made this quite easy to do.

Let's start by creating a new component to handle this:

ng g c update-notification

In that application, inject the SwUpdate service from @angular/service-worker:

constructor(private _update: SwUpdate) {}

And the ngOnInit function:

ngOnInit() {
  if (this._update.isEnabled) {
    this._update.available.subscribe(() => {
      this.updateAvailable = true;
    })
  }
}

This code inside the ngOnInit function checks the SwUpdate service that we injected to see if updates are enabled, and if so, it subscribes to that observable. When a new value is emitted, we set a local variable, this.updateAvailable, to true. We can then show something in the UI that will allow the user to select if they would like to update the application or not. That UI can look something like this:

<p *ngIf="updateAvailable" class="update-notification">
	There is a new version of this app available. <span (click)="doUpdate()">Update now?</span>
</p>

The doUpdate() function is simple:

doUpdate() {
  this.updateAvailable = false;
  window.location.reload();
}

That's all it takes to show the user the option of updating or not. You could technically update without giving them an option, but maybe their network is slow so the downloading of new files takes several seconds, and in the meantime they've filled out a form of some sort. Automatically updating would cause them to lose that data they'd entered. This way they can choose when to update.

Install Prompt

Another technique that's beneficial for users and interacting with progressive web apps is an install prompt. Chrome, and most browsers I believe, will show a prompt by default asking if the user would like to install the application on their device. But they can decline that prompt, and if they do it may be a while until that prompt shows up again. In one place I read that it could be up to a couple months before Chrome will ask again. We may not want to wait that long before asking, so there is a way to prompt the user. The following JavaScript code will take care of this for you:

var promptEvent;
var btn = document.querySelector('#pwa-install-button');

window.addEventListener('beforeinstallprompt', event => {
  promptEvent = event;
  btn.style.display = 'block';
  console.log('beforeinstallprompt caught');
});

btn.addEventListener('click', () => {
  if (promptEvent) {
    promptEvent.prompt();
    promptEvent.userChoice.then(result => {
      if (result.outcome === 'accepted') {
        console.log('user accepted add to homescreen');
      } else {
        console.log('user dismissed the add to homescreen');
      }
      promptEvent = undefined;
    });
  }
});

Let's cover what happens here. We first listen for the beforeinstallprompt event on the window, which will only fire if the app isn't already installed and the app meets the PWA requirements. When it happens, we save the prompt in a local variable and show a button. Then we add an event listener to the button and wait for it to be clicked. When it is, we call the prompt() method on the promptEvent. We wait for the result and then set the promptEvent to undefined so the button hides again. The event itself handles the showing of a prompt and the actual installation of the app.

You could potentially put the above code somewhere in your Angular app, but from what I read and tested, that beforeinstallprompt fires pretty soon after the window loads, and my Angular app was not catching the event. So instead I put that in a normal JS file in the assets folder, and load it manually into the index.html file. I also put the button directly into the HTML file, and then styled it in the app's global CSS file.

One note on the above code: I have not gotten it working in Firefox for Android. There's an add to homescreen button in Firefox for Android, but the install button never shows, so it appears to me that the beforeinstallprompt event never fires. The event does fire in Chrome for Android. So be aware of that.

Conclusion

These are two techniques that I've learned so far to make working with and the user's experience with PWAs better. I'm hoping (and sure I will) to learn more techniques over the next couple months as I work on a PWA project for work. As I learn more about it, I'll continue with this series and add more information and more techniques for everyone.

Click here to subscribe to the newsletter and be the notified when a new blog post is available!