CTO @
Google Developer Expert at Angular
with Angular since 2013
🇺🇦 Angular Kharkiv Meetup organizer
Angular Workshops on javascript.info
?
use
Angular
@Injectable()
export class RetryInterceptor implements HttpInterceptor {
intercept(httpRequest: HttpRequest<any>,
next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(httpRequest).pipe(retry(2));
}
}
Angular
HttpClient
Interceptors
ServiceWorkers
HttpClient
All requests
navigator.serviceWorker.register("/sw.js");
navigator.serviceWorker.register"/sw.js",
{ scope: "/"});
This could be for the following reasons:
https://bncb2v.csb.app/sw.js
, and the app's root is https://bncb2v.csb.app/
. But the path needs to be written as /sw.js
.install
self.addEventListener("install", (event) => { //... });
self.addEventListener("install", (event) => {
event.waitUntil(addToCache(["/", "/index.html", "/some.js"...]));
});
const addToCache = async (resources) => {
const cache = await caches.open("v1");
await cache.addAll(resources);
};
activate
fetch
self.addEventListener("fetch", (event) => {
event.respondWith(caches.match(event.request));
});
message
navigator.serviceWorker.ready.then((registration) => {
registration.active.postMessage("Hi service worker");
});
addEventListener("message", (event) => {
// event is an ExtendableMessageEvent object
console.log(`The client sent me a message: ${event.data}`);
event.source.postMessage("Hi client");
});
navigator.serviceWorker.addEventListener('message', (event) => {
console.log(event.data);
});
sync
self.addEventListener('periodicsync', (event) => {
if (event.tag === 'fetch-news') {
event.waitUntil(fetchAndCacheLatestNews());
}
});
const registration = await navigator.serviceWorker.ready;
await registration.periodicSync.register('fetch-news', {
minInterval: 24 * 60 * 60 * 1000,
});
push
self.addEventListener("push", (event) => {
const message = event.data.json();
});
Do you know how many SWs already registered in your browser?
ng add @angular/pwa
{
"$schema": "./node_modules/@angular/service-worker/config/schema.json",
"index": "/index.html",
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/manifest.webmanifest",
"/*.css",
"/*.js"
]
}
},
{
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
]
}
}
]
}
{
"$schema": "./node_modules/@angular/service-worker/config/schema.json",
"index": "/index.html",
"assetGroups": [
...
{
"name": "externals",
"installMode": "lazy",
"updateMode": "lazy",
"resources": {
"urls": ["https://angular.io/assets/images/logos/angular/angular.svg"]
}
}
...
]
}
{
...
"assetGroups": [ ... ],
"dataGroups": [
{
...
},
{
...
}
]
}
export interface DataGroup {
name: string;
urls: string[];
version?: number;
cacheConfig: {
maxSize: number;
maxAge: string;
timeout?: string;
strategy?: 'freshness' | 'performance';
};
cacheQueryOptions?: {
ignoreSearch?: boolean;
};
}
strategy: 'freshness' | 'performance';
performance(default) - cache first
freshness - network first
"dataGroups": [
{
"name": "random.org",
"urls": ["https://api.random.org/**"],
"cacheConfig": {
"maxSize": 20,
"maxAge": "7d",
"strategy": "freshness"
}
}
]
constructor(swUpdate: SwUpdate) {
swUpdate.available.subscribe(event => {
console.log('UPDATE!');
// user prompt?
// document.location.reload());
});
}