Products from the future
UI Engineer at ForgeRock
Google Experts are a global network of experienced product strategists, designers, developers and marketing professionals actively supporting developers, startups and companies changing the world through web and mobile applications.
Static
Dynamic
AJAX
RWD
PWA
... Progressive Web App can be seen as an evolving hybrid of regular web pages (or websites) and a mobile application...
AppShell
Service
Worker
Manifest
Push
Make the website function offline
Increase online performance by reducing network requests for certain assets
Provide a customized offline fallback experience
--mobile
<html>
<body>
<app-root-component>
<h1>Loading...</h1>
</app-root-component>
</body>
</html>
<html>
<body>
<app-root-component>
<style>...</style>
<div class="md-header">
<h3>Angular2Paris</h3>
</div>
<div class="md-progress-bar"></div>
</app-root-component>
</body>
</html>
<html>
<body>
<app-root-component>
<md-toolbar color="primary">
<span>{{title}}</span>
</md-toolbar>
<md-progress-bar mode="indeterminate"></md-progress-bar>
</app-root-component>
</body>
</html>
<html>
<style>md-toolbar { display: flex; box-sizing: border-box;...</style>
<body>
<app-root-component _nghost-cb4a-1="">
<md-toolbar _ngcontent-cb4a-1="" color="primary" class="md-primary" ng-reflect-color="primary">
<div class="md-toolbar-layout">
<md-toolbar-row>
<span _ngcontent-cb4a-1="">Angular2Paris</span>
</md-toolbar-row>
</div>
</md-toolbar>
<md-progress-bar _ngcontent-cb4a-1="" aria-valuemax="100" aria-valuemin="0" mode="indeterminate" role="progressbar" _nghost-cb4a-3="" ng-reflect-mode="indeterminate" aria-valuenow="0"> <div _ngcontent-cb4a-3="" class="md-progress-bar-background"></div> <div _ngcontent-cb4a-3="" class="md-progress-bar-buffer"></div> <div _ngcontent-cb4a-3="" class="md-progress-bar-primary md-progress-bar-fill" ng-reflect-ng-style="[object Object]" style="transform:scaleX(0);"></div> <div _ngcontent-cb4a-3="" class="md-progress-bar-secondary md-progress-bar-fill"></div> </md-progress-bar>
</app-root-component>
</body>
</html>
import { UniversalModule } from 'angular2-universal';
@NgModule({
bootstrap: [AppComponent],
imports: [
AppModule,
UniversalModule.withConfig({
...
}),
})
export class AppShellModule {}
platformUniversalDynamic()
.serializeModule(ShellModule)
.then(html => console.log(html));
# Install app-shell utility from github.com/angular/mobile-toolkit
$ npm install --save @angular/app-shell
// App code
import {AppShellModule} from '@angular/app-shell';
// In NgModule used for Universal prerendering:
AppShellModule.prerender()
// At runtime:
AppShellModule.runtime()
@Component({
selector: 'app-root-component',
template: `
<!-- Only show loading indicator in the shell -->
<loading-indicator *shellRender>
</loading-indicator>
<!-- Hide a dynamic view until runtime -->
<dynamic-view *shellNoRender>
</dynamic-view>
`
})
export class AppRootComponent {}
# 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
ngsw-manifest.json:
{
"static": {
"urls": {
"/index.html": "ae543...",
"/app.js": "9ff18...",
"/logo.png": "0e33a...",
...
}
}
}
/**
* Gulp tasks that generates a basic Angular service worker manifest.
*/
gulpGenerateManifest()
gulpAddStaticFiles(files, options)
/**
* Webpack plugin that generates a basic Angular service worker manifest.
*/
AngularServiceWorkerPlugin(manifestFile, manifestKey)
{
"optimizeFor": "performance", // try to respond as fast as possible
"refreshAheadMs": 300000, // refresh data if it's older than 5 min
"strategy": "lfu", // manage the cache by evicting the URLs using LFU
"maxEntries": 1000, // track 1,000 requests
"maxAgeMs": 86400000 // consider entries stale after 1 day
}
Coming soon
import {NgServiceWorker} from '@angular/service-worker';
constructor(public sw: NgServiceWorker) {
sw.log().subscribe(message => this.log.push(message));
}
this
.sw
.checkForUpdate()
.subscribe(res => {
this.result = JSON.stringify(res);
});
this
.sw
.ping()
.subscribe(res => {
this.result = JSON.stringify(res);
});
import {bootstrapServiceWorker} from '../bootstrap';
import {StaticContentCache} from '../../plugins/static';
import {RouteRedirection} from '../../plugins/routes';
import {Push} from '../../plugins/push';
bootstrapServiceWorker({
manifestUrl: '/ngsw-manifest.json',
plugins: [
StaticContentCache(),
RouteRedirection(),
Push(),
],
});
Backend
Push Service
Service worker
ngsw-manifest.json:
{
...,
"push": {
"showNotifications": true,
"backgroundOnly": false
}
}
import {NgServiceWorker} from '@angular/service-worker';
// Inject an API for communicating with the Angular service worker
export class PushService {
constructor(worker: NgServiceWorker) {
worker.registerForPush().subscribe(endpoint => {
// Send endpoint data to the server to complete registration
};
worker.push.subscribe(payload => {
// Process payload
});
}
}