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. This article is not about what progressive web apps are and what they are not, it is about how you can build progressive web apps with Angular. You can read more about progressive web apps, here.

##Getting started To show you how to build progressive web apps with Angular, we will build a very simple website. When we are done, we will use the Lighthouse Chrome extension to test if our app is really progressive. You can also clone this project from GitHub.

Click on this link to see our progressive web app in action.

Angular PWA

##Creating a new Angular Project To create a new Angular project via Angular CLI, you need to install Angular CLI on your machine, first. If Angular CLI is not installed on your machine yet, run the command below to install it globally.

//install Angular CLI
npm install -g @angular/cli

Once Angular CLI is globally installed, you can proceed to create a new project with it. By default Angular will generate test files which are of no use in our little project. In order to prevent this, we will add the --skip-tests flag to our command.

//create a new Angular project without test files.
ng new ng-pwa --skip-tests

##Web App Manifest A web app manifest is a simple JSON file which contains configurations that gives web applications the ability to be saved on user's home screen. While also defining its appearance and behaviour when launched from the home screen. Web app manifest is a basic requirement for progressive web apps but can be used on any website. You can read more about web app manifest here.

To create a web app manifest for our app, navigate to the src/ folder and create a file named manifest.json in the root of the folder. Copy and paste 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 colour the browser UI will assume when users visit our site. The background_color property controls the colour 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 need to make duplicates of your app icons in multiple dimensions. realfavicongenerator.net can help automate the process.

Real favicon generator

When you are done, go to the index.html file and add

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

to the head section.

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"
      ],
...

##Service Workers Service workers are the backbone of progressive web apps. Written in Javascript, they help cache important assets and files, so as to keep the downosaur at bay and make our 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. You can read more about service workers here.

Service Workers, Web Fundamentals

Creating a service worker in Angular is easy but not straightforward. This is because we need to build our app with webpack before pushing it to production and our service worker must be able to track and cache the build files. Thanks to sw-precache-webpack-plugin npm package, all we need to do is to install the package and configure it. We can then run a simple command that will auto-generate the service worker in the build folder.

Run the npm install command to install the package.

//install sw-precache-webpack-plugin as a development dependency
npm install --save-dev sw-precache-webpack-plugin

Once the package is installed, go to the app root and create a file named precache-config.js . Copy and paste the code below into the file.

// pre-cache.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 objects key-value pairs.

Angular as a front-end framework for building single page applications uses client-side URL routing and can, therefore, generate arbitrary URLs that are not cached by the auto-generated service worker. In such situations we'll define an HTML entry the requested URL will be mapped to, and navigateFallback handles that. The HTML entry should be able to provide the desired resources (Our app is a SPA, and index.html is the entry point. It can handle arbitrary URLs) and 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/pattern of URL that navigateFallback will be used for.

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

sw-precache documentation

To finish the service worker setup, we will create a custom npm script/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"
  },
 ...

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

<!-- 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>

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. Read more.

Below is the CSS code

/* src/styles.css */

body{
    background-color: teal;
}

.container{
    text-align: center;
}

ul{
    list-style: none;
}

h1, h2, a {
    color: white;
}

##Hosting The service worker is the heart beat of any progressive web app. However for the service worker to work fine, our app must be served over a secure connection. Hence, we will be deploying our app to Firebase because, apps hosted on Firebase are served over a secure connection. You will be walked through the steps. However, if you want learn in details how to deploy Angular apps to Firebase, check out this article by Chris.

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.

firebase-tools page, npmjs

Once a new Firebase project has been created, go to the command terminal and navigate to your progressive web app folder. Run npm install -g firebase-tools to install firebase-tools globally. 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 to deploy. To build our Angular app and auto-generate the service worker, run npm run pwa. This runs a custom script we created earlier, and makes our app production-ready.

Angular build process

Time to introduce Firebase to our app. Run firebase login command to login to Firebase. Run firebase init command to initialize Firebase in the project. 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

Our app is ready to be deployed. Run firebase deploy to deploy the app to Firebase.

Finally, run firebase open hosting: site to see our shiny new app in action.

PWA open in chrome mobile

PWA splash screen

##Lighthouse PWA Test 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% (There is room for improvement, obviously).

PWA Light house test

##Conclusion Progressive web apps are apps of the future because they provide users with an experience similar to that of native apps. However, PWAs are lighter and much more flexible. No doubt that Angular is one of the most popular front-end frameworks out there. Hopefully the relative ease of building progressive web apps with Angular as demonstrated in this article will encourage more Angular/front-end developers to make their apps progressive. Thank you.