DEV Community

Cover image for PWAs with Angular: Being Engaging
Michael Solati
Michael Solati

Posted on • Updated on • Originally published at michaelsolati.com

PWAs with Angular: Being Engaging

This past week we've done a lot of work taking a regular old Angular application and turning it into a Progressive Web App. We've made our application reliable with the use of Service Workers (click here); we also made out application fast by prerendering our home route and lazy loading our routes with modules (click here). We're missing one key component to building a PWA though: making our application engaging!

So what's the best way to go about making an engaging app? Two things.

  1. We want our application to offer a native experience, which for the most part our use of Angular Material does well, but it would be even better if users were able to install our PWA onto their launcher.
  2. Most apps are able to send push notifications, and with the use of our Service Worker we can do that too!

git clone --branch v2.0 https://github.com/MichaelSolati/ng-popular-movies-pwa.git
cd ng-popular-movies-pwa
npm install
Enter fullscreen mode Exit fullscreen mode

This app depends on The MovieDB’s APIs. Get an API key (check this out) and put it as the moviedb environment variable in your src/environments/environment.ts and src/environments/environment.prod.ts.

We already do a lot to offer a native app experience, all we need to do now is allow our user to install our app. What we're going to need to do is add a Web App Manifest to our application. To get started I'm going to advise you visit App Manifest Generator. This app will stub out a lot of what we need. You can fill out the fields however you want based on how you want your app to look and feel on the phone, but below is what I have selected. (I put it in JSON format for readability).

{
  "name": "Popular Movies",
  "short_name": "Movies"
  "theme_color": "#ff1744",
  "background_color": "#212121",
  "display": "standalone",
  "orientation": "portrait",
  "scope": null,
  "start_url": "/index.html",
}
Enter fullscreen mode Exit fullscreen mode

There's also an option to upload an icon so the generator can produce icons for the web app, lucky for us inside of our src/assets/ directory there is an image called app-icon.png! (You should pick that)

After you have everything selected, just click the GENERATE .ZIP button.

Web App Manifest Generator

Unzip the app-images.zip that was downloaded. We're going to move the manifest.json file into our src/ directory, and we're also going to take the icons/ directory inside of the images/ directory and move it into our src/assests/ directory.

Then we're going to modify our src/manifest.json by removing the "splash_pages": null at the end of the object, and also change all of our icon sources from having the prefix path of images/icons/ to assets/icons/. It should look like this...

{
  "name": "Popular Movies",
  "short_name": "Movies",
  "theme_color": "#ff1744",
  "background_color": "#212121",
  "display": "standalone",
  "orientation": "portrait",
  "start_url": "/index.html",
  "icons": [
    {
      "src": "assets/icons/icon-72x72.png",
      "sizes": "72x72",
      "type": "image/png"
    },
    {
      "src": "assets/icons/icon-96x96.png",
      "sizes": "96x96",
      "type": "image/png"
    },
    {
      "src": "assets/icons/icon-128x128.png",
      "sizes": "128x128",
      "type": "image/png"
    },
    {
      "src": "assets/icons/icon-144x144.png",
      "sizes": "144x144",
      "type": "image/png"
    },
    {
      "src": "assets/icons/icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png"
    },
    {
      "src": "assets/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "assets/icons/icon-384x384.png",
      "sizes": "384x384",
      "type": "image/png"
    },
    {
      "src": "assets/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

We now need to update our src/index.html to include the manifest.json, but we're also going to add a theme-color so when mobile browsers view our page, the browser's url bar will match that beautiful red we have as our app's navbar.

<head>
  <meta charset="utf-8">
  <title>Popular Movies</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">

  <meta name="theme-color" content="#ff1744">
  <link rel="manifest" href="/manifest.json">
</head>
Enter fullscreen mode Exit fullscreen mode

Finally we just need to tell our CLI to include the src/manifest.json file when it bundles our build. So we'll update our angular-cli.json to include it in the apps[0].assets array.

[
  "assets",
  "favicon.ico",
  "manifest.json"
]
Enter fullscreen mode Exit fullscreen mode

With all of that set, if we were to deploy this app and visit it, after a couple of uses, we would be prompted to add it to our home screen! (Keep in mind this prompt, as well as the push notifications we’re about to get into will only work if your app is served over HTTPS. If you’re using Firebase for your hosting, you’re all set to go!)

PWA on mobile


The last thing we want to be able to do is send push notifications to our users. While a movie app like ours may not really need it, it’s still a good and easy feature to implement for potential down the road uses, and can improve our engagement.

At the end of our src/ngsw-manifet.json file (after the dynamic sub object) we're going to add a push object looking like this.

"push": {
    "showNotifications": true
}
Enter fullscreen mode Exit fullscreen mode

Now we’re telling our Service Worker when it receives a push notification to display it, but we still need to register to receive these notifications. We can do this by using the NgServiceWorker service from the @angular/service-worker package, and we'll also need to import our ServiceWorkerModule into our app from the service worker package.

In our src/app/app.module.ts we'll add:

import { NgServiceWorker, ServiceWorkerModule } from '@angular/service-worker';
Enter fullscreen mode Exit fullscreen mode

We'll add the ServiceWorkerModule to our AppModule's imports, and we'll pass the NgServiceWorker as an argument of our AppModule's constructor.

constructor(private _moviesService: MoviesService, private _navbarService: NavbarService, private _sw: NgServiceWorker) { }
Enter fullscreen mode Exit fullscreen mode

Web push requires that our messages are sent from a backend via the Web Push Protocol. If we want to send data with our push messages we must also encrypt it following the Message Encryption for Web Push spec. In order to get the require Public Key and Private Key we'll use the web-push libraries CLI (details here).

npm install -g web-push
web-push generate-vapid-keys
Enter fullscreen mode Exit fullscreen mode

The web-push CLI will provide is with both the Public Key and Private Key we need for our application. The Private Key, well, it's a secret to everybody... So save it somewhere because you would use it on your server to send messages to users, but the Public Key we will use right away. Like with The MovieDB’s API we are going to add it to our src/environments/environment.ts and src/environments/environment.prod.ts. You should create a new key called applicationServerKey and assign your key to that.

Now we can import our environment into our src/app/app.module.ts like so:

import { environment } from '../environments/environment';
Enter fullscreen mode Exit fullscreen mode

Then in the constructor of our AppModule we'll register for our push notification.

constructor(private _moviesService: MoviesService, private _navbarService: NavbarService, private _sw: NgServiceWorker) {
  this._sw.registerForPush({
    applicationServerKey: environment.applicationServerKey
  }).subscribe((sub: any) => {
    // Use details to register on your server to send notifications to this device
    console.log(sub);
  });

  this._sw.push.subscribe((msg: any) => {
    // Handle message when in app
    console.log(msg);
  });
}
Enter fullscreen mode Exit fullscreen mode

Now we have registered our Service Worker to receive and display notifications, we can actually test it when we run our server (npm run start:pwa) like seen below.

Push notifications

When we take our application through the Lighthouse test we’re scoring up in the 80s for our PWA score (and we don’t need to be too concerned with the Performance score right now only because it’s mostly reflective of all the images we load as well as our other small factors outside of our control).

localhost Lighthouse testing


Well, we’ve done it! We’ve taken a very basic web application written in Angular and turned it into a Progressive Web App. It’s reliable, fast, and engaging! While their are a few visual tweaks we can make to improve the overall experience, our application is in a really good place right now! You can view the changes in our code from the last article to this one here, and if you want to see this app running in the wild here (I recommend trying it on your mobile phone).

Finally, I guess the most exciting and important question is how good of a PWA do we have when we deploy it? Well, why don’t you take a look below:

Deployed Lighthouse testing

Top comments (0)