Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
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
50
Type safe CSS with Reason
cironunes
0
110
What I've learned building automated docs for Ansarada's design system
cironunes
0
68
Beyond ng new
cironunes
2
200
Animate your Angular apps
cironunes
0
420
Sweet Angular, good forms never felt so good
cironunes
0
66
Sweet Angular, good forms never felt so good
cironunes
0
290
Angular: Um framework. Mobile & desktop.
cironunes
1
590
Firebase & Angular
cironunes
0
290
Other Decks in Technology
See All in Technology
Oracle Base Database Service 技術詳細
oracle4engineer
PRO
10
75k
250905 大吉祥寺.pm 2025 前夜祭 「プログラミングに出会って20年、『今』が1番楽しい」
msykd
PRO
1
980
TS-S205_昨年対比2倍以上の機能追加を実現するデータ基盤プロジェクトでのAI活用について
kaz3284
1
210
なぜテストマネージャの視点が 必要なのか? 〜 一歩先へ進むために 〜
moritamasami
0
230
20250912_RPALT_データを集める→とっ散らかる問題_Obsidian紹介
ratsbane666
0
100
AIをプライベートや業務で使ってみよう!効果的な認定資格の活かし方
fukazawashun
0
100
新アイテムをどう使っていくか?みんなであーだこーだ言ってみよう / 20250911-rpi-jam-tokyo
akkiesoft
0
310
共有と分離 - Compose Multiplatform "本番導入" の設計指針
error96num
2
1k
「何となくテストする」を卒業するためにプロダクトが動く仕組みを理解しよう
kawabeaver
0
420
普通のチームがスクラムを会得するたった一つの冴えたやり方 / the best way to scrum
okamototakuyasr2
0
100
CDK CLIで使ってたあの機能、CDK Toolkit Libraryではどうやるの?
smt7174
4
190
バイブスに「型」を!Kent Beckに学ぶ、AI時代のテスト駆動開発
amixedcolor
2
580
Featured
See All Featured
Testing 201, or: Great Expectations
jmmastey
45
7.7k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
15k
RailsConf 2023
tenderlove
30
1.2k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
5.6k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
127
53k
KATA
mclloyd
32
14k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
44
2.5k
How to train your dragon (web standard)
notwaldorf
96
6.2k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
The Art of Programming - Codeland 2020
erikaheidi
56
13k
GraphQLの誤解/rethinking-graphql
sonatard
72
11k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
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!