Custom offline page adalah salah satu cara menampilkan halaman pemberitahuan bila request tidak terdapat dalam cache dan tidak ada koneksi internet.
Browser sendiri sudah menampilkan pesan suatu halaman tidak bisa diakses pada saat tidak ada internet. Namun tampilannya nampak seperti error. Alangkah baiknya jika pesan ditampilkan dengan desain dan styling yang senada dengan web app kita.
Untuk mensimulasikanya dalam project, kita hapus dahulu cache dynamic melalui devTools. Pilih cache yang akan dihapus, click kanan, pilih Delete.
Setelah cache dihapus, jika Anda mengakses halaman help, akan tampil seperti umumnya browser tanpa internet koneksi. (cache perlu dihapus karena pada lecture sebelumnya halaman help sudah disimpan dalam cache).

Berikutnya kita buat satu file untuk menampilkan informasi bahwa cache belum pernah disimpan untuk halaman yang akan diakses. Lecture ini menggunakan nama file offlinepage.html, silakan menggunakan nama yang diinginkan.
Simpan file di public folder. Isi dari file offlinepage.html adalah file index.html, namun hanya akan berisi teks yang menginformasikan tentang cache belum disimpan. Tujuannya adalah agar tampilan dan styling tetap konsisten.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>TravelGram</title>
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700" rel="stylesheet">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.indigo-red.min.css" />
<!--<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/material-design-lite/1.3.0/material.indigo-pink.min.css">
//-->
<link rel="stylesheet" href="/src/css/app.css">
<link rel="manifest" href="/manifest.json">
</head>
<body>
<div id="app">
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
<header class="mdl-layout__header">
<div class="mdl-layout__header-row">
<!-- Title -->
<span class="mdl-layout-title">TravelGram</span>
<!-- Add spacer, to align navigation to the right -->
<div class="mdl-layout-spacer"></div>
<!-- Navigation. We hide it in small screens. -->
<nav class="mdl-navigation mdl-layout--large-screen-only">
<a class="mdl-navigation__link" href="/">Feed</a>
<a class="mdl-navigation__link" href="/help">Help</a>
<div class="drawer-option">
<button class="enable-notifications mdl-button mdl-js-button mdl-button--raised mdl-button--colored mdl-color--accent">
Enable Notifications
</button>
</div>
</nav>
</div>
</header>
<div class="mdl-layout__drawer">
<span class="mdl-layout-title">TravelGram</span>
<nav class="mdl-navigation">
<a class="mdl-navigation__link" href="/">Feed</a>
<a class="mdl-navigation__link" href="/help">Help</a>
<div class="drawer-option">
<button class="enable-notifications mdl-button mdl-js-button mdl-button--raised mdl-button--colored mdl-color--accent">
Enable Notifications
</button>
</div>
</nav>
</div>
<main class="mdl-layout__content mat-typography">
<div class="page-content">
<h5 class="text-center mdl-color-text--primary">Halaman yang Anda tuju tidak ada dalam cache</h5>
<p>Silakan kunjungi home page <a href="/">disini</a></p>
</div>
</main>
</div>
</div>
<script defer src="/src/js/material.min.js"></script>
<script src="/src/js/app.js"></script>
</body>
</html>
Pada file sw.js (file service worker), tambahkan cache ‘/offlinepage.html’ pada event ‘install’, lihat code dibawah.
Dan pada event fetch, pada bagian catch (pada lecture sebelumnya kosong), tambahkan perintah untuk load ‘offlinepage.html’ dari cache.
var APPSHELL_CACHE = 'appshell-v2';
var DYNAMIC_CACHE = 'dynamic';
//service worker life cycle event related
self.addEventListener('install', function(event){
console.log('[SW] Installing service worker...', event);
event.waitUntil(
caches.open(APPSHELL_CACHE)
.then(function(cache){
console.log('[SW] Pre-caching app shell..');
//cache.add('/');
//cache.add('/index.html');
//cache.add('/src/js/app.js');
cache.addAll([
'/',
'/index.html',
'/offlinepage.html',
'/src/js/app.js',
'/src/js/feed.js',
'/src/js/material.min.js',
'/src/css/app.css',
'/src/css/feed.css',
'/src/images/main-image.jpg',
'https://fonts.googleapis.com/css?family=Roboto:400,700',
'https://fonts.googleapis.com/icon?family=Material+Icons',
'https://code.getmdl.io/1.3.0/material.indigo-red.min.css'
]);
})
);
});
self.addEventListener('activate', function(event){
console.log('[SW] activating service worker...', event);
event.waitUntil(
caches.keys()
.then(function(keyList){
return Promise.all(keyList.map(function(key){
if (key !== APPSHELL_CACHE && key !== DYNAMIC_CACHE){
return caches.delete(key);
}
}));
})
)
return self.clients.claim();
});
self.addEventListener('fetch', function(event){
//console.log('[SW] fetching...', event);
event.respondWith(
caches.match(event.request)
.then(function(response){
if(response){
return response;
}else{
return fetch(event.request)
.then(function(res){
return caches.open(DYNAMIC_CACHE)
.then(function(cache){
cache.put(event.request.url, res.clone());
return res;
})
})
.catch(function(err){
// kode baru yang ditambahkan disini
return caches.open(APPSHELL_CACHE)
.then(function(cache){
return cache.match('/offlinepage.html');
});
});
}
})
);
});

Seperti biasa, jangan lupa untuk memastikan service worker terbaru sudah terinstal dengan menutup tab dan membuka ulang. Atau dengan menekan ‘skip waiting’ pada tab service worker di devtools, kemudian reload page.
Lalu simulasikan offline dengan mencentang checkbox offline pada devTools.
Bila berhasil maka, cache appshell-v2 akan berisi /offlinepage.html, dan saat kita akses halaman help, akan tampil seperti gambar diatas.
Ingat lecture sebelumnya halaman help sudah diakses, jadi bila Anda lupa menghapus cache dynamic secara manual melalui devTools, simulasi diatas tidak akan menampilkan halaman offlinepage.html.
Pendekatan ini akan memberikan pengalaman yang lebih baik untuk user, namun masih ada kelemahannya. Contoh, user mengakses suatu halaman yang mengembalikan file json namun tidak ada internet koneksi, maka halaman yang sama akan dikembalikan.
SIlakan bereksperimen untuk melakukan tuning, lecture ini bertujuan memberikan ide awal, bagaimana meng-handle halaman yang belum dicache dengan kondisi internet terputus.
Kesimpulan
Ada banyak strategi caching yang dapat dikembangkan berdasarkan kebutuhan. Strategi yang digunakan pada modul ini dan sebelumnya adalah Cache First, Network Fallback.

Ketika sebuah halaman di request, service worker akan mengintercept fetch dan mencoba mencari request tersebut didalam cache. Bila ditemukan, maka response akan dikembalikan dari cache. Bila tidak ditemukan, service worker akan melakukan request ke network dan response akan diteruskan ke pengguna.
- Keuntungan dari pendekatan ini adalah, web app akan dengan cepat menampilkan konten untuk user. Bila konten yang tidak ditemukan dalam cache, baru request ke network.
- Kekurangannya adalah, ada kemungkinan konten yang ditampilkan bukan yang terbaru.
Pada modul berikutnya kita akan bahas sedikit strategi Cache then Network, pendekatan yang paling mumpuni untuk berbagai skenario.