Update variable value for onbeforeunload - javascript

In my application, I'm trying to use sendBeacon to send data to my remote server. One of the data that I need is how many clicks its been on the page and I'm doing it as follows:
var clickNumber = 0;
document.addEventListener("mouseup", function () {clickNumber++;});
var SendToRemote = window.SendToRemote || [];
SendToRemote.init({
clicks: clickNumber
});
My sendBeacon
navigator.sendBeacon = (url, data) =>
window.fetch(url, { method: 'POST', body: {data: data}, credentials: 'include' });
My only issue now is that the clickNumber is always 0 (which is the default value) and even that mouseup does increment clickNumber, but when sending it sends 0.
How am I able to update the clickNumber so when sendBeacon is triggered, it gets the incremented/updated clickNumber instead of 0.
This is my SendToRemote.init which works fine: (PS: I have removed parts of the codes as it would be over 1000 lines, but kept whats needed):
if (!SendToRemote) {
var SendToRemote = (function(){
var defaults = {
endpoints: {
unload: "https://remote.com"
},
processData: function(results){},
},
results = {
click: 0,
// more stuff here
},
support = !!document.querySelector && !!document.addEventListener,
settings;
var actions = {
sendData: function () {
results.hmn = parseInt(actions.hmnDetection(
results.times.tt, results.times.tp, results.click, results.mouse, results.touch, results.scroll, results.tab
));
let url = settings.endpoints.unload,
data = JSON.stringify(results);
navigator.sendBeacon(url, data);
},
// more functions here
}
function init(options) {
document.addEventListener('DOMContentLoaded', (event) => {
// More stuff here
// Event Listener to porcess
if(modifiable.processOnAction){
let node = document.querySelector(modifiable.selector);
if(!!!node) throw new Error('Selector was not found.');
actions._e(node, modifiable.event, `init-${modifiable.selector}-processOnAction`, function() {
let nodeInput = document.getElementsByName(modifiable.element)[0];
if(!!!nodeInput) throw new Error('nodeInput was not found.');
nodeInput.value = JSON.stringify(results);
hasProcessed = true;
})
}
addEventListener('unload', (event) => {
if (!navigator.sendBeacon) {
navigator.sendBeacon = (url, data) =>
window.fetch(url, { method: 'POST', body: {data: data}, credentials: 'include' });
}
if (!hasProcessed) {
actions.sendData();
hasProcessed = true;
}
return;
});
});
}
function processResults(){
if(settings.hasOwnProperty('processData')){
if (!modifiable.processOnAction){
return settings.processData.call(undefined, results);
}
return results;
}
return false;
}
// Module pattern, only expose necessary methods
return {
init: init,
processResults: processResults,
};
})();
}
Thanks in advance!

Related

How to populate dropdown options dynamically using a promise

import { getCustomer } from './Customers';
let optionItems=[];
export const LOV: React.FunctionComponent = () => {
const loadCustomer = async () => {
const data = await getCustomer();
for (var i=0; i < data.value.length ; ++i)
{
optionItems.push({
key: data.value[i].CustomerId,
text: data.value[i].CustomerName
});
}
}
useEffect(() => {
loadCustomer();
}, [])
return (
<SearchableDropdown options={optionItems}/>
);};
Code in Customers.tsx
export const getCustomer = async (): Promise<any> => {
const response = await
$.ajax({
url: apiURL,
type: "GET",
headers: headers,
data: null,
cache: false,
beforeSend: function (request) {
request.setRequestHeader("Authorization", 'Bearer ' + accessToken);
}
})
.done( function (data) {
return data;
})
.fail(function (jqXHR) {
if (jqXHR.status == 401) {
promptAuth(jqXHR.getResponseHeader("WWW-Authenticate"));
}
else {
console.log("NOT 401");
}
});
return response;
}
I'm trying to populate a dropdown dynamically using a promise. I'm using Fluent-react Dropdown. getCustomer() loads values to const data. However, I can't see any values in the dropdown even though data is not null. Please help.
Fluent Dropdown => https://developer.microsoft.com/en-us/fluentui#/controls/web/dropdown
Your optionsItems need to be reactive. Try using useState-hook
export const LOV: React.FunctionComponent = () => {
const [optionItems, setOptionItems] = useState([]);
const loadCustomer = async () => {
const data = await getCustomer();
const options = [];
for (var i=0; i < data.value.length ; ++i)
{
options.push({
key: data.value[i].CustomerId,
text: data.value[i].CustomerName
});
}
setOptionItems(options)
}
useEffect(() => {
loadCustomer();
}, [])
return (
<SearchableDropdown options={optionItems}/>
);};
Please make sure your data.value[i].CustomerId and data.value[i].CustomerName are valid strings. You need to supply string, but it looks like your API call returns null there. After you fix this problem, together with Stutje's solution your app should start to work.

IndexedDB is not showing data in debugger but showing in console

I am doing a ajax call and store the response data into indexedDB. The response is an array of strings. After checking many questions and suggestions, i have followed the answer of #buley and did like this below :
$(document).ready(function () {
var db_name = 'mobileClaimsDb',
store_name = 'claims';
$.ajax({
url: 'index.cfm?action=main.appcache',
type: 'post',
cache:true,
success: function (data) {
var request,
upgrade = false,
doTx = function (db, entry) {
addData(db, entry, function () {
getData(db);
});
},
getData = function (db) {
db.transaction([store_name], "readonly").objectStore(store_name).openCursor(IDBKeyRange.lowerBound(0)).onsuccess = function (event) {
var cursor = event.target.result;
if (null !== cursor) {
console.log("entry", cursor.value);
cursor.continue();
}
};
},
addData = function (db, entry, finished) {
console.log('adding', entry);
var tx = db.transaction([store_name], "readwrite"),
claims = [];
tx.addEventListener('complete', function (e) {
finished();
});
$.each(claims, function (key, value) {
tx.objectStore(store_name).add(value);
});
};
request = window.indexedDB.open(db_name);
request.oncomplete = function (event) {
if (upgrade) {
doTx(request.result, data);
}
};
request.onsuccess = function (event) {
if (!upgrade) {
doTx(request.result, data);
}
};
request.onupgradeneeded = function (event) {
var db = event.target.result;
db.createObjectStore('claims', {
keyPath: null,
autoIncrement: true
});
}
}
});
});
It is creating the db and store too but data is not showing while opening the store.But, it is showing the data in console with key/value pair. I am not understand why it is not showing.
It is showing like this.
In console, it is populating properly.
What changes i need to do to show the key and value pair in store.

load content into page asynchronously with ajax promise

I want to asynchronously load content into my page when I click on the links on my navigation bar. While, using the .load() method works I am having difficulty figuring out how to make the promise object work the same way. I am also confused as why my form is visible when i click 'home' even though i set it to be hidden.
http://jsfiddle.net/Piq9117/Lzw514md/
// Navigation
var navigation = (function () {
var $nav = $('#navigation a');
var $page = $('#page');
var $container = $('#container');
$nav.on('click', function (e) {
e.preventDefault();
$page.remove();
dataservice.getPage()
.done(function (data) {
$container.html($(data).find('#page'));
})
.fail(function (jqXHR, statusText, err) {
alert(err);
})
})
// the .load works fine..
// $nav.on('click', function(e) {
// e.preventDefault();
// var url = $(this).attr('href');
// $page.remove();
// $container.load(url + ' #page');
// })
})();
// Promise object, passed to navigation
var dataservice = (function () {
var getPage = function () {
var url = $(this).attr('href'); // this returns undefined
return $.ajax({
type: 'GET',
url: url,
})
}
return {
getPage: getPage
}
})();
// chache/hide/show form
(function () {
var form = {
init: function () {
this.cacheDom();
this.events();
},
cacheDom: function () {
this.$container = $('.container');
this.$page = this.$container.find('#page');
this.$showbtn = this.$container.find('#showbtn');
this.$form = this.$page.find('#form');
this.$form.hide();
this.$submitbtn = this.$page.find('#submitbtn');
},
events: function () {
this.$showbtn.on('click', this.showForm.bind(this));
this.$submitbtn.on('click', this.hideForm.bind(this));
},
showForm: function () {
this.$form.fadeIn(300);
this.$showbtn.hide(300);
},
hideForm: function (e) {
e.preventDefault(300);
this.$form.hide(300);
this.$showbtn.show(300);
}
}
form.init();
})();
this within your dataservice.getPage() function is not the dom element that you want it to be. You would need to pass that element in as an argument and since all you really need is the href maybe it would look better just passing in the url
var getPage = function (url) {
return $.ajax({
type: 'GET',
url: url,
})
}
Then in even handler pass in the href:
$nav.on('click', function (e) {
e.preventDefault();
$page.remove();
dataservice.getPage(this.href) // pass in `href`
.done(function (data) {
$container.html($(data).find('#page'));
}).fail(function (jqXHR, statusText, err) {
alert(err);
});
})
The problem is that you have a reference to $(this) in your getPage method. $(this) doesn't exist in this context, you have to pass it through all your methods.
// Navigation
var navigation = (function () {
var $nav = $('#navigation a');
var $page = $('#page');
var $container = $('#container')
$nav.on('click', function (e) {
e.preventDefault();
$page.remove();
dataservice.getPage($(this))
.done(function (data) {
$container.html($(data).find('#page'));
})
.fail(function (jqXHR, statusText, err) {
alert(err);
})
})
// the .load works fine..
// $nav.on('click', function(e) {
// e.preventDefault();
// var url = $(this).attr('href');
// $page.remove();
// $container.load(url + ' #page');
// })
})();
// Promise object, passed to navigation
var dataservice = (function () {
var getPage = function ($this) {
var url = $this.attr('href'); // this returns undefined
return $.ajax({
type: 'GET',
url: url,
})
}
return {
getPage: function($this) {
getPage($this);
}
}
})();
// chache/hide/show form
(function () {
var form = {
init: function () {
this.cacheDom();
this.events();
},
cacheDom: function () {
this.$container = $('.container');
this.$page = this.$container.find('#page');
this.$showbtn = this.$container.find('#showbtn');
this.$form = this.$page.find('#form');
this.$form.hide();
this.$submitbtn = this.$page.find('#submitbtn');
},
events: function () {
this.$showbtn.on('click', this.showForm.bind(this));
this.$submitbtn.on('click', this.hideForm.bind(this));
},
showForm: function () {
this.$form.fadeIn(300);
this.$showbtn.hide(300);
},
hideForm: function (e) {
e.preventDefault(300);
this.$form.hide(300);
this.$showbtn.show(300);
}
}
form.init();
})();
here is the fiddle (check your javascript console to see the ajax requests)

Javascript jQuery then in interval

I'm checking the server if a job is done. I don't want to spam the server so that's why I will use setInterval.
I call the trigger function, and when the job is done on the server (perhaps 2-3 calls before it's done), the function is done.
I know that I could call my finishFunction in the interval and kind of solve it. But I would like to return it because I call the trigger function from another js file. And if it's possible, I would like to handle it there.
function trigger() {
var response = startInterval();
response.then(function() {
//done finishFunction()
});
}
function checkServer() {
var obj = { test: true }
var respons = $.ajax({
url: "MyUrl",
data: JSON.stringify({ obj: obj }),
type: "POST",
contentType: "application/json",
dataType: "JSON",
cache: false
});
return respons;
}
function startInterval() {
var inProgress = false;
var interval = setInterval(function () {
if (!inProgress) {
inProgress = true;
var response = checkServer().then(function (data) {
var isDoneInQueue = JSON.parse(data.d);
if (isDoneInQueue) {
//finishFunction()???
clearInterval(interval);
return response;
};
inProgress = false;
});
}
}, 1000);
}
Return a Deferred object from the function, and resolve it when the server job is done:
function startInterval() {
var result = $.Deferred();
var inProgress = false;
var interval = setInterval(function () {
if (!inProgress) {
inProgress = true;
checkServer().then(function (data) {
var isDoneInQueue = JSON.parse(data.d);
if (isDoneInQueue) {
clearInterval(interval);
result.resolve(data);
};
inProgress = false;
});
}
}, 1000);
return result;
}
Whatever you call the resolve method with, is sent to the function that you use with the then method:
function trigger() {
startInterval().then(function(data) {
//done finishFunction()
});
}

How can I get sinon to yield to multiple callbacks

I am trying to stub several ajax calls, but I want to have both beforeSend and success executed, is this possible?
I want something like this:
var stub = sinon.stub(jQuery, "ajax");
stub.onCall(0).yieldsTo("beforeSend").yieldsTo("success", {some: 'data'});
stub.onCall(1).yieldsTo("beforeSend").yieldsTo("success", {other: 'stuff'});
But this skips the 'beforeSend' method.
I know it would be easier to allow ajax to do it's stuff and use sinon's fakeServer, but I can't as I'm testing in Node with a fake browser and it just doesn't work
You could use yieldTo after the call:
var stub = sinon.stub();
stub({
foo: function() {
console.log('foo');
},
bar: function() {
console.log('bar');
}
});
stub.yieldTo('foo');
stub.yieldTo('bar');
I was able to work around this by adding some additional code:
var responses = {};
var executionComplete;
beforeEach(function () {
executionComplete = $.Deferred();
sinon.stub($, "ajax", function (options) {
if (options.beforeSend) {
options.beforeSend();
}
completeAjaxCall(options);
});
});
afterEach(function () {
$.ajax.restore();
});
var completeAjaxCall = function (options) {
var response = findResponse(options.url, options.type);
setTimeout(function () {
if (response.code < 400) {
if (options.dataFilter && response.data) {
response.data = options.dataFilter(JSON.stringify(response.data));
}
if (options.success) {
options.success(response.data);
}
} else {
if (options.error) {
options.error(response.data);
}
}
if (options.complete) {
options.complete();
}
if (response.completeExecution) {
executionComplete.resolve();
}
}, response.serverResponseTime);
};
var findResponse = function (url, type) {
var response = responses[url];
expect(response, url + ' should have a response').to.exist;
expect(response.type).to.equal(type);
delete responses[url];
if (Object.keys(responses).length === 0) {
response.completeExecution = true;
}
return response;
};
var givenResponse = function (response) {
responses[response.url] = response;
};
Then in my test I can use it like this:
it('should do some stuff', function (done) {
//given
givenResponse({serverResponseTime: 4, code: 200, url: '/saveStuff', type: 'POST'});
givenResponse({serverResponseTime: 1, code: 200, url: '/getStuff', type: 'GET'});
//when
$('button').click();
//then
executionComplete.then(function () {
expect(something).to.be.true;
done();
});
});

Categories