Tutorial

How To Build Progressive Web Apps with Angular

DevelopmentJavaScript

Introduction

Progressive web apps are web applications built with technologies that make them behave like native apps. A benefit of progressive web apps is the ability to work smoothly when network coverage is unreliable. Also, unlike native apps, no installation is required, but they are faster than typical web apps.

In this article, you’ll build a progressive web app with Angular and deploy it with Firebase.

Angular PWA

The code for this tutorial is available on GitHub.

Note: Currently, there is an @angular/pwa package that helps with this process. This tutorial will cover an earlier alternative approach.

Prerequisites

To complete this tutorial, you will need:

This tutorial was verified with Node v14.5.0 and npm v6.14.5.

Step 1 — Creating a New Angular Project

You can create a new project with Angular CLI. By default, Angular will generate test files that are not of use in this tutorial’s project. To prevent this generation, you’ll add the --skip-tests flag to the following command to create a new project:

  • npx @angular/cli@10.0.0 new ng-pwa --skip-tests

You will be prompted by some configuration options:

Output
? Would you like to add Angular routing? No ? Which stylesheet format would you like to use? CSS

This will create a new project directory named ng-pwa.

Navigate to the new directory:

  • cd ng-pwa

Now that you have a starter project, you can move on to creating a web app manifest.

Step 2 — Creating a Web App Manifest

A web app manifest is a JSON file that contains configuration that gives a web application the ability to be saved on the user’s home screen. It also defines its appearance and behavior when launched from the home screen. Web app manifest is a basic requirement for progressive web apps but can be used on any website.

To create a web app manifest for your app, you will need a new file named manifest.json in the root of the src folder:

  • nano src/manifest.json

Add the content below into the file:

src/manifest.json
{
   "name": "Angular Progressive Web App",
   "short_name": "Ng-PWA",
   "start_url": "./",
   "theme_color": "#008080",
   "background_color": "#008B8B",
   "display": "standalone",
   "description": "A PWA that is built with Angular",
   "icons": [
       {
           "src": "/assets/images/icons/icon-16x16.png",
           "sizes": "16x16",
           "type": "image/png"
       },
       {
            "src": "/assets/images/icons/icon-32x32.png",
            "sizes": "32x32",
            "type": "image/png"
        },
        {
            "src": "/assets/images/icons/icon-150x150.png",
            "sizes": "150x150",
            "type": "image/png"
        },
        {
            "src": "/assets/images/icons/icon-180x180.png",
            "sizes": "180x180",
            "type": "image/png"
        },
        {
            "src": "/assets/images/icons/icon-192x192.png",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "/assets/images/icons/icon-512x512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
   ]
}

In our web app manifest, we defined the name that will be attached to our app icon on users’ home screen and a short_name that will replace it, in case it is too long. We also specified the landing page of the app, when launched from the home screen with start_url. The theme_color specifies the color the browser UI (user interface) will assume when users visit our site. The background_color property controls the color of the background on which our app icon will be displayed when users launch our app from their home screen. With display, you specify if the browser UI should be hidden or not when users visit your site.

We expect users to visit our site with different types of devices with different screen sizes, so there is a need to make duplicates of your app icons in multiple dimensions.

When you are done adding image assets, go to the index.html file and add the following to the <head> section:

src/index.html
...
<head>
  ...
  <link rel="manifest" href="/manifest.json">
  <meta name="theme-color" content="#008080">
</head>

The web app manifest will not be added to the build folder unless we instruct Angular to do so. We will do that by adding the manifest.json file to the assets array in apps section of .angular-cli.json file:

.angular-cli.json
...
"apps": [
    {
     ...
      "assets": [
       ...
        "manifest.json"
      ],
...

Note: If your application is built with Angular 6 or later, you will need to edit angular.json instead of .angular-cli.json:

angular.json
...
"projects": {
  "ng-pwa": {
    ...
    "architect": {
      "build": {
        ...
        "assets": [
          "src/manifest.json"
        ],
...

After creating a manifest.json, modifying index.html, and editing angular-cli.json (or angular.json), you are ready for the next step.

Step 3 — Adding Service Workers

Service workers are the foundation of progressive web apps. Written in JavaScript, they help cache important assets and files, which helps keep an app functional when the network coverage is unavailable or unreliable. Service workers can also intercept requests and manage responses from the server amid other things.

We need to build our app with webpack before pushing it to production. Our service worker must be able to track and cache the build files.

With the sw-precache-webpack-plugin npm package, we install the package and configure it.

Note: Currently, the developer for sw-precache-webpack-plugin recommends using GenerateSW from workbox-webpack-plugin.

We can then run a command that will auto-generate the service worker in the build folder.

Run the npm install command from the ng-pwa directory to install the package:

  • npm install --save-dev sw-precache-webpack-plugin@1.0.0

Once the package is installed, create a file named precache-config.js:

  • nano precache-config.js

Add the following into the file:

precache-config.js
var SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');

module.exports = {
    navigateFallback: '/index.html',
    navigateFallbackWhitelist: [],
    stripePrefix: 'dist',
    root: 'dist/',
    plugins:[
        new SWPrecacheWebpackPlugin({
            cacheId: 'ng-pwa',
            filename: 'service-worker.js',
            staticFileGlobs: [
                'dist/index.html',
                'dist/**.js',
                'dist/**.css'
            ],

        })
    ],
    stripePrefix: 'dist/assets',
    mergeStaticsConfig: true
};

The precache-config.js file configures the sw-precache-webpack-plugin using literal object key-value pairs.

Angular as a front-end framework for building single-page applications uses client-side URL routing. This means that it can generate arbitrary URLs that are not cached by the auto-generated service worker. In such situations, we’ll define an HTML entry that the requested URL will be mapped to, and navigateFallback handles that. The HTML entry should be able to provide the desired resources. Because our app is a SPA (single-page application), and index.html is the entry point — it can handle arbitrary URLs — therefore it must be among the files selected to be cached in the staticFileGlobs array. navigateFallbackWhitelist can be empty or contains a regex that defines the type or pattern of URL that navigateFallback will be used for.

To get a deeper understanding of how to configure sw-precache-webpack-plugin, read its documentation.

To finish the service worker setup, we will create a custom npm script or command that will be used to build our app and auto-generate the service worker file in the build folder. Go to the package.json file and add the following to scripts:

package.json
 ...
 "scripts": {
    ...
    "pwa": "ng build --prod && sw-precache --root=dist --config=precache-config.js"
  },
 ...

After installing the sw-precache-webpack-plugin, creating precache-config.js, and editing package.json, you are ready for the next step.

Step 4 — Creating the View

We only have a single view, since our primary focus is on the process of building progressive web apps with Angular.

Edit app.component.html and replace the content with the following code:

src/app/app.component.html
<div class="container">
  <h1>
    A Progressive Web App Built with Angular.
  </h1>
  <img width="300" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOS4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCAyNTAgMjUwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAyNTAgMjUwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KCS5zdDB7ZmlsbDojREQwMDMxO30NCgkuc3Qxe2ZpbGw6I0MzMDAyRjt9DQoJLnN0MntmaWxsOiNGRkZGRkY7fQ0KPC9zdHlsZT4NCjxnPg0KCTxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iMTI1LDMwIDEyNSwzMCAxMjUsMzAgMzEuOSw2My4yIDQ2LjEsMTg2LjMgMTI1LDIzMCAxMjUsMjMwIDEyNSwyMzAgMjAzLjksMTg2LjMgMjE4LjEsNjMuMiAJIi8+DQoJPHBvbHlnb24gY2xhc3M9InN0MSIgcG9pbnRzPSIxMjUsMzAgMTI1LDUyLjIgMTI1LDUyLjEgMTI1LDE1My40IDEyNSwxNTMuNCAxMjUsMjMwIDEyNSwyMzAgMjAzLjksMTg2LjMgMjE4LjEsNjMuMiAxMjUsMzAgCSIvPg0KCTxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik0xMjUsNTIuMUw2Ni44LDE4Mi42aDBoMjEuN2gwbDExLjctMjkuMmg0OS40bDExLjcsMjkuMmgwaDIxLjdoMEwxMjUsNTIuMUwxMjUsNTIuMUwxMjUsNTIuMUwxMjUsNTIuMQ0KCQlMMTI1LDUyLjF6IE0xNDIsMTM1LjRIMTA4bDE3LTQwLjlMMTQyLDEzNS40eiIvPg0KPC9nPg0KPC9zdmc+DQo=" alt="Angular logo">
  <h2>Get Started With Progressive Web Apps:</h2>
  <ul>
    <li>
      <h4><a target="_blank" rel="noopener" href="https://developers.google.com/web/fundamentals/primers/service-workers/">Service Workers</a></h4>
    </li>
    <li>
      <h4><a target="_blank" rel="noopener" href="https://developers.google.com/web/fundamentals/web-app-manifest/">Web App Manifest</a></h4>
    </li>
    <li>
      <h4><a target="_blank" rel="noopener" href="https://developers.google.com/web/fundamentals/codelabs/your-first-pwapp/">Code Lab (PWA)</a></h4>
    </li>
  </ul>
</div>

This code produces a web page with an image, some text, and three links.

The rel="noopener attribute is essential in progressive web apps if the anchor element’s target attribute is set to _blank for security and performance reasons.

Edit styles.css and replace the content with the following code:

src/styles.css
body {
    background-color: teal;
}

.container {
    text-align: center;
}

ul {
    list-style: none;
}

h1, h2, a {
    color: white;
}

This code creates a teal background color, centers the text, and gives the text a white color.

Now, you have completed the view and can move on to deploying the app.

Step 5 — Deploying Your App

The service worker is the heartbeat of any progressive web app. However, for the service worker to work properly, our app must be served over a secure connection. Hence, we will be deploying our app to Firebase, which hosts over a secure connection. In this step, you will deploy your app to Firebase.

Create a new Firebase project

To get started, visit firebase.google.com. If you don’t have an account already, create one to have access to the console. From the console, create a new Firebase project.

Once logged in, click on the Go to console link in the top right corner. On the console page, select Create a project. You will be prompted for a name for your project. You will also be prompted to enable Google Analytics. This tutorial will not require Google Analytics.

Click Create project and wait for the process to complete.

After the project has been created, you can click Continue to arrive at the Project Dashboard. For the purposes of this tutorial, we will be interested in the Develop section and the Hosting page.

Now you can go back to your command terminal. Run the npm install command to install the firebase-tools package globally:

  • npm install -g firebase-tools@8.4.3

The firebase-tools package will allow us to test run and deploy apps to Firebase from the command terminal.

When the installation is complete, we need to build our app in preparation for deployment.

To build the Angular app and auto-generate the service worker, run the following from the ng-pwa directory:

  • npm run pwa

This runs a custom script we created earlier and makes our app production-ready.

Angular build process

Now it is time to introduce Firebase to the app. Run this command to log in to Firebase:

  • firebase login

At this point, you will be prompted for credentials. Enter your account information into the terminal.

Once authenticated, run the following command to initialize Firebase in the project:

  • firebase init

Then answer the questions as follows:

Are you ready to proceed? (Y/n) = Y
Which Firebase CLI features do you want to setup for this folder? = Hosting
Select a default Firebase project for this directory = Your-Firebase-Project-Name
What do you want to use as your public directory? = dist
Configure as a single-page app (rewrite all urls to /index.html)? (y/N) = Y
File dist/index.html already exists. Overwrite? (y/N) = N

Firebase command line showing the mentioned choices

Our app is ready to be deployed. Run the following command to deploy the app to Firebase:

  • firebase deploy

Finally, run this command to see the app:

  • firebase open hosting:site

PWA open in Chrome mobile

PWA splash screen

Troubleshooting

If you see a “Firebase Hosting Setup Complete” message instead of your app, you may have overwritten your index.html. Rebuild with npm run pwa, firebase init, and ensure you select “No” for overwriting index.html.

Depending on your configuration, your app may exist under "ng-pwa" (the name of the Angular project). During firebase init, you can set the public directory to dist/nw-pwa instead of dist to avoid this.

Your progressive web application has now been built and deployed. Now, we can use a tool to test it.

Step 6 — Testing with Lighthouse

Lighthouse is a Chrome extension made by Google. It can be used to test how compliant a progressive web app is to the progressive web app standard, in addition to other tests.

The highest score is 100%, and the PWA score for this app is 91%.

PWA Lighthouse test

To test your PWA, open the hosted app in your Google Chrome Web Browser. Click on the Extensions button and select Lighthouse. A window will display and prompt you to click on a Generate report button. After clicking the button, you will temporarily see a screen with a Waiting for Lighthouse results message. When the test completes, you will be presented with a screen of the test results.

Conclusion

In this article, you’ve built a progressive web app with Angular and deployed through Firebase. Progressive web apps provide users with an experience similar to native apps. However, PWAs are lighter and much more flexible.

Creative Commons License