Method in class not being called after XMLHttpRequest is done - javascript

I have the following method to log into my API:
login()
{
var formData = new FormData();
formData.append("admin[email]", "user");
formData.append("admin[password]", "pass");
var request = new XMLHttpRequest();
request.open("POST", "my_link");
request.onreadystatechange = (function() {
if (request.readyState === XMLHttpRequest.DONE && request.status === 201)
{
return function() { this.loadMembers(); }
}
});
request.send(formData);
}
Then I have my method that I'm trying to call AFTER my post async request is completely done:
loadMembers()
{
....
}
Now for some reason, this.loadMembers() is never being called. If I put a testing console.log() right above it (not within the return callback) it calls just fine - so I know that the requestState and request status is correct.
Is there a better way to do this? Or what am I doing wrong?
Thanks

It is because you are returning a function instead of just calling loadMembers.
So instead of:
request.onreadystatechange = (function() {
if (request.readyState === XMLHttpRequest.DONE && request.status === 201)
{
return function() { this.loadMembers(); }
}
});
You likely want:
var that = this;
request.onreadystatechange = (function() {
if (request.readyState === XMLHttpRequest.DONE && request.status === 201)
{
that.loadMembers();
}
});

Remember the "this" in login() is not the same this as the one inside the (function() { if....}) so you need to save this var that = this in the way that rasmeister shows.

Related

Display Javascript Ajax webservice request

I was trying to display an ajax request from a web service in my HTML.
I can display the result in the console but can not retrieve the result and then display it in my HTML.
I want to display the result of the request in "weather-result" div by clicking the "ask-weather" button.
Her is my code.
Thank everyone.
const askWeather = function(result){
var request = new XMLHttpRequest();
request.onreadystatechange = function subFunction() {
if (this.readyState == XMLHttpRequest.DONE && this.status == 200) {
result = JSON.parse(this.responseText);
return result.current_condition.condition;
}
};
request.open("GET", "https://www.prevision-meteo.ch/services/json/paris");
request.send();
}
const ask = document.getElementById('ask-weather');
ask.addEventListener('click', function(){
const weatherResult = document.getElementById('weather-result');
weatherResult.innerHTML = askWeather();
});
<html>
<head>
<link rel="stylesheet" type="text/css" href="base.css">
</head>
<body>
<div><button id="ask-weather">Quelle est la météo sur Paris ?</button></div>
<div id="weather-result"></div>
<script type="text/javascript" src="index.js"></script>
</body>
</html>
This seems like a asynchronous/callback problem here. When the button is clicked, it sends a request somewhere and will return right away (with undefined in case of the code above - this can be checked by saving it in a variable and console.log it).
When askWeather() is called, it could return something itself. The return in request.onreadystatechange cannot return for askWeather as it's happening multiple times and later - after askWeather is done and the request is being sent.
If you pass a function a variable and set it to something new in its body, it will not be changed for the caller. That means doing result = ... does not really help, if you wanted to pass a variabel and get it set by the inner function.
A different approach is necessary to handle this. Here are a few alternatives:
To keep it as most similar to the code you had, you can set the innerHTML in the onreadystatechange function:
const askWeather = function() {
var request = new XMLHttpRequest();
request.onreadystatechange = function subFunction() {
if (this.readyState == XMLHttpRequest.DONE && this.status == 200) {
const result = JSON.parse(this.responseText);
// set it here directly
const weatherResult = document.getElementById('weather-result');
weatherResult.innerHTML = result.current_condition.condition;
}
};
request.open("GET", "https://www.prevision-meteo.ch/services/json/paris");
request.send();
}
const ask = document.getElementById('ask-weather');
ask.addEventListener('click', function() {
askWeather();
});
Make it more general and let askWeather use a callback (call a function when it's "done"):
const askWeather = function(callback) {
var request = new XMLHttpRequest();
request.onreadystatechange = function subFunction() {
if (this.readyState == XMLHttpRequest.DONE && this.status == 200) {
const result = JSON.parse(this.responseText);
// send the result to the passed "callback" function
callback(result.current_condition.condition);
}
};
request.open("GET", "https://www.prevision-meteo.ch/services/json/paris");
request.send();
}
const ask = document.getElementById('ask-weather');
ask.addEventListener('click', function() {
askWeather(function (result) { // this whole function is the "callback" parameter
const weatherResult = document.getElementById('weather-result');
weatherResult.innerHTML = result;
});
});
(a) Let askWeather return a promise and use it in the caller
const askWeather = () => new Promise((resolve, reject) => {
var request = new XMLHttpRequest();
request.onreadystatechange = function subFunction() {
if (this.readyState == XMLHttpRequest.DONE && this.status == 200) {
const result = JSON.parse(this.responseText);
// send the result to the passed "callback" function
resolve(result.current_condition.condition);
}
// not sure about the error path here, but something like this:
if (this.readyState == XMLHttpRequest.DONE && this.status != 200) {
reject(new Error("There was an error with the XMLHttpRequest!"));
}
};
request.open("GET", "https://www.prevision-meteo.ch/services/json/paris");
request.send();
});
const ask = document.getElementById('ask-weather');
ask.addEventListener('click', function() {
askWeather()
.catch((err) => weatherResult.innerHTML = err.message) // to handle possible errors, maybe?
.then((result) => { // like the callback solution, but as promise!
const weatherResult = document.getElementById('weather-result');
weatherResult.innerHTML = result;
});
});
});
(b) Additionally to the Promise solution, in newer browsers there is already async and await syntax:
ask.addEventListener('click', async function() {
try {
const result = await askWeather(); // this "pauses" until the Promise return of `askWeather` resolves (or throws an error if it doesn't)
const weatherResult = document.getElementById('weather-result');
weatherResult.innerHTML = result;
} catch (e) {
// error could be handled here
}
});
Instead of XMLHttpRequest, use the fetch API, which usually should be available if Promises are available in the browsers you support. The solution is in the comments of the original question. With most modern browsers, this should work:
ask.addEventListener('click', async () => {
const response = await fetch("https://www.prevision-meteo.ch/services/json/paris");
const result = await response.json();
const weatherResult = document.getElementById('ask-weather');
weatherResult.innerHTML = result.current_condition.condition;
});
If you don't have to support IE, I would use the fetch alternative.
I hope the other alternatives make it clear, asynchronous patterns can be resolved in JavaScript.
function getJSON(path) {
return new Promise(function(resolve, reject) {
var xhttp = new XMLHttpRequest();
xhttp.open('GET', path, true);
xhttp.onreadystatechange = function() {
if (this.readyState === 4) {
if ((this.status >= 200 && this.status < 300) || this.status === 304) {
var response = JSON.parse(this.responseText);
resolve(response);
} else {
var error = this.statusText;
reject('Http/App Error: ' + error);
}
}
}
xhttp.onerror = processError;
xhttp.onabort = processError;
xhttp.send();
xhttp = null;
function processError(err) {
reject('Network Error: ' + err.target.status);
}
});
}
const ask = document.getElementById('ask-weather')
const weather = document.getElementById('weather-result')
const endpoint = 'https://www.prevision-meteo.ch/services/json/paris'
ask.addEventListener('click', function() {
getJSON(endpoint).then((success) => {
const response = success.current_condition.condition
weather.innerHTML = response
}, (error) => {
console.log(error)
})
})
This is a simple example using Promise. See the fiddle working.

Connecting JSON API for parsing

I am looking to parse the following page and extract every instance of a name. http://api.openparliament.ca/politicians/.
I have been following this guide for reference: https://www.taniarascia.com/how-to-connect-to-an-api-with-javascript/ However when it runs, there is nothing returned. What am I doing wrong?
var request = new XMLHttpRequest();
request.open('GET', 'api.openparliament.ca/politicians/?format=json', true);
request.onload = function () {
// Begin accessing JSON data here
var data = JSON.parse(this.response);
if (request.status >= 200 && request.status < 400) {
data.forEach(politicians => {
console.log(politicians.name);
});
} else {
console.log('error');
}
}
request.send();
Welcome Sean to StackOverflow.
Well, first of all you have some issues in your code.
Add the http:// in the URL in this line: request.open('GET', 'http://api.openparliament.ca/politicians/?format=json', true);.
You need to wait for XMLHttpRequest.readyState is DONE. In your code you can check the readyState property in this way:
if (request.readyState === 4) {
// Code goes here...
}
Check if the XMLHttpRequest has returned a 200 status code. You can do in this way:
if (request.status === 200) {
// Code goes here...
}
Then with the previous code you can do:
var data = JSON.parse(this.response);
Where data is an object that it has two properties: objects and pagination where objects is an array of objects and pagination is an object.
Then you can do:
data.objects.forEach(politician => {
console.log(politician.name);
});
Here is the complete demo:
(function() {
var request = new XMLHttpRequest();
request.open('GET', 'http://api.openparliament.ca/politicians/?format=json', true);
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
var data = JSON.parse(this.response);
data.objects.forEach(politician => {
console.log(politician.name);
});
} else {
console.log('error');
}
}
}
request.send();
}());
Hope this helps.

How to get JSON data and store into variable.

I have a one variable file like this.
var geography = [
{ id:"Country", header:"", width:150},
{ id:"Capital", header:"Capital", width:150},
{ id:"Falg", header:"Falg", width:150},
{ id:"Language", header:"Language", width:150},
{id:"Population", header:"Population", width:150},
],
Now I wanted to load this data from the json. I placed this data into JSON file and Using this code.
getGeography function(){
var geography;
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", "data.json",true);
}
Now from here how to store into a variable and return that.
It should be read when the ready state of xmlhttp is 4 and response status is 200. You should parse the response with JSON.parse(). However you can't return the value from the function. Because XMLHTTPRequest is asynchronous by default.
function getGeography() {
var geography;
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", "data.json",true);
xmlhttp.onreadystatechange = function () {
if(xmlhttp.readyState === 4 && xmlhttp.status === 200) {
geography = JSON.parse(xmlhttp.responseText;)
}
}
}
Instead of returning geography you have to programmatically read the value of geography when the AJAX request is complete. Something like this (read this):
Instead of writing code like this:
function anotherFunc() {
var geography = getGeography();
thenDoSomething(geography);
}
Write like this:
function anotherFunc() {
getGeography();
}
function getGeography() {
var geography;
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", "data.json",true);
xmlhttp.onreadystatechange = function () {
if(xmlhttp.readyState === 4 && xmlhttp.status === 200) {
geography = JSON.parse(xmlhttp.responseText);
thenDoSomething(geography);
}
}
}
It's like handing over the control of execution of rest of the code to getGeography() function, instead of expecting a return value from the function and then using that value. The getGeography() function resumes execution of rest of the code with the value received from AJAX response, when the AJAX call completes.
I'm not a fan of jQuery but in this case, you would probably benefit from this.
$.get( "ajax/test.html", function( data ) {
// data is your result
console.log(data);
console.log(JSON.parse(data));
});
https://api.jquery.com/jquery.get/
Here is how to use XMLHttRequest() :
<script>
const req = new XMLHttpRequest();
var geography = [];
req.onreadystatechange = function(event) {
// XMLHttpRequest.DONE === 4
if (this.readyState === XMLHttpRequest.DONE) {
if (this.status === 200) {
geography = JSON.parse(this.responseText);
console.log(geography);
alert("Great Success : check console !");
} else {
alert("Something has gone really Bad !");
}
}
};
req.open('GET', 'data.json', true);
req.send(null);
Be careful to use correct JSON :
[
{"id":"Country","header":"","width":150},
{ "id":"Capital","header":"Capital", "width":150},
{ "id":"Falg","header":"Falg","width":150},
{ "id":"Language","header":"Language", "width":150},
{ "id":"Population", "header":"Population", "width":150}
]

Add EXTRA parameter to XMLHttpRequest Handler

Following is the way I know for adding parameters to request handlers for XMLHttpRequest.
var request = new XMLHttpRequest();
function ABC() {
if (request) {
request.open('GET', url, true);
request.onreadystatechange = function() { handler(param1, param2); };
request.send();
}
}
function handler(param1, param2) {
if (request.readyState == 4) {
if (request.status == 200) {
//do something on success
} else {
alert("Invocation Errors Occured");
}
}
}
That is fine and good. But, kindly look at the following code.
var request = new XMLHttpRequest();
function ABC() {
if (request) {
request.open('GET', url, true);
request.onreadystatechange = handler;
request.send();
}
}
function handler(evtXHR) {
if (request.readyState == 4) {
if (request.status == 200) {
//do something on success
} else {
alert("Invocation Errors Occured");
}
}
}
Here, i'm calling the handler without any parameter, but i'm getting an object of type XMLHttpRequestProgressEvent in the evtXHR parameter of the code.
Now I have two questions.
How am I getting evtXHR parameter when I make a parameter-less call?
How to add a parameter along with evtXHR so that I still get the XMLHttpRequestProgressEvent object?
So, I want something like this:
function handler(evtXHR, myParam) {
if (request.readyState == 4) {
if (request.status == 200) {
//do something on success
} else {
alert("Invocation Errors Occured");
}
}
}
You aren't making a call. the XHR object is. The handler function is being called by the XHR object in response to receiving the event.
Don't try to pass it as an argument. Use a closure instead.
Such:
var myData = 1234;
request.onreadystatechange = handler;
function handler(event) {
alert(myData); // Use myData from the wider scope instead of trying to pass it as an argument
}

fallback on XHR fail, outside of the XHR function

I am doing an XMLHttpRequest and I would like to fallback on doing something else (reading a local file) if it fails, but I want to do it outside of the XHR function (getDBfileXHR) itself.
I am using Jquery too.
How is that possible, given the fact that it doesn't seem to work with .done() and .fail(), maybe with a deferred variable or something else ?
getDBfileXHR( encode_utf8("http://john:hispasswd#mysite.com/DBfile.jsonp") );
//here I want to do something else if getDBfileXHR fails like this :
fallbackToLocalDBfile();
function getDBfileXHR(url) {
var request = new XMLHttpRequest();
request.open("GET", url, true); //3rd parameter is sync/async
request.onreadystatechange = function() { //Call a function when the state changes.
if (request.readyState == 4) {
if (request.status == 200 || request.status == 0) {
console.log('we get a response from XHR');
var jsonText = request.responseText.replace("callback(", "");
jsonText = jsonText.replace(");", "");
storeJsonInProdata(JSON.parse(jsonText));
dbReadyDeferred.resolve();
} else {
console.log('error : request.status = '+request.status);
}
}
}
console.log("Sending XMLHttpRequest...");
request.send();
}
function fallbackToLocalDBfile(){
$.get('proDB.jsonp').done(function(data){
console.log(data);
//storeJsonInProdata(data);
//dbReadyDeferred.resolve();
});
}
Mmm something like this maybe :
var d=$.Deferred()
function getDBfileXHR(url) {
....
if (request.readyState == 4) {
...
d.resolve(_MyData);
} else {
console.log('error : request.status = '+request.status);
d.reject(_myError);
}
}
}
console.log("Sending XMLHttpRequest...");
request.send();
}
d.done(function (a){...}).fail(function (b){});

Categories