Unable to pass variables to onreadystatechange - javascript

This is the current source code:
var xhttp = new XMLHttpRequest();
function passVars(var1, var2, var3) {
if (var1.readyState == 4) {
if (var1.status == 200) {
var data = var1.responseText;
if (data) {
playSuccess();
classSwitch(data, var2, var3);
} else {
playFailure();
alert("Error: returned status code " + var1.status + " " + var1.statusText);
}
}
}
}
xhttp.onreadystatechange = function() {
passVars(xhttp, "tab", "p1234");
};
xhttp.open("POST", "index.php", true);
xhttp.send(formData); //formdata is a POST send to PHP's backend which returns responseText = 0 or 1 for var data
function classSwitch(state, sOrigin, stateButtonID) {
if (sOrigin === "tab") {
Monitored = state;
if (state === "1") {
if (document.getElementById("setmonitoring").classList.contains("productmonitoringdisabled")) {
document.getElementById("setmonitoring").classList.remove("productmonitoringdisabled");
}
document.getElementById("setmonitoring").classList.add("productmonitoringenabled");
}
if (state === "0") {
if (document.getElementById("setmonitoring").classList.contains("productmonitoringenabled")) {
document.getElementById("setmonitoring").classList.remove("productmonitoringenabled");
}
document.getElementById("setmonitoring").classList.add("productmonitoringdisabled");
}
}
if (sOrigin === "issues") {
if (state === "1") {
if (document.getElementById(stateButtonID).classList.contains("productmonitoringdisabled")) {
document.getElementById(stateButtonID).classList.remove("productmonitoringdisabled");
} else document.getElementById(stateButtonID).classList.add("productmonitoringenabled");
}
if (state === "0") {
if (document.getElementById(stateButtonID).classList.contains("productmonitoringenabled")) {
document.getElementById(stateButtonID).classList.remove("productmonitoringenabled");
} else document.getElementById(stateButtonID).classList.add("productmonitoringdisabled");
}
}
}
Tried a lot of ways to pass them using mainly SO anwsers and each time var2 and var2 are undefined. This is used in an inventory control system, early alpha version. The idea is to pass element's id to change button class when the backend returns product's current monitoring state
Any ideas how to pass those variables? Thanks in advance

Your structure is no good. You can't pass xhttp like that in a asynchronous call. Rewrite it directly like this...
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4) {
if (xhttp.status == 200) {
var data = xhttp.responseText;
if (data) {
playSuccess();
classSwitch(data, 'tab', '1234');
} else {
playFailure();
alert("Error: returned status code " + xhttp.status + " " + xhttp.statusText);
}
}
}
};

Below is a minimal example of how to pass parameters to the onreadystatechange callback function. You've overcomplicated it a bit - there's no need to pass your xhttp variable to the callback either since it's already available in scope.
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://developer.mozilla.org/");
xhr.send();
xhr.onreadystatechange = function() {
callback("tab", "1234");
};
function callback(var1, var2) {
console.log(`var1 = ${var1}`);
console.log(`var2 = ${var2}`);
//Do your xhr.readyState and xhr.status checks in this function
console.log(`xhr.readyState = ${xhr.readyState}`);
console.log(`xhr.status = ${xhr.status}`);
}

used
function passVars(var1, var2, var3) {
xhr.onreadystatechange = function(e) {}
}
the "e" in the function bracket will call back all var you have before in the function passVars

Related

XMLHttpRequest.readyState & XMLHttpRequest.status final stage comparison

I have a script that I want to see if I can fix a comparison.
this.refreshLyric = function (currentSong, currentArtist) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState === 4 && this.status === 200) {
var data = JSON.parse(this.responseText);
var openLyric = document.getElementsByClassName('lyrics')[0];
if (data.type === 'exact' || data.type === 'aprox') {
var lyric = data.mus[0].text;
document.getElementById('lyric').innerHTML = lyric.replace(/\n/g, '<br />');
//debugging
console.log("Success Lyric found");
} else {
//debugging
console.log("Lyric not found");
}
} else {
//HERE if the condition is not met, it goes to another function
var page = new Page();
page.refreshLyric2(currentSong, currentArtist);
}
}
xhttp.open('GET', 'https://api.vagalume.com.br/search.php?apikey=' + API_KEY + '&art=' + currentArtist + '&mus=' + currentSong.toLowerCase(), true);
xhttp.send()
}
The code is simple, but what I want I cannot achieve.
This comparison to be true has to go through some previous states:
if (this.readyState === 4 && this.status === 200) {
XMLHttpRequest.readyState:
Value State Description
0 UNSENT Client has been created. open() not called yet.
1 OPENED open() has been called.
2 HEADERS_RECEIVED send() has been called, and headers and status are available.
3 LOADING Downloading; responseText holds partial data.
4 DONE The operation is complete.
XMLHttpRequest.status:
Before the request completes, the value of status is 0. Browsers also report a status of 0 in case of XMLHttpRequest errors.
UNSENT: 0
OPENED: 0
LOADING: 200
DONE: 200
What I want to do is if the final stage comparison of the states is not identical to 4 and 200 respectively then go to another function.
if (this.readyState === 4 && this.status === 200) {
//run this code
.....
} else {
//Go to another function
var page = new Page();
page.refreshLyric2(currentSong, currentArtist);
}
Is it possible to achieve this, or am I daydreaming?
If you want different actions in the final stage depending on the status, you need nested if statements. The first if detects the final stage, then you test the status.
this.refreshLyric = function(currentSong, currentArtist) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4) {
if (this.status == 200) {
var data = JSON.parse(this.responseText);
var openLyric = document.getElementsByClassName('lyrics')[0];
if (data.type === 'exact' || data.type === 'aprox') {
var lyric = data.mus[0].text;
document.getElementById('lyric').innerHTML = lyric.replace(/\n/g, '<br />');
//debugging
console.log("Success Lyric found");
} else {
//debugging
console.log("Lyric not found");
}
} else {
// go to another function
var page = new Page();
page.refreshLyric2(currentSong, currentArtist);
}
}
}
xhttp.open('GET', 'https://api.vagalume.com.br/search.php?apikey=' + API_KEY + '&art=' + currentArtist + '&mus=' + currentSong.toLowerCase(), true);
xhttp.send()
}

I tried to create AJAX function but it shows noting in output

I tried to create AJAX function but it shows noting in output
var ajaxObj = function(url, meth, data = "") {
var x = new XMLHttpRequest();
x.onreadystatechange = function() {
if (x.readyState == 4 && x.status == 200) {
this.responseAjax = this.responseText;
}
}
x.open(meth, url, true);
x.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
x.send(data);
}
function showHint(str) {
var xhttp = new ajaxObj("gethint.php?q=" + str, "GET");
if (str.length == 0) {
document.getElementById("txtHint").innerHTML = "";
return;
}
document.getElementById("txtHint").innerHTML = xhttp.responseAjax;
}
<!DOCTYPE html>
<html>
<body>
<h3> Start typing a name in the input field below :</h3>
<form action="">
First Name : <input type="text" id="txt1" onkeyup="showHint(this.value)">
</form>
<p>Suggestions:
<sapn id="txtHint"></sapn>
</p>
</body>
</html>
I tried to get suggested Names from gethint.php file when user starts typing into text box.
But it seems that responseAjax gets value after showHint() call please help me.
You need to handle the AJAX request asynchronously. In your showHint function when you call
document.getElementById("txtHint").innerHTML = xhttp.responseAjax;
the AJAX call has not yet returned, so the xhttp.responseAjax object is not yet defined. You need to wait to handle the response once it arrives. You can pass a callback function in to the ajaxObj definition, and the object will call that function when it gets its response.
var ajaxObj = function(url, meth, callback, data = "") {
var x = new XMLHttpRequest();
x.onreadystatechange = function() {
if (x.readyState == 4 && x.status == 200) {
// we're ready to handle the response data now
callback(x.responseText);
}
}
x.open(meth, url, true);
x.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
x.send(data);
}
// Callback function to be invoked when AJAX is complete
function fillTxtHint(responseHtml)
{
document.getElementById("txtHint").innerHTML = responseHtml;
}
function showHint(str) {
if (str.length == 0) {
document.getElementById("txtHint").innerHTML = "";
}
// Pass in the callback function to be invoked when AJAX has returned
ajaxObj("gethint.php?q=" + str, "GET", fillTxtHint);
}
this inside onreadystatechange's handler is an instance of XMLHttpRequest so the line this.responseAjax = this.responseText is adding a field to the XMLHttpRequest object and setting its value to another field in the same object. This is completely redundant. In showHint, xhttp is an instance of ajaxObj and there is no responseAjax field ever defined for this object. You can directly set innerHTML of the element that shows suggestion inside the handler of onreadystatechange like the following:
function getSuggestions (meth, data) {
meth = meth.toUpperCase();
var params = "q=" + data;
var url = (meth == "GET") ? "gethint.php?" + params : "gethint.php";
var elem = document.getElementById("txtHint");
if (data.length == 0) {
elem.innerHTML = "";
return;
}
var x = new XMLHttpRequest();
x.onreadystatechange = function() {
if (x.readyState == 4 && x.status == 200) {
elem.innerHTML = this.responseText;
}
}
x.open(meth, url, true);
x.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
if (meth == "GET") {
x.send();
} else {
x.send(params);
}
}
and showHint becomes:
function showHint(str) {
getSuggestions ("GET", str);
}

Returning the return value from inner function to parent function- Javascript, Dynamics crm

Will the below code return the boolean value from the innner function to the parent function displayButton()? The parent function is called on click of a button in dynamics CRM. The function should return a boolean value depending if the a case is selected and the selected is active or resolve.
//function called on click of a button in ms crm.
function displayButton()
{
var Obj = parent.Xrm.Page.getAttribute("regardingobjectid");
var ObjValue = Obj.getValue();
//parent.Xrm.Utility.alertDialog(" Value: " + ObjValue);
if (ObjValue == null)
return false;
//else
// parent.Xrm.Utility.alertDialog(" Hi");
var EntityType = ObjValue[0].entityType;
var Guid = ObjValue[0].id;
var id = Guid.slice(1, -1);
//parent.Xrm.Utility.alertDialog(" Guid: " + id);
//Checking if regarding field is selected a case lookup value
if (EntityType == "incident")
{
var req = new XMLHttpRequest();
req.open("GET", parent.Xrm.Page.context.getClientUrl() + "/api/data/v8.2/incidents(" + id + ")?$select=statecode", true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
req.onreadystatechange = function ()
{
if (this.readyState === 4)
{
req.onreadystatechange = null;
if (this.status === 200)
{
debugger;
var result = JSON.parse(this.response);
//checking if selected case is active or resolved.
var statecode = result["statecode"];
var statecode_formatted = result["statecode#OData.Community.Display.V1.FormattedValue"];
if (statecode_formatted == "Active") {
return true;
}
else if (statecode_formatted == "Resolved")
return false;
else {
return false;
}
}
else
{
parent.Xrm.Utility.alertDialog("Zero");
}
}
};
req.send();
}
else {
return false;
}
}
No. If you want to access the value returned by your asynchronous XmlHttpRequest, either you'll need to put your logic in the if (this.status === 200) scope or provide a callback.
Your XMLHttpRequest is asynchronous because of the true parameter in this line:
req.open("GET", parent.Xrm.Page.context.getClientUrl() + "/api/data/v8.2/incidents(" + id + ")?$select=statecode", true);
To provide a callback, separate out your code into two functions:
function getCaseState(id, callback) {
var req = new XMLHttpRequest();
req.open("GET", parent.Xrm.Page.context.getClientUrl() + "/api/data/v8.2/incidents(" + id + ")?$select=statecode", true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
req.onreadystatechange = function () {
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 200) {
var result = JSON.parse(this.response);
var statecode = result["statecode"];
var statecode_formatted = result["statecode#OData.Community.Display.V1.FormattedValue"];
callback(statecode_formatted);
}
}
};
req.send();
}
Then call getCaseState, passing it the id of your incident and a function to call once the request is ready:
// called on click of a button in CRM.
function displayButton() {
var regarding = parent.Xrm.Page.getAttribute("regardingobjectid").getValue();
var entityType = regarding[0].entityType;
var regardingId = regarding[0].id.slice(1, -1);
// Check if regarding is an active case.
if (entityType == "incident") {
getCaseState(regardingId, function(state) {
var isActive = state === "Active";
if (isActive) {
// TODO
}
});
}
}
The function passed in the code above is anonymous -- you should separate it out further and name it.
The short answer is No. The inner function gets defined and assigned to req.onreadystatechange. displayButton() never calls the inner function. So it won't be executed in this context and therefore won't return a value.
Probably the this could be a good dig deeper into functions in JS: https://www.w3schools.com/js/js_function_definition.asp
First of all you have pasted way too much code. This is Stack Overflow, you should provide Minimal, Complete, and Verifiable example
The only way to do that without changing your code too much would be to change your request to synchronous as suggested already and assign result to some variable defined outside of the callback. Something like that:
function displayButton(){
var result = false; //or true, depends what you want as default
//some code
//change it to synchonous request!!
var req = new XMLHttpRequest();
req.open("GET", parent.Xrm.Page.context.getClientUrl() + "/api/data/v8.2/incidents(" + id + ")?$select=statecode", false);
//...
req.onreadystatechange = function () {
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 200) {
//ommiting more code to get to the clue
//...
//...
if (statecode_formatted == "Active") {
result = true;
}
else if (statecode_formatted == "Resolved")
result = false;
else {
result = false;
}
}
}
};
req.send();
return result;
}
I don't want to change your whole code, because you pasted way too much of it, but I'm sure you've got the idea. As suggested in another answer, you should move your calling function to a separate funtion with callback and assign your "result" in this callback. Callback will be run synchronously, so the function will return your "result" with proper value.
Your XMLHttpRequest is asynchronous here. So you can not get Response immediately so execution will not end immediately.
i solved this problem using
setTimeout(function () {
},550);
i think u can try in following way.
here change i did is i kept XMLHttpRequest in separate function i.e, getResult(id) and calling that function from parent function i.e, displayButton().
And insted of returning value from child function, i'm setting that Boolean value to Global variable i.e, var boolValue;
And in parent function before returning a value i'm waiting for 550 mil sec
, so that asynchronous execution gets over.
below is the code snippet.
Parent function
var boolValue;
//function called on click of a button in ms crm.
function displayButton() {
var Obj = parent.Xrm.Page.getAttribute("regardingobjectid");
var ObjValue = Obj.getValue();
//parent.Xrm.Utility.alertDialog(" Value: " + ObjValue);
if (ObjValue == null)
return false;
//else
// parent.Xrm.Utility.alertDialog(" Hi");
var EntityType = ObjValue[0].entityType;
var Guid = ObjValue[0].id;
var id = Guid.slice(1, -1);
//parent.Xrm.Utility.alertDialog(" Guid: " + id);
//Checking if regarding field is selected a case lookup value
if (EntityType == "incident") {
getResult(id);
setTimeout(function () {
if(boolValue == true || boolValue == false)
{
return boolValue;
}
}, 550);
}
else {
return false;
}
}
child function
function getResult(id)
{
var req = new XMLHttpRequest();
req.open("GET", parent.Xrm.Page.context.getClientUrl() + "/api/data/v8.2/incidents(" + id + ")?$select=statecode", true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
req.onreadystatechange = function () {
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 200) {
var result = JSON.parse(this.response);
//checking if selected case is active or resolved.
var statecode = result["statecode"];
var statecode_formatted = result["statecode#OData.Community.Display.V1.FormattedValue"];
if (statecode_formatted == "Active") {
boolValue = true;
}
else if (statecode_formatted == "Resolved")
boolValue = false;
else {
boolValue = false;
}
}
else {
parent.Xrm.Utility.alertDialog("Zero");
}
}
};
req.send();
}
This way solved my problem.

return value from onreadystatechange with callback [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 6 years ago.
I'm trying to return a value from an onreadystatechange AJAX call... I found this page : stackoverflow link. I though I had it working but realised that it made no difference to add or remove the fn function. The following code works :
username_is_available();
function username_is_available() {
var username = document.getElementById('username').value;
get_data('username', username, function(returned_value) {
if (returned_value == 'true') {
document.getElementById('username_err').innerHTML = 'Taken';
} else {
document.getElementById('username_err').innerHTML = 'Available';
};
});
}
function get_data(data_type, data, fn) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
fn(xmlhttp.responseText);
}
};
xmlhttp.open("GET", "availability.php?" + data_type + "=" + data, true);
xmlhttp.send();
}
It all works fine but that's not my goal, I would like a function username_is_available() that returns true if the username is indeed available.
Instead, here I an action happens (innerHTML is changed). And if I try and do a return in the anonymous function I get the same result as if I had returned it from directly inside the onreadystatechange : var unasigned
Unfortunately, since the process to determine if a username is taken is asynchronous, there is no way to simply return a value of true or false from the function call. What you can do is set up something similar to what you have now (callbacks) using language features specifically designed for this exact purpose.
A Promise is one of these features.
Usage would look something roughly like this:
function username_is_available(username) {
return new Promise(resolve => {
get_data("username", username, resolve);
});
}
function get_data(data_type, data, fn) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
fn(xmlhttp.responseText == "true");
}
};
xmlhttp.open("GET", "availability.php?" + data_type + "=" + data, true);
xmlhttp.send();
}
// Usage:
username_is_available("Ivan").then(available => {
let text = available ? "Available" : "Taken";
document.getElementById("username_err").innerHTML = text;
});
This relies on availablity.php returning true and false as text, which is converted to a Boolean before resolve is called.
In the future, when ES7+ async and await directives are available, using the promise will be as simple as this (note the await keyword):
let available = await username_is_available("Ivan");
let text = available ? "Available" : "Taken";
document.getElementById("username_err").innerHTML = text;
Edit: If you can't use ES6 or promises, it's back to good ol' callbacks!
function username_is_available(username, callback) {
get_data("username", username, callback);
}
function get_data(data_type, data, fn) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
fn(xmlhttp.responseText == "true");
}
};
xmlhttp.open("GET", "availability.php?" + data_type + "=" + data, true);
xmlhttp.send();
}
// Usage:
username_is_available("Ivan", function(available) {
var text = available ? "Available" : "Taken";
document.getElementById("username_err").innerHTML = text;
});
I've been playing around with observers as an alternative to promises. I set up a an example in plnkr to show how it can work. I think in your case it would look like this:
function Producer() {
this.listeners = [];
}
Producer.prototype.add = function(listener) {
this.listeners.push(listener);
};
Producer.prototype.remove = function(listener) {
var index = this.listeners.indexOf(listener);
this.listeners.splice(index, 1);
};
Producer.prototype.notify = function(message) {
this.listeners.forEach(function(listener) {
listener.update(message);
});
};
var notifier = new Producer;
function get_data(data_type, data) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
notifier.notify(xmlhttp.responseText);
}
};
xmlhttp.open("GET", "availability.php?" + data_type + "=" + data, true);
xmlhttp.send();
}
var username_is_available = {
update: function(returned_value) {
var username = document.getElementById('username').value;
if (returned_value == 'true') {
document.getElementById('username_err').innerHTML = 'Taken';
} else {
document.getElementById('username_err').innerHTML = 'Available';
};
}
}
notifier.add(username_is_available);
get_data("username", username);
Note the Producer code is reusable, you would make a new instance for other ajax/observable requests.

Function call after AJAX request does not work in javascript

I have a function where the alert is working:
function RequestNext() {
var xhr = getXMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
MyCard = GetCard(xhr.responseText);
**alert(MyCard.GetNo());**
return MyCard;
}
};
xhr.open("GET", "../../HttpRequest_Next.php" , true);
xhr.send(null);
}
Then I have this other function where the first one gets called and the same alert does not work:
function Start(){
var MyCard = RequestNext();
alert("Patience.js");
**alert(MyCard.GetNo());**
alert("test2");
//alert(Card.GetKind());
//WriteCard(Card);
alert("test3");
}
For information, those functions are in 2 files.
This is where a callback is a great idea. Basically you pass a function as an argument, so that you can then run that function when the ajax (which is asyncronous) completes. The syntax here may be slightly off, but you could do something like:
function RequestNext(callback) {
var xhr = getXMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
MyCard = GetCard(xhr.responseText);
**alert(MyCard.GetNo());**
if (typeof callback=='undefined') return MyCard;
else {
return callback.call(MyCard);
}
}
};
xhr.open("GET", "../../HttpRequest_Next.php" , true);
xhr.send(null);
}
function Start(){
var MyCard = RequestNext(function() {
alert("Patience.js");
alert(this.GetNo());
alert("test2");
//alert(this.GetKind());
//WriteCard(this);
alert("test3");
return this;
});
}

Categories