Django PWA Tutorial Series:
- Introduction
- Add Web app manifest to Django
- Add Service Worker to Django
- Fallback Offline Page in Django
- Caching and Routing (coming soon)
- How to add install button to PWA (coming soon)
- Send Web Push Notification from Django (part 1) (coming soon)
- Send Web Push Notification from Django (part 2) (coming soon)
The source code is available on https://github.com/AccordBox/django-pwa-demo
Objectives
By the end of this chapter, you should be able to:
- Use
Service worker
to cache the offline page, and return cached response when network is not available.
Offline Page
First, we create an offline page in Django
Update django_pwa_app/urls.py
from django.contrib import admin
from django.urls import path
from django.views.generic import TemplateView
import django_pwa_app.views
urlpatterns = [
path('', TemplateView.as_view(template_name="index.html")),
path('sw.js', django_pwa_app.views.service_worker),
path('offline/', TemplateView.as_view(template_name="offline.html")), # new
path('admin/', admin.site.urls),
]
Create django_pwa_app/templates/offline.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>You are offline</title>
<!-- Inline the page's stylesheet. -->
<style>
body {
font-family: helvetica, arial, sans-serif;
margin: 2em;
}
h1 {
font-style: italic;
color: #373fff;
}
p {
margin-block: 1rem;
}
button {
display: block;
}
</style>
</head>
<body>
<h1>You are offline</h1>
<p>
The page will automatically reload once the connection is re-established.
Click the button below to try reloading manually.
</p>
<button type="button">⤾ Reload</button>
<!-- Inline the page's JavaScript file. -->
<script>
document.querySelector('button').addEventListener('click', () => {
window.location.reload();
});
// Listen to changes in the network state, reload when online.
// This handles the case when the device is completely offline.
window.addEventListener('online', () => {
window.location.reload();
});
// Check if the server is responding & reload the page if it is.
// This handles the case when the device is online, but the server
// is offline or misbehaving.
async function checkNetworkAndReload() {
try {
const response = await fetch('.');
// Verify we get a valid response from the server
if (response.status >= 200 && response.status < 500) {
window.location.reload();
return;
}
} catch {
// Unable to connect to the server, ignore.
}
window.setTimeout(checkNetworkAndReload, 2500);
}
checkNetworkAndReload();
</script>
</body>
</html>
Notes:
- The above source code come from Create an offline fallback page
- It demonstrates both manual reload based on a button press as well as automatic reload based on the online event and regular server polling.
(env)$ python manage.py runserver
# check on http://127.0.0.1:8000/offline/
We wil see the offline page, and it will keep reloading. This is the correct behavior.
Let's close the page.
Service Worker
Update frontend/src/sw.js
const manifest = self.__WB_MANIFEST;
if (manifest) {
// do nothing
}
// https://web.dev/offline-fallback-page/
const CACHE_NAME = 'offline-html';
const FALLBACK_HTML_URL = '/offline/';
self.addEventListener('install', (event) => {
event.waitUntil(
// Setting {cache: 'reload'} in the new request will ensure that the
// response isn't fulfilled from the HTTP cache; i.e., it will be from
// the network.
caches.open(CACHE_NAME)
.then((cache) => cache.add(
new Request(FALLBACK_HTML_URL, { cache: "reload" })
))
);
// Force the waiting service worker to become the active service worker.
self.skipWaiting();
});
self.addEventListener('activate', function(event) {
// Tell the active service worker to take control of the page immediately.
self.clients.claim();
});
self.addEventListener("fetch", event => {
event.respondWith(
caches.match(event.request)
.then(response => {
return response || fetch(event.request);
})
.catch(() => {
return caches.match(FALLBACK_HTML_URL);
})
);
});
Notes:
- In the
install
event handler, we add the response ofFALLBACK_HTML_URL
to theCacheStorage
- In the
fetch
event handler, we usecaches.match
to checks if a match for the request is found in theCacheStorage
. If so, use it, if not,fetch
the request. - If the
fetch(event.request)
fail, try to serve thefallback offline page
Simple Test
$ cd frontend
$ npm run watch
# in another terminal
(env)$ python manage.py runserver
If we visit http://127.0.0.1:8000/
, and check the Cache Storage
, we will see the page response has been stored in the Cache Storage
Now, let's terminate the Django server, and try to reload the page again.
As you can see, the offline page is served instead. And it will do regular server polling.
If we run Django dev server again, then the home page
will display again!
Django PWA Tutorial Series:
- Introduction
- Add Web app manifest to Django
- Add Service Worker to Django
- Fallback Offline Page in Django
- Caching and Routing (coming soon)
- How to add install button to PWA (coming soon)
- Send Web Push Notification from Django (part 1) (coming soon)
- Send Web Push Notification from Django (part 2) (coming soon)
The source code is available on https://github.com/AccordBox/django-pwa-demo