How to use service worker to respond to navigation requests? - javascript

I was trying to build a PWA with the help of a service worker when it comes to caching, everything went smooth. But I ran into a curious problem. I could not serve my assets with SW when the app is offline. It seems that SW always fails to respond to a 'navigate' request.
Uncaught (in promise) TypeError: Failed to fetch
this.addEventListener('fetch', async event => {
event.respondWith(
(async function() {
const requestObj = event.request;
console.log(event);
const urlParts = requestObj.url.split('/');
const fileName = urlParts[urlParts.length - 1];
const fileExtension = fileName.split('.')[fileName.split('.').length - 1];
if (requestObj.method === 'GET') {
if (requestObj.mode === 'navigate' && event.request.headers.get('accept').includes('text/html')) {
console.log('Navigating', requestObj);
const urlParts = requestObj.url.split('/');
console.log(urlParts);
console.log('looking for another option...');
caches.match(event.request).then(function(response) {
return response || fetch(event.request);
});
}
// If its an image, then save it if it is in '.png' format
if (fileExtension === 'jpg' || requestObj.destination === 'image') {
caches
.match(requestObj)
.then(res => {
if (!res) {
throw new TypeError('Bad response status');
} else {
return res;
}
})
.catch(() => {
fetch(requestObj).then(response => {
console.log(response);
if (response.ok || (response.type === 'opaque' && response.status === 0)) {
caches.open('v1').then(cache => {
cache.put(requestObj, response);
});
}
return response;
});
return fetch(requestObj);
});
}
///////////////////////
if (
requestObj.destination === 'script' ||
requestObj.destination === 'style' ||
requestObj.destination === 'font'
) {
caches
.match(requestObj)
.then(response => {
if (response) {
return response;
} else {
throw new TypeError('Bad response status');
}
})
.catch(() => {
fetch(requestObj).then(res => {
if (res.ok) {
caches.open('v1').then(cache => {
cache.put(requestObj, res);
});
}
return res.clone();
});
});
}
//////////////////////
}
return fetch(requestObj);
})()
);
});

I don't think you need the async function inside the fetch event handler, caches.match returns a promise so it is good enough to be the parameter for the respondWith method
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(function(response) {
const requestObj = event.request;
console.log(event);
const urlParts = requestObj.url.split('/');
const fileName = urlParts[urlParts.length - 1];
const fileExtension = fileName.split('.')[fileName.split('.').length - 1];
if (requestObj.method === 'GET') {
if (requestObj.mode === 'navigate' && event.request.headers.get('accept').includes('text/html')) {
console.log('Navigating', requestObj);
const urlParts = requestObj.url.split('/');
console.log(urlParts);
console.log('looking for another option...');
caches.match(requestObj).then(function(response) {
return response || fetch(event.request);
});
}
// If its an image, then save it if it is in '.png' format
if (fileExtension === 'jpg' || requestObj.destination === 'image') {
caches
.match(requestObj)
.then(res => {
if (!res) {
throw new TypeError('Bad response status');
} else {
return res;
}
})
.catch(() => {
fetch(requestObj).then(response => {
console.log(response);
if (response.ok || (response.type === 'opaque' && response.status === 0)) {
caches.open('v1').then(cache => {
cache.put(requestObj, response);
});
}
return response;
});
return fetch(requestObj);
});
}
///////////////////////
if (
requestObj.destination === 'script' ||
requestObj.destination === 'style' ||
requestObj.destination === 'font'
) {
caches
.match(requestObj)
.then(response => {
if (response) {
return response;
} else {
throw new TypeError('Bad response status');
}
})
.catch(() => {
fetch(requestObj).then(res => {
if (res.ok) {
caches.open('v1').then(cache => {
cache.put(requestObj, res);
});
}
return res.clone();
});
});
}
return fetch(requestObj);
}
})
)
});

Related

Axios to return value from interceptor without actually making call

I want to check for value in my Axios interceptor for a value in the params. If it matches a condition, don't make the call, but return an empty array.
Here is my entire Axios config:
import Axios from 'axios'
import store from '#/store'
const AxiosConfig = Axios.create({
baseURL: process.env.VUE_APP_BASE_API_URL
})
const addToken = (config) => {
const token = store.getters['AuthModule/getAuthToken']
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
}
let numOfRequests = 0
AxiosConfig.interceptors.request.use(
(config) => {
addToken(config)
if (config.params.autocomplete.length < 2){
// don't make the call and return an empty [] like I did make the call.
}
store.commit('SET_LOADER_TO_STATE', true)
numOfRequests++
return config
},
(error) => {
store.commit('SET_LOADER_TO_STATE', false)
numOfRequests--
return Promise.reject(error)
}
)
AxiosConfig.interceptors.response.use(
(response) => {
numOfRequests--
if (numOfRequests === 0) {
store.commit('SET_LOADER_TO_STATE', false)
}
return response
},
(error) => {
store.commit('SET_LOADER_TO_STATE', false)
numOfRequests--
if (numOfRequests === 0) {
store.commit('SET_LOADER_TO_STATE', false)
}
if (
error.response.status === 401 &&
error?.response?.data?.error?.message === 'Unauthenticated.'
) {
store.dispatch('AuthModule/logoutUser')
return
}
if (error?.response?.data?.error) {
throw new Error(
error?.response?.data?.error.message ||
error.response.data.error ||
error.response.data
)
} else if (error?.response?.data?.errors) {
const errors = {}
const errorsList = error?.response?.data?.errors
for (const key in errorsList) {
if (Object.prototype.hasOwnProperty.call(errorsList, key)) {
errors[key] = errorsList[key][0]
}
}
throw new Error(JSON.stringify(errors))
} else if (error) {
throw new Error(error.response.data.message)
} else {
throw new Error('Network Error')
}
}
)
export default AxiosConfig
Is this possible?

async and await not workingg

its not waiting for validation and just run the else part :| where is my mistake?
async validateBeforeSubmit(event) {
await this.$validator.validateAll().then( result => {
if (result) {
console.log(result); // just log the -> true
// go submit
}else{
console.log(result); // just log the -> false
event.preventDefault();
var elmnt = document.getElementById("drop_zone");
elmnt.scrollIntoView();
}
})
.catch(error=>console.log(error));
},
i'm using veevalidator and i define some custom rules that need afew seconds to resolve:
created() {
this.$validator.extend('unique', {
// getMessage: field => 'At least one ' + field + ' needs to be checked.',
async validate(value, arg) {
arg = arg[0];
let sw = false;
if (arg == 'n_code') {
let data = {
'n_code': value
}
await Axios.post(duplicate_ncode, data, {
headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content') }
})
.then((response) => {
if (response.data == true) {
sw = true;
}
})
.catch(error => console.log(error));
if (sw) {
return true;
} else {
return false;
}
}
if (arg == 'email') {
let data = {
'email': value
}
await Axios.post(duplicate_email, data, {
headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content') }
})
.then((response) => {
if (response.data == true) {
sw = true;
}
})
.catch(error => console.log(error));
if (sw) {
return true;
} else {
return false;
}
}
if (arg == 'mobile') {
let data = {
'mobile': value
}
await Axios.post(duplicate_mobile, data, {
headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content') }
})
.then((response) => {
if (response.data == true) {
sw = true;
}
})
.catch(error => console.log(error));
if (sw) {
return true;
} else {
return false;
}
}
// console.log('questions', value, testProp, options.some((option) => option[testProp]));
// return true;
}
});
}
when user fill all fields it will check 3 api and that need momnets to check.
and i need to await to get the answer but there is somthing wrong which is not working.
please help
I believe what you want to do is:
async validateBeforeSubmit(event) {
try {
const result = await this.$validator.validateAll();
if (result) {
// go submit
} else {
event.preventDefault();
var elmnt = document.getElementById('drop_zone');
elmnt.scrollIntoView();
}
}
catch (e) {
console.log(e);
}
},

properties are undefined inside of promises

This is my typescript function where I'm trying to use a promise:
public onEditSubmit() {
const model = this._sharedService.createUpdateModel(
null,
this.editForm
) as LOG;
model.fileId = this.fileId;
model.startEffectiveDate = Shared.toISODate(model.startEffectiveDate);
model.endEffectiveDate = Shared.toISODate(model.endEffectiveDate);
let deferredExecutionCheck = new Promise((resolve, reject) => {
this._updateService
.getAllById(this.selectedItem.LogId)
.subscribe(
r => {
this.records = r;
this.records.forEach(element => {
if (
element.StatusId === 1 ||
element.StatusId === 2 ||
element.StatusId === 4 ||
element.StatusId === 5
) {
this._notificationService.showErrorMessage(
`MESSAGE GOES HERE`,
"IN PROGRESS"
);
reject("In Progress");
}
});
resolve("Not In Progress");
},
e => {
throw e;
}
);
console.log("finished");
});
let originalEditSubmit = function(result: any) {
if (this.editMode === "Add") {
this.add(model);
} else {
if (
(model.wfStatusId === Status.Review ||
model.wfStatusId === Status.LoadFailed ||
model.wfStatusId === Status.Completed) &&
model.eventStatusId === eStatus.Cancelled
) {
this._confirmDlg.closable = false;
this._confSvc.confirm({
accept: () => {
model.cancelRcdb = true;
this.update(model);
},
message: "Cancel RCdB Dataset?",
reject: () => {
model.cancelRcdb = false;
this.update(model);
}
});
} else {
this.update(model);
}
}
};
deferredExecutionCheck.then(
result => originalEditSubmit(result),
error => console.log("error", error)
);
}
Error: Uncaught (in promise): TypeError: Cannot read property
'editMode' of undefined TypeError: Cannot read property 'editMode' of
undefined at originalEditSubmit
I moved the this.fileId property outside of the originalEditSumbmit method and it now is being read. But now it seems like this.editMode is now having the same issue.
Can I not have these properties inside of my promises like this?
change
let originalEditSubmit = function(result: any) {
to
let originalEditSubmit = (result: any) => {

Using while loop in Promise()

I have a user list and i am checking all user with certain details.I am using sequelize js with express.I want to know that can we use while loop like this for searching and saving data in database.Please help me.Thanks In advance.
let royalty_bonus = async (sponsor) => {
return await new Promise((resolve, reject) => {
models.RoyaltyUser.findById(sponsor)
.then(async (sponsorRow) => {
let user_level = 1;
let sponsor_id = sponsorRow;
try {
while (sponsor_id != null && user_level <= 3) {
let level_length = await getLevel(sponsor_id.id, user_level);
if (user_level === 1 && level_length.length === 3) {
console.log('Level One Achieved By ', sponsor_id.id);
} else if (user_level === 2 && level_length.length === 9) {
console.log('Level Two Is Achieved By ', sponsor_id.id);
} else {
console.log('No Level');
}
await models.RoyaltyUser.findOne({where: {id: sponsor_id.sId}})
.then((sponsor_new_row) => {
sponsor_id = sponsor_new_row;
})
.catch((e) => {
console.log(' Inner Catch Error ', e.message);
reject();
});
user_level++;
}
resolve();
}
catch (e) {
reject(e);
}
})
.catch((e) => {
reject('catch ', e.message);
});
});
};
router.get('/royalty_user', async (req, res, next) => {
royalty_bonus(4)
.then(() => {
console.log('done');
})
.catch((e) => {
console.log('Catch two', e.message);
})
});
Avoid the Promise constructor antipattern, avoid return await, and don't mix .then callbacks with async/await syntax. You can simplify a lot:
async function royalty_bonus(sponsor) {
const sponsorRow = await models.RoyaltyUser.findById(sponsor);
let user_level = 1;
let sponsor_id = sponsorRow;
while (sponsor_id != null && user_level <= 3) {
let level_length = await getLevel(sponsor_id.id, user_level);
if (user_level === 1 && level_length.length === 3) {
console.log('Level One Achieved By ', sponsor_id.id);
} else if (user_level === 2 && level_length.length === 9) {
console.log('Level Two Is Achieved By ', sponsor_id.id);
} else {
console.log('No Level');
}
const sponsor_new_row = await models.RoyaltyUser.findOne({where: {id: sponsor_id.sId}});
sponsor_id = sponsor_new_row;
user_level++;
}
}
router.get('/royalty_user', (req, res, next) => {
royalty_bonus(4).then(() => {
console.log('done');
}, e => {
console.log('Catch two', e.message);
});
});

Adding images and CSS files ALONGSIDE HTML file in service worker for offline caching

I have this service worker:
'use strict';
const CACHE_VERSION = 1;
let CURRENT_CACHES = {
offline: 'offline-v' + CACHE_VERSION
};
const OFFLINE_URL = 'http://example.com/offline.html';
function createCacheBustedRequest(url) {
let request = new Request(url, {cache: 'reload'});
if ('cache' in request) {
return request;
}
let bustedUrl = new URL(url, self.location.href);
bustedUrl.search += (bustedUrl.search ? '&' : '') + 'cachebust=' + Date.now();
return new Request(bustedUrl);
}
self.addEventListener('install', event => {
event.waitUntil(
fetch(createCacheBustedRequest(OFFLINE_URL)).then(function(response) {
return caches.open(CURRENT_CACHES.offline).then(function(cache) {
return cache.put(OFFLINE_URL, response);
});
})
);
});
self.addEventListener('activate', event => {
let expectedCacheNames = Object.keys(CURRENT_CACHES).map(function(key) {
return CURRENT_CACHES[key];
});
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (expectedCacheNames.indexOf(cacheName) === -1) {
console.log('Deleting out of date cache:', cacheName);
return caches.delete(cacheName);
}
})
);
})
);
});
self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate' ||
(event.request.method === 'GET' &&
event.request.headers.get('accept').includes('text/html'))) {
console.log('Handling fetch event for', event.request.url);
event.respondWith(
fetch(createCacheBustedRequest(event.request.url)).catch(error => {
console.log('Fetch failed; returning offline page instead.', error);
return caches.match(OFFLINE_URL);
})
);
}
});
It's the standard service worker -
This is my service worker:
'use strict';
const CACHE_VERSION = 1;
let CURRENT_CACHES = {
offline: 'offline-v' + CACHE_VERSION
};
const OFFLINE_URL = 'offline.html';
function createCacheBustedRequest(url) {
let request = new Request(url, {cache: 'reload'});
if ('cache' in request) {
return request;
}
let bustedUrl = new URL(url, self.location.href);
bustedUrl.search += (bustedUrl.search ? '&' : '') + 'cachebust=' + Date.now();
return new Request(bustedUrl);
}
self.addEventListener('install', event => {
event.waitUntil(
fetch(createCacheBustedRequest(OFFLINE_URL)).then(function(response) {
return caches.open(CURRENT_CACHES.offline).then(function(cache) {
return cache.put(OFFLINE_URL, response);
});
})
);
});
self.addEventListener('activate', event => {
let expectedCacheNames = Object.keys(CURRENT_CACHES).map(function(key) {
return CURRENT_CACHES[key];
});
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (expectedCacheNames.indexOf(cacheName) === -1) {
console.log('Deleting out of date cache:', cacheName);
return caches.delete(cacheName);
}
})
);
})
);
});
self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate' ||
(event.request.method === 'GET' &&
event.request.headers.get('accept').includes('text/html'))) {
console.log('Handling fetch event for', event.request.url);
event.respondWith(
fetch(createCacheBustedRequest(event.request.url)).catch(error => {
console.log('Fetch failed; returning offline page instead.', error);
return caches.match(OFFLINE_URL);
})
);
}
});
It's the standard offline cache service worker: https://googlechrome.github.io/samples/service-worker/custom-offline-page/
How do I cache images and CSS files? Right now, I create an offline page with in-line CSS and converted by images into SVG code. This is not ideal.
Do I need to have multiple service workers with different IDs for images for offline-caching?
Or can I use 1 service worker for multiple elements for offline caching?
Caches can store multiple requests so you can call cache.put() several times, you could write:
var ASSETS = ['/index.html', '/js/index.js', '/style/style.css'];
self.oninstall = function (evt) {
evt.waitUntil(caches.open('offline-cache-name').then(function (cache) {
return Promise.all(ASSETS.map(function (url) {
return fetch(url).then(function (response) {
return cache.put(url, response);
});
}));
}))
};
Or, similarly and shorter, using addAll():
var ASSETS = ['/index.html', '/js/index.js', '/style/style.css'];
self.oninstall = function (evt) {
evt.waitUntil(caches.open('offline-cache-name').then(function (cache) {
return cache.addAll(ASSETS);
}))
};
You can find an example loading the set of assets from an external resource in the Service Worker Cookbook.

Categories

Resources