Progressive Web Apps

using Angular Mobile Toolkit

Maxim Salnikov

Angular GDE

Automatic

Progressive Web Apps

using Angular Service Worker

Maxim Salnikov

Angular GDE

Automatic

Maxim Salnikov

  • Google Developer Expert in Angular

  • Angular Oslo / PWA Oslo meetups organizer

  • Mobile Era conference organizer

Products from the future

UI Engineer at ForgeRock

MobileEra.Rocks

  • All kinds of native mobile development

  • Progressive Web Apps

  • Mobile Web: React Native, NativeScript, Ionic

20% off with code

October 5-6, Oslo

Milestones of the web

AJAX

Static

Dynamic

RWD

PWA

Progressive Web App

... Progressive Web App can be seen as an evolving hybrid of regular web pages (or websites) and a mobile application

... a new software development methodology

10 characteristics

  • Progressive
  • Discoverable
  • Linkable
  • App-like
  • Responsive
  • Connectivity-independent
  • Re-engageable
  • Installable
  • Fresh
  • Safe

 App Shell architecture

 Service Worker API

Push API and Notifications API

Involved APIs

  • Service Worker API

  • Cache API

  • Fetch API

  • Notifications API

  • Push API

  • IndexedDB API

  • Promises

2   packages

@angular/service-worker

v1.0.0-beta.14 - Experimental service worker by the Angular Mobile team

@angular/app-shell

v0.1.0 - App Shell runtime library for Angular Progressive Web Apps

Service Worker

Angular Service Worker

NGSW

4   main parts

@angular/service-worker/bundles

@angular/service-worker/build

@angular/service-worker/companion

@angular/service-worker/worker

@angular/service-worker/plugins

NGSW installation

# First, install the Angular Service Worker
$ npm install --save @angular/service-worker
# Enable the SW registration + app shell in Angular CLI
$ ng set apps.0.serviceWorker=true
"apps": [
    {
        "serviceWorker": true
    }
]

.angular-cli.json

App build

# NGSW works only in --prod mode
$ ng build --prod
<script src="sw-register.{HASH}.bundle.js"></script>
  • sw-register.{HASH}.bundle.js

  • worker-basic.min.js

  • ngsw-manifest.json

index.html

Static content caching

{
  "static": {
    "urls": {
      "/index.html": "ae543...",
      "/main.bundle.js": "9ff18...",
      "/styles.bundle.css": "d6f44...",
      "/assets/images/logo.png": "0e33a...",
      ...
    }
  }
}

ngsw-manifest.json

Angular PWA

Static Content

Cache

Route redirection

{
  "routing": {
    "index": "/index.html",
    "routes": {
      "/": {
        "prefix": false
      },
      "/some/routes-group-prefix": {
        "prefix": true
      }
    }
  }
}

ngsw-manifest.json

Extending NGSW manifest

{
    "static" : {
        ...autogenerated
    }
}
{
    "routing" : {...}
    ...
}

ngsw-manifest.json

External content caching

{
  "external": {
    "urls": [
      {
        "url": "https://fonts.gstatic.com/Roboto.ttf"
      }
    ]
  }
}

ngsw-manifest.json

Angular PWA

Static Content

Cache

External Content Cache

Dynamic content caching

{
  "dynamic": {
    "groups": [
      {
        "name": "api",
        "urls": {...},
        "cache": {...}
       }
    ]
  }
}

ngsw-manifest.json

Dynamic cache settings

cache: {
  "optimizeFor": "freshness",   // or 'performance'
  "networkTimeoutMs": 200,
  "maxAgeMs": 1000,
  "maxEntries": 2,
  "strategy": "fifo"            // or 'lru', 'lfu'
}
urls: {
    "/api/breakingnews": {
        "match": "prefix"
    }
},

ngsw-manifest.json

Angular PWA

Static Content

Cache

External Content Cache

Dynamic Content

Cache

Angular PWA Tools

ngu-sw-manifest

$ npm install --save ng-pwa-tools
# Create settings for Static Content Cache plugin
$ ngu-sw-manifest --out dist/ngsw-manifest.json
# Add settings for Routing plugin
$ ngu-sw-manifest --module src/app/app.module.ts \
                  --out dist/ngsw-manifest.json

ngu-app-shell

# Server-render Application Shell
$ ngu-app-shell --module src/app/app.module.ts \
                --out dist/index.html
# Use a different module for loading time
$ ngu-app-shell --module src/app/app.module.ts \
                --url /loading \
                --insert-module src/app/loading.module.ts \
                --out dist/index.html

Companion

import {NgServiceWorker} from '@angular/service-worker';
constructor(public sw: NgServiceWorker) {}

app.component.ts

import {ServiceWorkerModule} from '@angular/service-worker'
imports: [
    ...
    ServiceWorkerModule
]

app.module.ts

Gentle app update flow

sw.updates.subscribe(event => {
  if (event.type === 'pending') {
    // Ask user if they want to update?
    if (agreeToUpdate) {
        sw.activateUpdate(event.version);
    }
  } else {
    // event.type === 'activation'
    // NGSW is now serving a new version
    location.reload();
  }
});

app.component.ts

Push notifications

Backend

Notification

Push Service

NGSW

App

Backend

Push Service

App

Subscription

Sending a notification

Push settings

{
  "push": {
    "showNotifications": true
  }
}

ngsw-manifest.json

Push subscription

constructor(public sw: NgServiceWorker) {}
sw
    .registerForPush({
        applicationServerkey: 'H7nef93Mds....'
    })
    .subscribe(subscriptionObject => {
        // Send subscription data to the server
    });
import {NgServiceWorker} from '@angular/service-worker';

push.component.ts

Push subscription

push.component.ts

sw.push.subscribe(notificationPayload => {
    // Process notification data
});

Angular PWA

Static Content

Cache

External Content Cache

Push

Dynamic Content

Cache

Basic Service Worker

import {bootstrapServiceWorker} from '@angular/service-worker/worker';
import {StaticContentCache} from '@angular/service-worker/plugins/static';
...

bootstrapServiceWorker({
  manifestUrl: '/ngsw-manifest.json',
  plugins: [
    StaticContentCache(),
    ...
  ]
});

worker/builds/basic.ts

Extending functionality

import {bootstrapServiceWorker} from '@angular/service-worker/worker';
import {StaticContentCache} from '@angular/service-worker/plugins/static';
...
import {MyCustomPlugin} from './plugins/my-custom-plugin';

bootstrapServiceWorker({
  manifestUrl: '/ngsw-manifest.json',
  plugins: [
    StaticContentCache(),
    ...
    MyCustomPlugin()
  ]
});

worker-custom.js

Automatic PWA

{
  "static": {...},
  "routing": {...},
  "external": {...},
  "dynamic": {...},
  "push": {...}
}

ngsw-manifest.json

Angular PWA

Static Content

Cache

External Content Cache

Push

Dynamic Content

Cache

Automatically

Roadmap

  • Move Angular Service Worker to Angular Core

  • More tight integration with Angular CLI

  • Simplify server-side rendering flow

  • Bake Angular PWA Tools into CLI

  • Make Progressive Angular Apps very common!

 

Documentation!

Mulţumesc mult!

@webmaxru

Maxim Salnikov

Questions?

Automatic Progressive Web Apps using Angular Service Worker

By Maxim Salnikov

Automatic Progressive Web Apps using Angular Service Worker

Progressive Web Apps are the next big thing for the web. They combine the advantages of two platforms: searchability and shareability of the web with capabilities and performance of native mobile. As a result, web developers can use their favorite tools to build installable, re-engageable, connectivity independent apps, that can bring native-like performance and user experience.

  • 2,414