$30 off During Our Annual Pro Sale. View Details »
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Progressive Angular apps
Search
Ciro Nunes
November 22, 2016
Technology
3
900
Progressive Angular apps
20 minutes talk about Progressive Web Apps with Angular 2
Ciro Nunes
November 22, 2016
Tweet
Share
More Decks by Ciro Nunes
See All by Ciro Nunes
Rust Front-end with Yew
cironunes
0
54
Type safe CSS with Reason
cironunes
0
120
What I've learned building automated docs for Ansarada's design system
cironunes
0
73
Beyond ng new
cironunes
2
210
Animate your Angular apps
cironunes
0
430
Sweet Angular, good forms never felt so good
cironunes
0
78
Sweet Angular, good forms never felt so good
cironunes
0
300
Angular: Um framework. Mobile & desktop.
cironunes
1
590
Firebase & Angular
cironunes
0
290
Other Decks in Technology
See All in Technology
バグハンター視点によるサプライチェーンの脆弱性
scgajge12
2
560
翻訳・対話・越境で強いチームワークを作ろう! / Building Strong Teamwork through Interpretation, Dialogue, and Border-Crossing
ar_tama
4
1.7k
Modern Data Stack大好きマンが語るSnowflakeの魅力
sagara
0
280
JSConf JP 2025 LINEヤフーのセッション紹介&アンケート 結果発表
lycorptech_jp
PRO
0
130
なぜフロントエンド技術を追うのか?なぜカンファレンスに参加するのか?
sakito
9
2k
Eight Engineering Unit 紹介資料
sansan33
PRO
0
5.8k
AI時代の開発フローとともに気を付けたいこと
kkamegawa
0
740
Docker, Infraestructuras seguras y Hardening
josejuansanchez
0
150
Bakuraku Engineering Team Deck
layerx
PRO
11
6.1k
AI時代におけるアジャイル開発について
polyscape_inc
0
110
AI活用によるPRレビュー改善の歩み ― 社内全体に広がる学びと実践
lycorptech_jp
PRO
1
130
21st ACRi Webinar - AMD Presentation Slide (Nao Sumikawa)
nao_sumikawa
0
220
Featured
See All Featured
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
37
2.6k
Git: the NoSQL Database
bkeepers
PRO
432
66k
The Cult of Friendly URLs
andyhume
79
6.7k
A Modern Web Designer's Workflow
chriscoyier
697
190k
The Cost Of JavaScript in 2023
addyosmani
55
9.3k
Raft: Consensus for Rubyists
vanstee
140
7.2k
Building Applications with DynamoDB
mza
96
6.8k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
970
Building a Scalable Design System with Sketch
lauravandoore
463
34k
Designing for Performance
lara
610
69k
Context Engineering - Making Every Token Count
addyosmani
9
470
Testing 201, or: Great Expectations
jmmastey
46
7.8k
Transcript
Progressive WEB APPS
@cironunesdev
None
None
None
Progressive Web App
Progressive Web App Enhancement
Progressive Web App Enhancement Like
http://alistapart.com/article/understandingprogressiveenhancement
http://alistapart.com/article/understandingprogressiveenhancement
App Like
RESPONSIVE CONNECTIVITY INDEPENDENT DISCOVERABLE ENGAGEABLE INSTALLABLE SAFE LINKABLE
App Like, but…
Service Workers Cache API Fetch API Notification API HTTPS ❤
}
How to build a PWA?
1. Web Application Manifest 2. App Shell 3. Cache (SW)
{ "name": "Ali Express", "short_name": "AliExpress", "icons": [{ "src": "images/icons/icon-128x128.png",
"sizes": "128x128", "type": "image/png" }], "start_url": "/index.html", "display": "standalone", "background_color": "#3E4EB8", "theme_color": "#2F3BA2" } manifest.webapp
Splash Screen
1. Web Application Manifest 2. App Shell 3. Cache (SW)
None
None
None
1. Architect & implement your app shell 2. Cache via
service workers
<link rel="stylesheet" href="inline.css"> <div> <header> <h1>App shell</h1> <button>Menu</button> <menu>...</menu> </header>
<main class="content"> ... </main> </div>
1. Architect & implement your app shell 2. Cache via
service workers
if ('serviceWorker' in navigator) { navigator['serviceWorker'] .register('./service-worker.js') .then(() => console.log('Service
Worker Registered')); } main.ts REGISTER
var cacheName = 'myApp'; var filesToCache = [...]; self.addEventListener('install', function(e)
{ e.waitUntil( caches.open(cacheName).then(function(cache) { return cache.addAll(filesToCache); }) ); }); service-worker.js ADD TO CACHE
self.addEventListener('activate', (e) => { e.waitUntil(self.clients.claim()); e.waitUntil( caches.keys().then((keyList) => { return
Promise.all(keyList.map((key) => { if (key !== cacheName) { return caches.delete(key); } })); }) ); }); service-worker.js CLEAR CACHE
self.addEventListener('fetch', (e) => { e.respondWith( caches.match(e.request).then((response) => { return response
|| fetch(e.request); }) ); }); service-worker.js READING FROM CACHE
1. Web Application Manifest 2. App Shell 3. Cache (SW)
self.addEventListener('fetch', function(e) { var dataUrl = 'https://sample-api.com'; if (e.request.url.indexOf(dataUrl) >
-1) { e.respondWith( caches.open(dataCacheName).then(function(cache) { return fetch(e.request).then(function(response){ cache.put(e.request.url, response.clone()); return response; }); }) ); } else { e.respondWith( caches.match(e.request).then(function(response) { return response || fetch(e.request); }) ); } }); service-worker.js
self.addEventListener('fetch', function(e) { var dataUrl = 'https://sample-api.com'; if (e.request.url.indexOf(dataUrl) >
-1) { e.respondWith( caches.open(dataCacheName).then(function(cache) { return fetch(e.request).then(function(response){ cache.put(e.request.url, response.clone()); return response; }); }) ); } else { e.respondWith( caches.match(e.request).then(function(response) { return response || fetch(e.request); }) ); } }); service-worker.js
self.addEventListener('fetch', function(e) { var dataUrl = 'https://sample-api.com'; if (e.request.url.indexOf(dataUrl) >
-1) { e.respondWith( caches.open(dataCacheName).then(function(cache) { return fetch(e.request).then(function(response){ cache.put(e.request.url, response.clone()); return response; }); }) ); } else { e.respondWith( caches.match(e.request).then(function(response) { return response || fetch(e.request); }) ); } }); service-worker.js
self.addEventListener('fetch', function(e) { var dataUrl = 'https://sample-api.com'; if (e.request.url.indexOf(dataUrl) >
-1) { e.respondWith( caches.open(dataCacheName).then(function(cache) { return fetch(e.request).then(function(response){ cache.put(e.request.url, response.clone()); return response; }); }) ); } else { e.respondWith( caches.match(e.request).then(function(response) { return response || fetch(e.request); }) ); } }); service-worker.js
None
caches.match(e.request.clone()).then((response) => { return response || fetch(e.request.clone()).then((theResponse) => { return
caches.open(dataCacheName).then((cache) => { cache.put(e.request.url, theResponse.clone()); return theResponse.clone(); }); }); }); service-worker.js
Profit!
Profit! What about a protip?
Lighthouse
None
None
None
Bootstrap App shell Service worker Notifications
None
None
Bootstrap App shell Service worker Notifications
None
~ $ npm i --save @angular/app-shell
~ $ npm i --save @angular/app-shell // App code import
{ AppShellModule } from '@angular/app-shell'; @NgModule({ bootstrap: [AppComponent], imports: [ BrowserModule, AppShellModule.runtime(), AppModule })
@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 {}
Can we be faster?
None
@NgModule({ bootstrap: [AppComponent], imports: [ AppShellModule.prerender(), AppModule, UniversalModule.withConfig({...}) })
Bootstrap App shell Service worker Notifications
~ $ npm i --save @angular/service-worker
~ $ npm i --save @angular/service-worker // worker-basic.min.js is copied
from // node_modules/@angular/service-worker/bundles if (navigator.serviceWorker) { navigator.serviceWorker.register('/worker-basic.min.js'); }
{ "routing": { "routes": { "/": { "prefix": false }
}, "index": "/index.html" } } ngsw-manifest.json
Bootstrap App shell Service worker Notifications
self.addEventListener('push', function(e) { e.waitUntil( fetch('http://localhost:8090/pushdata').then(function(response) { return response.json(); }).then(function(data) {
return self.registration.showNotification(title, { body: body, icon: icon, tag: tag }); }, function(err) { err(err); }) ); }); worker-push.js
self.addEventListener('push', function(e) { e.waitUntil( fetch('http://localhost:8090/pushdata').then(function(response) { return response.json(); }).then(function(data) {
return self.registration.showNotification(title, { body: body, icon: icon, tag: tag }); }, function(err) { err(err); }) ); }); worker-push.js
None
pwa.rocks Go get inspired and create something great!
@cironunesdev Dzięki!