Building an Angular PWA

Blog post image for Building an Angular PWA
Michael Cassar's profile pictureMichael Cassar

Hello again humanoids! Ever wondered how some web applications let you install them on your device? That's one of the cool features of PWAs!

What is a PWA?

A Progressive Web App or PWA utilizes web platform technologies to mimic the user experience of platform-specific applications and it functions seamlessly across various platforms and devices. Similar to platform-specific applications, a PWA can be installed on devices, operate offline, and in the background, and easily integrate with the device, and other installed applications.

In the realm of Progressive Web Apps, developers benefit from a comprehensive feature set, with one key element being the inclusion of a service worker. These service workers act as a bridge between the user's browser and the server, granting developers complete control over caching. Additionally, service workers empower PWAs to seamlessly integrate with push notifications and background sync APIs, enhancing the overall user experience.

Navigating the wild journey of crafting a service worker might sound like embarking on a quest, but fear not! In the marvelous universe of Angular, a service worker comes pre-packed with Angular PWA, saving you from the epic struggle of developing one from scratch. Now, it is time to channel your inner configurator!

Enabling Angular PWA

To activate Angular PWA support, execute the provided command within an existing Angular application's directory using your terminal. This command facilitates the installation of essential PWA components and adds some new configuration files into your project folder. We'll delve into each file and their purpose, providing insights into their roles and how they contribute to configuring your PWA.

ng add @angular/pwa

Web Manifest

The manifest.webmanifest file serves the purpose of providing a browser with the necessary information to be able to install a PWA. This file utilizes a JSON format. This is an example of the manifest.webmanifest structure:

{
  "name": "Weather App",
  "short_name": "WA",
  "theme_color": "#000000",
  "background_color": "#fafafa",
  "display": "standalone",
  "scope": "./",
  "start_url": "./",
  "icons": [
    {
      "src": "assets/icons/icon-72x72.png",
      "sizes": "72x72",
      "type": "image/png",
      "purpose": "maskable any"
    },
    {
      "src": "assets/icons/icon-96x96.png",
      "sizes": "96x96",
      "type": "image/png",
      "purpose": "maskable any"
    },
    {
      "src": "assets/icons/icon-128x128.png",
      "sizes": "128x128",
      "type": "image/png",
      "purpose": "maskable any"
    },
    {
      "src": "assets/icons/icon-144x144.png",
      "sizes": "144x144",
      "type": "image/png",
      "purpose": "maskable any"
    },
    {
      "src": "assets/icons/icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png",
      "purpose": "maskable any"
    },
    {
      "src": "assets/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "maskable any"
    },
    {
      "src": "assets/icons/icon-384x384.png",
      "sizes": "384x384",
      "type": "image/png",
      "purpose": "maskable any"
    },
    {
      "src": "assets/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "maskable any"
    }
  ]
}

Let's examine the members of this file:

  • name - The name member is a string representing your web application's name. This name is displayed in various contexts, like among a list of applications or as a label for an icon. This member is directionally capable, meaning it can be shown from left-to-right or right-to-left based on the values of the dir and lang manifest members.
  • short_name - The short_name member operates similarly to the name member but comes into play when there's limited space to display the complete name. Similar to the name member, this member also supports languages and is directionally capable.
  • theme_color - The theme_color member sets the default theme color for the application. In certain situations, it can impact how the OS displays your application.
  • background_color - The background_color member sets a temporary background color for the application to show before its stylesheet loads. Ideally the background_color matches your body background-color css class to avoid momentary flickering and ensure consistency.
  • display - The display member indicates the preferred display mode for the application. This mode determines how much of the browser UI is visible to the user after the application has been installed.
  • scope - The scope member tells the application what its navigation scope is. If a user navigates to a URL that's not within this scope, the application will open that URL in a different browser window.
  • start_url - The start_url member indicates the preferred URL used to launch your application when it is installed, this would usually be set to the root URL of your site. Absolute and relative URLs are both supported with this configuration option.
  • icons - The icons member is essentially an array of objects that represent image files. These images can function as application icons for different purposes, like displaying the web application among other applications or integrating it with an OS's task switcher and system preferences.

The Web Manifest file supports many more configuration options, some of which are experimental, here's a link to find fully comprehensive documentation on web manifest configuration.

Angular Service Worker Configuration

The ngsw-config.json configuration file outlines what files and data URLs the Angular service worker should cache and how to retrieve and update them. The Angular CLI processes this configuration file during an ng build.

{
  "$schema": "./node_modules/@angular/service-worker/config/schema.json",
  "index": "/index.html",
  "navigationUrls": [],
  "assetGroups": [
    {
      "name": "app",
      "installMode": "prefetch",
      "resources": {
        "files": [
          "/favicon.ico",
          "/index.html",
          "/manifest.webmanifest",
          "/*.css",
          "/*.js"
        ]
      }
    },
    {
      "name": "assets",
      "installMode": "lazy",
      "updateMode": "prefetch",
      "resources": {
        "files": [
          "/assets/**",
          "/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)"
        ]
      }
    }
  ]
}

Let's discuss a couple of these properties:

  • navigationUrls - This property allows you to provide a list of URLs which will be redirected to the index.html file.
  • assetGroups - Assets are essential parts of the application that get updated alongside the application. They include resources from the page's origin and third-party ones from CDNs and external URLs.

The ngsw-config.json configuration file supports many more configuration options. The Angular team provides extensive documentation on service worker configuration if you require more information.

Testing

Angular PWA is disabled by default in development, yet it's crucial to conduct thorough testing before enabling it on a production environment. To achieve this, simply make the necessary changes to your app.module.ts file, and set the enabled property to true:

ServiceWorkerModule.register('ngsw-worker.js', {
  enabled: !isDevMode(),
  registrationStrategy: 'registerWhenStable:30000'
})

Additionally, to verify the proper registration and operation of your Angular PWA, there's a handy trick. You can use the following URL to check if your Angular PWA is in a nominal state or find hints to possible issues: {protocol}://{domain}/ngsw/state

Managing Updates

Another noteworthy feature of PWAs is their capability to notify users about application updates. To manage this effectively in Angular, it's necessary to create a service.

Essentially, the structure of your service should be as follows; The initial step involves injecting the SwUpdate service and checking the availability of the service worker on the client. Subsequently, a timer should be created to periodically check for updates once the application is stable. Finally, subscribe to the versionUpdates event and listen out for the "VERSION_READY" state. When this event fires, it is safe to prompt the user about the availability of a new version of your application, you can then simply reload your application once the user decides to update.

Production Pitfalls

In Angular applications, building generates file hashes for all of your bundle's files, which are then stored in the ngsw-config.json file. These file hashes are used by the service worker to decide if your application has changed to trigger update events. Since these hashes are generated at build time, changing files after deployment can adversely impact your PWA.

For instance, Cloudflare Analytics in automatic setup mode injects beacon.js into your index.html, causing a hash mismatch with ngsw-config.json. This results in your PWA state to never be nominal, causing a ton of issues. Please be wary of this, it will save you hours of debugging!

Conclusion

In conclusion, delving into Angular PWAs opens doors to crafting modern, user-friendly web applications. The Progressive Web App architecture seamlessly integrates with various platforms and devices, allowing installation, offline operation, and integration.

Key to this is the service worker, offering precise caching control and enhancing user interactions. Enabling Angular PWA involves a simple command and understanding configurations in the project folder, notably the manifest.webmanifest and ngsw-config.json files for appearance and behavior customization.

Beware of production pitfalls, particularly the impact of file changes on your Progressive Web App. Changes to your files after build time will cause hash mismatches, disrupting the state of your PWA.

Embracing Angular PWA means harnessing cutting-edge web technologies for a seamless user experience. Equip yourself with this knowledge, embark on this journey, and elevate your web development endeavors!