Using another variable in order to initialize the knockout observables - javascript

I am just wondering why one has to use a temporary variable "that" (initialized with the currently allocated object i.e. "this") in the script below:
$(document).ready(function() {
function ChatViewModel() {
var that = this;
that.userName = ko.observable('');
that.chatContent = ko.observable('');
that.message = ko.observable('');
that.messageIndex = ko.observable(0);
that.activePollingXhr = ko.observable(null);
var keepPolling = false;
that.joinChat = function() {
if (that.userName().trim() != '') {
keepPolling = true;
pollForMessages();
}
}
function pollForMessages() {
if (!keepPolling) {
return;
}
var form = $("#joinChatForm");
that.activePollingXhr($.ajax({url: form.attr("action"), type: "GET", data: form.serialize(), cache: false,
success: function(messages) {
console.log(messages);
for (var i = 0; i < messages.length; i++) {
that.chatContent(that.chatContent() + messages[i] + "\n");
that.messageIndex(that.messageIndex() + 1);
}
},
error: function(xhr) {
if (xhr.statusText != "abort" && xhr.status != 503) {
resetUI();
console.error("Unable to retrieve chat messages. Chat ended.");
}
},
complete: pollForMessages
}));
$('#message').focus();
}
that.postMessage = function() {
if (that.message().trim() != '') {
var form = $("#postMessageForm");
$.ajax({url: form.attr("action"), type: "POST",
data: "message=[" + that.userName() + "] " + $("#postMessageForm input[name=message]").val(),
error: function(xhr) {
console.error("Error posting chat message: status=" + xhr.status + ", statusText=" + xhr.statusText);
}
});
that.message('');
}
}
that.leaveChat = function() {
that.activePollingXhr(null);
resetUI();
this.userName('');
}
function resetUI() {
keepPolling = false;
that.activePollingXhr(null);
that.message('');
that.messageIndex(0);
that.chatContent('');
}
}
//Activate knockout.js
ko.applyBindings(new ChatViewModel());
});
Why can't I just use "this"? Can anyone please explain?

this always refers to the object that is in scope when the call has been made, and this can change depending on your code. If you want it to still be your object in a sub-function, then assigning it to a variable that won't change in value gets around this issue.

This refers to the owner.
You can rewrite your code like this :
$(document).ready(function() {
function ChatViewModel() {
var that = this;
this.userName = ko.observable('');
this.chatContent = ko.observable('');
this.message = ko.observable('');
this.messageIndex = ko.observable(0);
this.activePollingXhr = ko.observable(null);
var keepPolling = false;
this.joinChat = function() {
if (that.userName().trim() != '') {
keepPolling = true;
pollForMessages();
}
}
function pollForMessages() {
if (!keepPolling) {
return;
}
var form = $("#joinChatForm");
this.activePollingXhr($.ajax({url: form.attr("action"), type: "GET", data: form.serialize(), cache: false,
success: function(messages) {
console.log(messages);
for (var i = 0; i < messages.length; i++) {
that.chatContent(that.chatContent() + messages[i] + "\n");
that.messageIndex(that.messageIndex() + 1);
}
},
error: function(xhr) {
if (xhr.statusText != "abort" && xhr.status != 503) {
resetUI();
console.error("Unable to retrieve chat messages. Chat ended.");
}
},
complete: pollForMessages
}));
$('#message').focus();
}
this.postMessage = function() {
if (that.message().trim() != '') {
var form = $("#postMessageForm");
$.ajax({url: form.attr("action"), type: "POST",
data: "message=[" + that.userName() + "] " + $("#postMessageForm input[name=message]").val(),
error: function(xhr) {
console.error("Error posting chat message: status=" + xhr.status + ", statusText=" + xhr.statusText);
}
});
that.message('');
}
}
this.leaveChat = function() {
that.activePollingXhr(null);
resetUI();
that.userName('');
}
function resetUI() {
keepPolling = false;
that.activePollingXhr(null);
that.message('');
that.messageIndex(0);
that.chatContent('');
}
}
//Activate knockout.js
ko.applyBindings(new ChatViewModel());
//fixing bracet
});
Check this link: http://www.quirksmode.org/js/this.html

Related

Unexpected undefined return value from a function

I'm getting an unexpected undefined return value from a function.
function createselbox(arr) {
console.log('In createselbox');
var startstr = `Something`;
mytemp = '';
for (var i = 0; i < arr.length; i++) {
mytemp = mytemp + '<option>' + arr[i] + '</option>';
}
var ret = startstr + mytemp + `Something else `;
IsStaff = CheckifStaff();
console.log('Checkifstaff is ' + IsStaff);
if (IsStaff) {
console.log('Got true createselbox');
console.log('In sel loop');
ret = startstr + mytemp + `Something more`;
} else {
console.log('Got false createselbox');
}
return ret;
}
function CheckifStaff() {
var data = {
"isstaff": 'check'
};
data = $(this).serialize() + "&" + $.param(data);
$.ajax({
type: "POST",
dataType: "html",
url: "/functions/checkifstaff",
data: data,
success: function (data) {
var recdata = data;
if (recdata.indexOf('Has add permissions') != -1) {
console.log('Returning true');
return 1;
} else {
console.log('Returning false');
return 0;
}
},
error: function (xhr, textStatus, errorThrown) {
console.log('An error occured in CheckifStaff');
return 0;
}
});
}
The console shows:
In createselbox
appointment.js ? dev = 96168005 : 292 Checkifstaff is undefined
appointment.js ? dev = 96168005 : 305 Got false createselbox
appointment.js ? dev = 96168005 : 158 Returning true
createselbox is called first. But the value from the function is being returned as false, apparently even before the function execution.
Why is this happening?
CheckifStaff() has no explicit return statement, so it returns what all functions return by default, which is undefined.
To make this work, CheckiStaff needs to return something, here, for example CheckiStaff will return a Promise which's value is either 1 or 0. createselbox will now await CheckiStaff and then use the value it returns afterwards
async function createselbox(arr) {
console.log('In createselbox');
var startstr = `Something`;
mytemp = '';
for (var i = 0; i < arr.length; i++) {
mytemp = mytemp + '<option>' + arr[i] + '</option>';
}
var ret = startstr + mytemp + `Something else `;
let IsStaff = await CheckiStaff();
console.log('Checkifstaff is ' + IsStaff);
if (IsStaff) {
console.log('Got true createselbox');
console.log('In sel loop');
ret = startstr + mytemp + `Something more`;
} else {
console.log('Got false createselbox');
}
return ret;
}
function CheckifStaff() {
var data = {
"isstaff": 'check'
};
data = $(this).serialize() + "&" + $.param(data);
return new Promise((resolve, reject) => {
$.ajax({
type: "POST",
dataType: "html",
url: "/functions/checkifstaff",
data: data,
success: function (data) {
var recdata = data;
if (recdata.indexOf('Has add permissions') != -1) {
console.log('Returning true');
resolve(1);
} else {
console.log('Returning false');
resolve(0);
}
},
error: function (xhr, textStatus, errorThrown) {
console.log('An error occured in CheckifStaff');
resolve(0);
}
});
});
}
you can also do change your ajax to be like this
$.ajax({
type: "POST",
dataType: "html",
url: "/functions/checkifstaff",
data: data,
success: function (data) {
var recdata = data;
if (recdata.indexOf('Has add permissions') != -1) {
console.log('Returning true');
return 1;
} else {
console.log('Returning false');
return 0;
}
},
error: function (xhr, textStatus, errorThrown) {
console.log('An error occured in CheckifStaff');
return 0;
}
}).then(function(data){
return data
});
i'm not an expert, but i think when you use ajax you should wait for the response - use promise, or async/await func

How to deal with asynchronous problems in javascript

I want to display data stored in ch ! But my problem is that ch is displayed before the data is stored !
I think this is an Asynchronous Problems! How can I solve this problem.
When I try to get length of ch, I get always 0. Even if I store data statically in ch, I get the length 0.
I think this is an Asynchronous Problems! How can I solve this problem.
function RechercheFiltrée() {
var nom = document.getElementById('nompre').value;
var matricule = document.getElementById('matcle').value;
$.ajax({
url: "myWebServiceURL",
type: "GET",
dataType: "xml",
success: function(xml) {
var stock = [];
$(xml).find('Population').each(function() {
var table = document.getElementById("myTable");
$(this).find("directories").each(function()
{
dossier = $(this).attr('dossier');
stock.push(dossier);
});
});
var ch = [];
for (var i = 0; i < stock.length; i++) {
$.ajax({
url: "/mySecondWebServiceURL" + stock[i],
type: "GET",
dataType: "xml",
success: function(xml) {
var NMPRES = "";
var jsonObj = JSON.parse(xml2json(xml, ""));
var nom = jsonObj.SubmitResponse.occurrences.occurrence.filter(x => x["#datasection"] === "TS")[0].data.filter(x => x.item === "NMPRES")[0].value;
var matcle = jsonObj.SubmitResponse.occurrences.occurrence.filter(function(x) {
return x["#datasection"] === "LM"
})[0].data.filter(x => x.item === "MATCLE")[0].value;
var dossier = jsonObj.SubmitResponse.occurrences.occurrence.filter(function(x) {
return x["#datasection"] === "LM"
})[0]["#dossier"];
ch.push({
"nom": nom,
"matcle": matcle,
"dossier": dossier
});
if ($('#population').val() != null && firstIter == false) {
}
},
error: function(request, error) {
console.log('error Connexion : ' + error + ' request Connexion : ' + request);
}
});
}
var txt = "";
var firstIter = true;
for (var key in ch) {
if (ch[key].matcle === matricule) {
txt += "<option value='" + ch[key].dossier + "'" + firstSelect(firstIter) + ">" + ch[key].nom + "</option>";
firstIter = false;
}
}
$('#population').html(txt)
},
error: function(request, error) {
console.log('error Connexion : ' + error + ' request Connexion : ' + request);
}
});
}
The problem is that you are not waiting for the second service to respond.
It should be something like this:
const deferreds = stock.map((stockItem) => {
//... your logic with ch.push here
return $.ajax({
// your call to the second service
});
});
$.when(...deferreds).then(() => {
// your code
// for (var key in ch) {
});
The approach I'd rather take is to break the code down and use Promises. You really should take your time to learn Promises. It's a JavaScript standard and what jQuery uses under the hood.
function RechercheFiltrée() {
var nom = document.getElementById('nompre').value;
var matricule = document.getElementById('matcle').value;
return $.ajax({
url: "myWebServiceURL",
type: "GET",
dataType: "xml"
});
}
function getStockArray(xml) {
var stocks = [];
$(xml).find('Population').each(function() {
var table = document.getElementById("myTable");
$(this).find("directories").each(function() {
{
dossier = $(this).attr('dossier');
stocks.push(dossier);
});
});
});
return stocks;
}
function getStocks(stocks) {
return Promise.all(stocks.map(fetchStock));
}
function fetchStock (stock) {
return $.ajax({
url: "/mySecondWebServiceURL" + stock,
type: "GET",
dataType: "xml"
})
.then(formatStockInfo)
}
function formatStockInfo (xml) {
var NMPRES = "";
var jsonObj = JSON.parse(xml2json(xml, ""));
var nom = jsonObj.SubmitResponse.occurrences.occurrence.filter(x => x["#datasection"] === "TS")[0].data.filter(x => x.item === "NMPRES")[0].value;
var matcle = jsonObj.SubmitResponse.occurrences.occurrence.filter(function(x) {
return x["#datasection"] === "LM"
})[0].data.filter(x => x.item === "MATCLE")[0].value;
var dossier = jsonObj.SubmitResponse.occurrences.occurrence.filter(function(x) {
return x["#datasection"] === "LM"
})[0]["#dossier"];
if ($('#population').val() != null && firstIter == false) {
}
return {
"nom": nom,
"matcle": matcle,
"dossier": dossier
};
}
function updateSelectMenu (ch) {
var txt = "";
var firstIter = true;
for (var key in ch) {
if (ch[key].matcle === matricule) {
txt += "<option value='" + ch[key].dossier + "'" + firstSelect(firstIter) + ">" + ch[key].nom + "</option>";
firstIter = false;
}
}
$('#population').html(txt)
}
RechercheFiltrée()
.then(getStockArray)
.then(getStocks)
.done(updateSelectMenu);

Getting total page size: syncronicity problems

I'm trying to set up a script to get total page size, including images. I'm having serious problems with ajax asyncronicity and JQuery throws error when I try to set async: false due to negative effects in user experience.
The problem is, ajax calls to get image size return NaN in a random and very frequent way, surely due to too many concurrent connections.
Is there a way you can think to overcome this?
This is my code (it was much shorter in origin, callback approach was based on this post, but didn't work):
function getPageSize(callback)
{
var pageWeight = 0;
var lastWeight = 0;
var xhr = $.ajax({
type: "HEAD",
// async: false,
url: "test01.html",
success: function(msg)
{
if (xhr.readyState == 4)
{
if (xhr.status == 200 || xhr.status == 0)
{
if ( !isNaN(xhr.getResponseHeader('Content-Length')) && !isNaN($('#size').html()) )
{
pageWeight = parseInt(xhr.getResponseHeader('Content-Length'));
callback(pageWeight);
// lastWeight = parseInt($('#size').html());
// $('#size').html(pageWeight + lastWeight);
console.log("Page " + pageWeight);
}
}
}
}
});
}
function getImagesSize(callback)
{
var imageWeight = 0;
var lastWeight = 0;
$('img').each(function()
{
var imgPath = $(this).attr('src');
xhr = null;
xhr = $.ajax(
{
type: "HEAD",
url: $(this).attr('src'),
async: false,
success: function(msg)
{
if (xhr.readyState == 4)
{
if (xhr.status == 200 || xhr.status == 0)
{
if (!isNaN(xhr.getResponseHeader('Content-Length')) && !isNaN($('#size').html()) )
{
imageWeight = parseInt(xhr.getResponseHeader('Content-Length'));
callback(imageWeight);
// lastWeight = parseInt($('#size').html());
// $('#size').html(imageWeight + lastWeight);
console.log("Image " + imgPath + ": " + imageWeight);
}
}
}
}
});
});
}
function updateTotalPageSize(size)
{
var lastWeight = 0;
lastWeight = parseInt($('#size').html());
$('#size').html(size + lastWeight);
}
$(document).ready(function()
{
getPageSize(function(size)
{
//alert(size);
updateTotalPageSize(size);
});
getImagesSize(function(size)
{
//alert(size);
updateTotalPageSize(size);
});
});
SOLUTION by #Regent
function getImagesSize(callback)
{
var allImages = $('img');
function handleNext(index)
{
if (index >= allImages.length)
{
return;
}
var imgPath = allImages.eq(index).attr('src');
$.ajax({
type: "HEAD",
url: imgPath,
success: function(msg, status, xhr)
{
if (!isNaN(xhr.getResponseHeader('Content-Length')) && !isNaN($('#size').html()))
{
var imageWeight = parseInt(xhr.getResponseHeader('Content-Length'));
callback(imageWeight);
console.log("Image " + imgPath + ": " + imageWeight);
handleNext(index + 1);
}
}
});
}
handleNext(0);
}

Jquery form submit not working, instead get js warning 'body.scrollLeft is deprecated in strict mode' in console

I have the following code in my js file:
function PS_SL_HandleEvent()
{
$(document).ready(function() {
$('#form').removeAttr('onsubmit').submit(function(e) {
if(acceptCGV())
{
e.preventDefault();
if ($('#send_order_form input[type="radio"]:checked').val() == "")
{
resetAjaxQueries();
delSelection(1);
}
else
{
var carrierClass = $('input:radio[name="order_choose"]:checked').attr('class');
carrierClass = carrierClass.replace("carrier_","");
var radio_selector = '.delivery_options_address input[value="' + carrierClass + ',"], #carrierTable input[value="' + carrierClass + '"]';
$(radio_selector).attr('checked','checked');
resetAjaxQueries();
saveSelection(1);
}
}
else
e.preventDefault();
});
});
}
function saveSelection(is_submit)
{
$('#sendwithorder_errors').slideUp();
$('#sendwithorder_errors_list').children().remove();
//displayWaitingAjax('block', SL_RedirectTS);
//$('.SE_SubmitRefreshCard').fadeOut('fast');
var query = $.ajax({
type: 'POST',
headers: { "cache-control": "no-cache" },
url: baseDir + 'modules/sendwithorder/ajax.php' + '?rand=' + new Date().getTime(),
data: 'method=saveSelection&' + 'order_choose=' + $('#send_order_form input[name=order_choose]:checked').val(),
dataType: 'json',
success: function(json) {
if (json.length)
{
for (error in json)
$('#sendwithorder_errors_list').append('<li>'+json[error]+'</li>');
$('#sendwithorder_errors').slideDown();
displayWaitingAjax('none', '');
}
else
{
displayWaitingAjax('none', '');
if(is_submit==1)
{
$('#form').submit();
alert("sam");
}
//$('#show_carrier, .SE_SubmitRefreshCard span').show();
//$('.SE_SubmitRefreshCard').fadeIn('fast');
//$('#SE_AjaxSuccess').show().delay(3000).fadeOut();
//location.reload(true);
}
}
});
ajaxQueries.push(query);
return false;
}
Even "sam" is alerted but the form does not submit. Instead, I get the following warning on submit:
body.scrollLeft is deprecated in strict mode. Please use 'documentElement.scrollLeft' if in strict mode and 'body.scrollLeft' only if in quirks mode.
Even though there is a javascript warning, but this should not hinder form submit.
Your form cannot be submitted since you call e.preventDefault(); at every submit.
Try reorganizing the logic in submit() to either preventDefault() and go through your checks, or do nothing and let the form be submitted.
I did it like this and the code worked:
var js_submit = false;
function PS_SL_HandleEvent() {
$(document).ready(function () {
$('#form').removeAttr('onsubmit').submit(function (e) {
if (js_submit == false) {
if (acceptCGV()) {
e.preventDefault();
if ($('#send_order_form input[type="radio"]:checked').val() == "") {
resetAjaxQueries();
delSelection(1);
} else {
var carrierClass = $('input:radio[name="order_choose"]:checked').attr('class');
carrierClass = carrierClass.replace("carrier_", "");
var radio_selector = '.delivery_options_address input[value="' + carrierClass + ',"], #carrierTable input[value="' + carrierClass + '"]';
$(radio_selector).attr('checked', 'checked');
resetAjaxQueries();
saveSelection(1);
}
} else
e.preventDefault();
}
});
});
}
function saveSelection(is_submit) {
$('#sendwithorder_errors').slideUp();
$('#sendwithorder_errors_list').children().remove();
//displayWaitingAjax('block', SL_RedirectTS);
//$('.SE_SubmitRefreshCard').fadeOut('fast');
var query = $.ajax({
type: 'POST',
headers: {
"cache-control": "no-cache"
},
url: baseDir + 'modules/sendwithorder/ajax.php' + '?rand=' + new Date().getTime(),
data: 'method=saveSelection&' + 'order_choose=' + $('#send_order_form input[name=order_choose]:checked').val(),
dataType: 'json',
success: function (json) {
if (json.length) {
for (error in json)
$('#sendwithorder_errors_list').append('<li>' + json[error] + '</li>');
$('#sendwithorder_errors').slideDown();
displayWaitingAjax('none', '');
} else {
displayWaitingAjax('none', '');
if (is_submit == 1) {
js_submit = true;
$('#form').submit();
}
//$('#show_carrier, .SE_SubmitRefreshCard span').show();
//$('.SE_SubmitRefreshCard').fadeIn('fast');
//$('#SE_AjaxSuccess').show().delay(3000).fadeOut();
//location.reload(true);
}
}
});
ajaxQueries.push(query);
return false;
}

Function as a KnockoutJS observable

I have the following script (see below). I have two questions regarding it:
1.What does the following line mean in the context of Knockoutjs?
ko.observable(null);
2.How can I invoke a function not yet defined as in here:
that.activePollingXhr(...
Here is the full script:
$(document).ready(function() {
function ChatViewModel() {
var that = this;
that.userName = ko.observable('');
that.chatContent = ko.observable('');
that.message = ko.observable('');
that.messageIndex = ko.observable(0);
that.activePollingXhr = ko.observable(null);
var keepPolling = false;
that.joinChat = function() {
if (that.userName().trim() != '') {
keepPolling = true;
pollForMessages();
}
}
function pollForMessages() {
if (!keepPolling) {
return;
}
var form = $("#joinChatForm");
that.activePollingXhr($.ajax({url: form.attr("action"), type: "GET", data: form.serialize(), cache: false,
success: function(messages) {
console.log(messages);
for (var i = 0; i < messages.length; i++) {
that.chatContent(that.chatContent() + messages[i] + "\n");
that.messageIndex(that.messageIndex() + 1);
}
},
error: function(xhr) {
if (xhr.statusText != "abort" && xhr.status != 503) {
resetUI();
console.error("Unable to retrieve chat messages. Chat ended.");
}
},
complete: pollForMessages
}));
$('#message').focus();
}
that.postMessage = function() {
if (that.message().trim() != '') {
var form = $("#postMessageForm");
$.ajax({url: form.attr("action"), type: "POST",
data: "message=[" + that.userName() + "] " + $("#postMessageForm input[name=message]").val(),
error: function(xhr) {
console.error("Error posting chat message: status=" + xhr.status + ", statusText=" + xhr.statusText);
}
});
that.message('');
}
}
that.leaveChat = function() {
that.activePollingXhr(null);
resetUI();
this.userName('');
}
function resetUI() {
keepPolling = false;
that.activePollingXhr(null);
that.message('');
that.messageIndex(0);
that.chatContent('');
}
}
//Activate knockout.js
ko.applyBindings(new ChatViewModel());
});
ko.observable(null); creates an observable with a value of null. Nothing different than ko.observable(5);, where the value would be 5.
I see that you're using the that.activePollingXhr observable by passing it the result of an ajax call. However, this call is asynchronous and $.ajax doesn't return the data it got from the server, but rather a jquery deferred. You need to use that.activePollingXhr insude the success callback. Here's is how your code might look like:
$.ajax({url: form.attr("action"), type: "GET", data: form.serialize(), cache: false,
success: function(messages) {
console.log(messages);
for (var i = 0; i < messages.length; i++) {
that.chatContent(that.chatContent() + messages[i] + "\n");
that.messageIndex(that.messageIndex() + 1);
}
that.activePollingXhr(messages); // <-- Note where the call to activePollingXhr is
},
error: function(xhr) {
if (xhr.statusText != "abort" && xhr.status != 503) {
resetUI();
console.error("Unable to retrieve chat messages. Chat ended.");
}
},
complete: pollForMessages
});
As for the comment under your question: that.activePollingXhr is defined as that.activePollingXhr = ko.observable(null); - an observable with value of null.
That just initializes an observable with null as the initial value.
If you need to invoke a function that is an observable, just add a second set of parenthesis.
that.activePollingXhr()()

Categories

Resources