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
910
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
59
Type safe CSS with Reason
cironunes
0
130
What I've learned building automated docs for Ansarada's design system
cironunes
0
79
Beyond ng new
cironunes
2
210
Animate your Angular apps
cironunes
0
440
Sweet Angular, good forms never felt so good
cironunes
0
85
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
SREのプラクティスを用いた3領域同時 マネジメントへの挑戦 〜SRE・情シス・セキュリティを統合した チーム運営術〜
coconala_engineer
2
670
プロダクト成長を支える開発基盤とスケールに伴う課題
yuu26
4
1.3k
Bill One 開発エンジニア 紹介資料
sansan33
PRO
5
17k
AzureでのIaC - Bicep? Terraform? それ早く言ってよ会議
torumakabe
1
580
[CV勉強会@関東 World Model 読み会] Orbis: Overcoming Challenges of Long-Horizon Prediction in Driving World Models (Mousakhan+, NeurIPS 2025)
abemii
0
140
【Oracle Cloud ウェビナー】[Oracle AI Database + AWS] Oracle Database@AWSで広がるクラウドの新たな選択肢とAI時代のデータ戦略
oracle4engineer
PRO
2
170
~Everything as Codeを諦めない~ 後からCDK
mu7889yoon
3
430
Data Hubグループ 紹介資料
sansan33
PRO
0
2.7k
20260208_第66回 コンピュータビジョン勉強会
keiichiito1978
0
180
OpenShiftでllm-dを動かそう!
jpishikawa
0
130
10Xにおける品質保証活動の全体像と改善 #no_more_wait_for_test
nihonbuson
PRO
2
320
usermode linux without MMU - fosdem2026 kernel devroom
thehajime
0
240
Featured
See All Featured
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.6k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1k
The Cult of Friendly URLs
andyhume
79
6.8k
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.1k
GraphQLの誤解/rethinking-graphql
sonatard
74
11k
What does AI have to do with Human Rights?
axbom
PRO
0
2k
Balancing Empowerment & Direction
lara
5
890
Future Trends and Review - Lecture 12 - Web Technologies (1019888BNR)
signer
PRO
0
3.2k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.4k
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
1
300
The Limits of Empathy - UXLibs8
cassininazir
1
220
Google's AI Overviews - The New Search
badams
0
910
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!