Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Progressive Angular apps
Search
Ciro Nunes
November 22, 2016
Technology
960
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Progressive Angular apps
20 minutes talk about Progressive Web Apps with Angular 2
Ciro Nunes
November 22, 2016
More Decks by Ciro Nunes
See All by Ciro Nunes
Rust Front-end with Yew
cironunes
0
100
Type safe CSS with Reason
cironunes
0
160
What I've learned building automated docs for Ansarada's design system
cironunes
0
100
Beyond ng new
cironunes
2
250
Animate your Angular apps
cironunes
0
470
Sweet Angular, good forms never felt so good
cironunes
0
110
Sweet Angular, good forms never felt so good
cironunes
0
330
Angular: Um framework. Mobile & desktop.
cironunes
1
620
Firebase & Angular
cironunes
0
310
Other Decks in Technology
See All in Technology
AIは推し活である。
kurazuuuuuu
1
860
登壇の自信を奪う3匹のオバケ / 3 Ghosts That Rob You of Your Confidence in Public Speaking
pauli
9
1k
10Xに技術的負債をもたらした「2つの境界の歪み」その構造と解消への営み
10xinc
0
2.1k
AIを活用するために決めた "やらないこと" - 価値に注目する / Not betting on AI
soudai
PRO
3
560
ソフトウェアDNAとクラウドエージェントのススメ
cloudace
0
120
その Lambda、8分で 管理者権限まで奪われます
k1nakayama
7
3.3k
EventBridge に「合流」はない ― サーバーレスのワークフローを育てるということ / No Join in EventBridge
yusukeshimizu
2
300
サーバーレスをどこまで使う? WebRTC対戦ゲームで選んだVPSとの共存設計
kaidouji85
0
200
beyond jj: config & tools ecosystem
indirect
0
5.4k
データ界隈LT祭 第1回LT登壇
taromatsui_cccmkhd
2
1.5k
30座EKS, 180次升級淬煉的EKS Upgrade Skill 的歷程
eric8230
0
190
顧客に向き合う開発組織へ。リアーキテクチャとフィーチャーチーム化で挑む組織改革
safie
0
2.1k
Featured
See All Featured
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
360
[SF Ruby Conf 2025] Rails X
palkan
3
1.4k
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
2
2.9k
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
3.1k
Design in an AI World
tapps
1
320
Writing Fast Ruby
sferik
630
63k
Speed Design
sergeychernyshev
33
2.1k
WENDY [Excerpt]
tessaabrams
14
39k
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
480
Designing for Timeless Needs
cassininazir
1
480
Paper Plane (Part 1)
katiecoart
PRO
2
11k
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
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!