Not able to retrieve offfline.html from service workers - progressive-web-apps

I am working on PWA project. As per requirement, cached data is dynamic. Means, we are not using self.__WB_Manifest to cache the data. Instead of that, all the URL's sending over the API and storing those URL's under one cache.
Now issue is that, if certain html page, which is in same domain and same origin but not cached, in those case we want to show offline.html page.
Usually it will show if we use self.__WB_Manifest. But, as per requirement, we are not using it.
We do have offline.html page in cached data. Even it's present in folder structure, but it not triggering it.
Can anybody please help me guide on it, in case how to pickup the URL from cache and show as a offline page when app is in offline mode and particular page is not cached.
Currently using Workbox V6
addEventListener('fetch',async (event)=>{
console.log("add event listener fetch" + event);
event.respondWith(
caches.match(event.request).then(async (cachedResponse) =>{
console.log("inside cached response" + event.request.mode);
if (cachedResponse) {
console.log("fetch event if");
return cachedResponse;
}else if(!cachedResponse){
console.log("fetch event else");
return fetch(event.request);
}else{
const cache = await caches.open(OFFLINE_PAGES_RESPONSE);
return cache.match('/offline.html');
}
}).catch(async()=>{
console.log("inside cache");
const cache = await caches.open(OFFLINE_PAGES_RESPONSE);
return cache.match('/offline.html');
})
);
});

Related

Service worker goes to redudent phase because pre-caching files are blocked by Ad Blocker(a chrome extension.)

All the logic get failed because chrome extensions block one of my js files,
Is there a way to make pre-caching more robust? even if I get an error when caching some files, I can still cache most of them correctly, and I have run time caching.
If you're using workbox-precaching, then the answer is no—it's designed so that service worker installation will only proceed if all of the items in the precache manifest were successfully added to the cache. That way, you're guaranteed to have a functional set of resources available offline. (There's a longstanding feature request for adding support for "optional" precaching, but it's not clear how that would work in practice.)
I'd recommend using runtime caching for URLs that are optional, and might be blocked by browser extensions. If you want to "warm" the cache in this scenario, and you don't care if the cache population fails, you can add your own logic along the lines of:
import {CacheFirst} from 'workbox-strategies';
import {registerRoute} from 'workbox-routing';
import {precacheAndRoute} from 'workbox-precaching';
const OPTIONAL_CACHE_NAME = 'optional-resources';
const OPTIONAL_URLS = [
// Add URLs here that might be blocked.
];
self.addEventListener('install', (event) => {
event.waitUntil((async () => {
const cache = await caches.open(RUNTIME_CACHE_NAME);
for (const url of OPTIONAL_URLS) {
try {
await cache.add(url);
} catch (e) {
// Ignore failures due to, e.g., a content blocker.
}
}
})());
});
// Precache everything in the manifest, which you need to
// configure to exclude your "optional" URLs.
precacheAndRoute(self.__WB_MANIFEST);
// Use a cache-first runtime strategy.
registerRoute(
// Check url.pathname, or url.href, if OPTIONAL_URLS
// contains full URLs.
({url}) => OPTIONAL_URLS.includes(url.pathname),
new CacheFirst({cacheName: OPTIONAL_CACHE_NAME}),
);

Workbox Precaching / RuntimeCaching hybrid

I have converted my app into a PWA with workbox, and using the precaching strategy
Right now I reload the page when the workbox worker has finished refetching the cache
// Register service worker extract
import { register } from 'register-service-worker';
if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, {
updatefound() {
// New content is downloading.
},
updated() {
// New content is available; refresh.
setTimeout(() => {
window.location.reload(true);
}, 500);
},
});
}
// Service worker extract
import { precacheAndRoute } from 'workbox-precaching/precacheAndRoute';
precacheAndRoute(self.__WB_MANIFEST);
self.skipWaiting();
But I find it really bothering to have a stale version for 2-5 seconds and then the page reloaded with the new version
What I would like to achieve, is RuntimeCaching when an update is found, the new files are used directly instead of refetching the cache in the background
Is there a way to configure workbox for that, so that I can reload the page straight away
// Register service worker extract
updatefound() {
window.location.reload(true);
},
updated() {
},
And the worbox worker will not serve the cache on the reloaded page and instead make the network requests on the fly, basically a Precaching and RuntimeCaching hybrid to get the best of both worlds?
I couldn't find anything that achieves that anywhere
What you're describing is using a NetworkFirst strategy, along with optionally "warming" the runtime cache with the content that you want to make sure is available offline.
Precaching, with its cache-first approach to serving content, doesn't sound like an appropriate solution to your use case.

Is it possible to execute a long running function before the browser is reloaded?

I do prevent a page reload in my web application by the following function:
window.onbeforeunload = (event) => {
const e = event || window.event;
// Cancel the event
e.preventDefault();
save_user_data_to_indexed_db();
if (e) {
e.returnValue = ''; // Legacy method for cross browser support
}
return ''; // Legacy method for cross browser support
};
However, the save_user_data_to_indexed_db() function is not being executed during the "Reload site?" message. I thought that if I could execute my function during the displayed message, I could maybe automatically answer the same dialog programmatically and let the browser continue reloading the page.
Is there a way to make the browser wait for this kind of operation?
Generally, there is no way to make the browser wait. What I often do in this case is write the data to an intermediate place, such as localStorage, synchronously, and then asynchronously copy that data over to indexedDB later on when there is time, such as when the page is next loaded again, or from within a service worker.

self.addEventListener('fetch', function(e) { }) is not working

I have a doubt in PWA and will be glad if someone helps me with that. In my PWA I don't have any problem with storing static files like HTML, JS & CSS. But am facing Issues on dynamic data. i.e : my self.addEventListener('fetch', function(e) { }) is not getting called, but other functionalities are working fine i.e: 'install' and 'active' event.
to be more particular, I am using #angular/service-worker which worked fine but I created another sw file called sw.js. In my sw-js I'm listening to the events like 'install' 'active' and 'fetch'. My sw.js fetch is not getting called whereas the other two methods work well. But while fetching the ngsw-worker.js's fetch method alone gets called.
The thing I need is to make CRUD Operations in PWA with angular.
Thanks in advance!
You can do the dynamic caching like below , the service worker will intercept every request and add in to the cache.
self.addEventListener("fetch", function (event) {
event.respondWith(
caches.open("dynamiccache").then(function (cache) {
return fetch(event.request).then(function (res) {
cache.put(event.request, res.clone());
return res;
})
})
)
}
Note : You can't cache POST Requests
Can service workers cache POST requests?

What are the options for offline registration and forms?

I have a project that caters for individuals with poor internet connections in predominantly rural areas. I need to allow for users to download(or any other applicable means), or fill out details offline and then when they are ready and the internet connection is ready the data filled out offline should sync with the online database and give a report.
The offline form also needs the same validation as online, to ensure no time wastage.
What are the options I know that HTML 5 has an offline application ability. I would prefer an open source option, which will allow people with intermittent internet issues to continue filling out a form or series of forms even though internet has dropped, and the data sync when internet reconnects.
So what are the best options? Having the user requiring to download a large application is also not the best case, I would prefer a browser or small download solution. Maybe even a way of downloading a validatable form in some format for re-upload.
This is something I've been muddling through myself as some of the users of the site I am currently tasked with building have poor connections or would like to fill in forms away from a network for various reasons. Depending on your precise needs and your customer's browser compatibility, the solution I've decided to go with is to use the HTML5 cache capability you mention in your post.
The amount of data stored is not that great, and it will mean that the webpage you want them to fill in is available offline.
If you couple this with the localStorage interface you can keep all form submissions until they regain connection.
As an example of my current solution:
The cache.php file, to write the manifest
<?php
header("Content-Type: text/cache-manifest");
echo "CACHE MANIFEST\n";
$pages = array(
//an array of the pages you want cached for later
);
foreach($pages as $page) {
echo $page."\n";
}
$time = new datetime("now");
//this makes sure that the cache is different when the browser checks it
//otherwise the cache will not be rebuilt even if you change a cached page
echo "#Last Build Time: ".$time->format("d m Y H:i:s T");
You can then have a simple ajax script checking for connection
setInterval( function() {
$.ajax({
url: 'testconnection.php',
type: 'post',
data: { 'test' : 'true' },
error: function(XHR, textStatus, errorThrown) {
if(textStatus === 'timeout') {
//update a global var saying connection is down
noCon = true;
}
}
});
if(hasUnsavedData) {
//using the key/value pairs in localstorage, put together a data object and ajax it into the database
//once complete, return unsavedData to false to prevent refiring this until we have new data
//also using localStorage.removeItem(key) to clear out all localstorage info
}
}, 20000 /*medium gap between calls, do whatever works best for you here*/);
Then for your form submission script, use localstorage if that noCon variable is set to true
$(/*submit button*/).on("click", function(event) {
event.preventDefault();
if(noCon) {
//go through all inputs in some way and put to localstorage, your method is up to you
$("input").each( function() {
var key = $(this).attr("name"), val = $(this).val();
localStorage[key] = val;
});
hasUnsavedData = true;
//update a global variable to let the script above know to save information
} else {
//or if there's connection
$("form").submit();
//submit the form in some manner
}
});
I've not tested every script on this page, but they're written based on the skeleton of what my current solution is doing, minus a lot of error checking etc, so hopefully it will give you some ideas on how to approach this
Suggestions for improvements are welcomed