I have defined one root object that I want to use as "namespace" for the rest of my classes. Inside this root object i have two classes - TOOLS and PRESENTATION. In PRESENTATION class i need to call one of public methods from TOOLS. As I can tell after playing with console.log in every step of execution of this code problem is that return xhr.responseText don't pass anything back to tools.getData(configPath) and I'm ending up with undefined in console.log(pres.config).
CODE:
// Create Namespace
var AppSpace = AppSpace || {};
// Class and Constructor
AppSpace.Tools = function() {
//public methodts
this.test = function(arg) {
return arg
}
this.getData = function(path) {
var xhr = new XMLHttpRequest();
xhr.open('GET', path, false);
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) return;
if (xhr.status !== 0 && xhr.status !== 200) {
if (xhr.status === 400) {
console.log("Could not locate " + path);
} else {
console.error("app.getData " + path + " HTTP error: " + xhr.status);
}
return;
}
return xhr.responseText;
};
xhr.send();
}
}
// Class and Constructor
AppSpace.Presentation = function(initName, initfPath){
//private properties
var configPath = initfPath || 'config.json';
var configData = null;
var name = initName || 'Unnamed Presentation';
//private methods
var getConfigContent = function() {
return tools.getData(configPath);
}
var getConfigData = function() {
return JSON.parse(getConfigContent);
}
//public methodts
//public properties
this.name = null;
this.config = null;
this.debug = null;
//logic
this.name = name;
this.config = getConfigContent();
}
//execution
var tools = new AppSpace.Tools();
var pres = new AppSpace.Presentation('Some Name');
pres.debug = tools.test('value passed')
console.log(pres.debug);
console.log(pres.config);
console.log(pres.name);
Output in browsers console is:
value passed js-oop.dev:99
**undefined js-oop.dev:100**
Some Name js-oop.dev:101
Can anyone give little advice on this? TIA.
I mean that if you want that your ajax control return some datas directly from your function you have to use a synchronous method. Without it, datas will be sent from the onreadystatechange event.
Here is a sample how to create a synchronous call for ajax
// Create Namespace
var AppSpace = AppSpace || {};
// Class and Constructor
AppSpace.Tools = function() {
//public methodts
this.test = function(arg) {
return arg
}
this.getData = function(path) {
var xhr_object= new XMLHttpRequest();
// Start synchronous ajax
xhr_object.open("GET", path, false);
xhr_object.send(null);
// Return data
return xhr_object.responseText;
}
}
// Class and Constructor
AppSpace.Presentation = function(initName, initfPath){
//private properties
var configPath = initfPath || 'config.json';
var configData = null;
var name = initName || 'Unnamed Presentation';
//private methods
var getConfigContent = function() {
return tools.getData(configPath);
}
var getConfigData = function() {
return JSON.parse(getConfigContent);
}
//public methodts
//public properties
this.name = null;
this.config = null;
this.debug = null;
//logic
this.name = name;
this.config = getConfigContent();
}
//execution
var tools = new AppSpace.Tools();
var pres = new AppSpace.Presentation('Some Name');
pres.debug = tools.test('value passed')
console.log(pres.debug);
console.log(pres.config);
console.log(pres.name);
First, in this code:
this.getData = function(path) {
var xhr = new XMLHttpRequest();
xhr.open('GET', path, false);
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) return;
if (xhr.status !== 0 && xhr.status !== 200) {
if (xhr.status === 400) {
console.log("Could not locate " + path);
} else {
console.error("app.getData " + path + " HTTP error: " + xhr.status);
}
return;
}
return xhr.responseText;
};
xhr.send();
}
return xhr.responseText; will not work. It is inside handler function and value will be returned from xhr.onreadystatechange, but not from getData, so you may do something like this:
this.getData = function(path) {
var res;
var xhr = new XMLHttpRequest();
xhr.open('GET', path, false);
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) return;
if (xhr.status !== 0 && xhr.status !== 200) {
if (xhr.status === 400) {
console.log("Could not locate " + path);
} else {
console.error("app.getData " + path + " HTTP error: " + xhr.status);
}
return;
}
res = xhr.responseText;
};
xhr.send();
return res;
}
Also, this should be like next (you are trying to parse an function, not what it returns)
var getConfigData = function() {
return JSON.parse(getConfigContent());
}
If you'd like to keep it asynchrous you can do something like this. I'd either remove the dest and prop parameters, or the callback, depending on your needs.
this.getData = function(path, dest, prop, callback) {
callback = callback || null;
var xhr = new XMLHttpRequest();
xhr.open('GET', path, false);
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) return;
if (xhr.status !== 0 && xhr.status !== 200) {
/* ... */
}
dest[prop] = xhr.responseText;
if (Callback) callback(xhr.responseText);
};
xhr.send();
}
//private methods
var getConfigContent = function() {
return tools.getData(configPath, this, 'config', );
}
Related
I'm creating an application using Module Pattern in JS. I've create two modules and I have this code:
var dataController = (function () {
var request = new XMLHttpRequest();
var getFilmes = function () {
request.onreadystatechange = function() {
if(request.readyState === 4) {
if(request.status === 200) {
var obj = JSON.parse(request.responseText);
return obj;
} else {
console.log('An error occurred during your request: ' + request.status + ' ' + request.statusText);
}
}
}
request.open('Get', 'http://localhost:8080/api/filmes/5b8947446f506266bc522f38');
request.send();
}
return {
filmes: function (){
return getFilmes();
}
};
})();
var controller = (function (dataCtrl) {
var preencheFilmes = function(){
var obj = dataCtrl.filmes();
console.log(obj);
}
return {
init: function(){
console.log("APP START");
preencheFilmes();
}
};
})(dataController, UIController);
controller.init();
The problem is that I can't get the response from AJAX when I'm calling preencheFIlmes in the "init". But I can get the result in the dataController.
Someone can help me? I'm learning how to work with this pattern.
Thank you so much.
Your function getFilmes() is asynchronous and doesn't return anything. A simple solution is to add a callback parameter like this:
var getFilmes = function(callback) {
request.onreadystatechange = function() {
if(request.readyState === 4) {
if(request.status === 200) {
var obj = JSON.parse(request.responseText);
callback(obj); // <-- calls the callback function
} else {
...
}
}
}
...
}
Then you can pass an anonymous callback function when you want to get the results:
var preencheFilmes = function(){
dataCtrl.filmes(function(obj) {
console.log(obj);
});
}
Another option is to use the async/await feature, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Response is received without any problems, but properties which must be changed after response by received data are undefined. Probably they getting values from the temp variables way too fast. How to fix this?
By the way, is there an option to avoid those temp variables and use properties of the object inside if statement? It would be so much better. Tried different ways, but it didn't work =(
var tickerBittrex = function(tickerName)
{
this.tickerName = tickerName;
this.requestURL = "https://bittrex.com/api/v1.1/public/getticker?market=" + tickerName;
/* Properties */
this.success = false;
this.message = "";
this.Last = 0;
this.sendRequest = function()
{ /* temp variables */
var success;
var message;
var Last;
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function(success, message, Last)
{
if (this.readyState == 4 && this.status == 200)
{
console.log(xhttp.responseText); // test
var response = JSON.parse(xhttp.responseText); // parsed response
console.log(response); // test
success = response.success;
message = response.message;
Last = response.result.Last;
console.log(Last); // test
console.log("xhttp.status: " + xhttp.status + " readyStat:" + xhttp.readyStat); // test
}
};
xhttp.open("GET", this.requestURL, true);
xhttp.send();
this.success = success;
this.message = message;
this.Last = Last;
}
}
var bittrexBTC = new tickerBittrex("usdt-btc");
bittrexBTC.sendRequest();
this should work:
var tickerBittrex = function(tickerName) {
this.tickerName = tickerName;
this.requestURL = "https://bittrex.com/api/v1.1/public/getticker?market=" + tickerName;
/* Properties */
this.success = false;
this.message = "";
this.Last = 0;
var self = this;
this.sendRequest = function() { /* temp variables */
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function(success, message, Last) {
var success;
var message;
var Last;
if (this.readyState == 4 && this.status == 200) {
console.log(xhttp.responseText); // test
var response = JSON.parse(xhttp.responseText); // parsed response
console.log(response); // test
success = response.success;
message = response.message;
Last = response.result.Last;
console.log(Last, success, message); // test
self.success = success;
self.message = message;
self.Last = Last;
console.log(self);
console.log("xhttp.status: " + xhttp.status + " readyState:" + xhttp.readyState); // test
}
};
xhttp.open("GET", this.requestURL, true);
xhttp.send();
}
}
var bittrexBTC = new tickerBittrex("usdt-btc");
bittrexBTC.sendRequest();
Explanation:
first,put code below in the onreadystatechange callback because before the callback executed the value of success,message,Last will be undefined.
this.success = success;
this.message = message;
this.Last = Last;
second:change this to self which defined in the tickerBittrex function,so can get the right this object.
var self = this;
so comes:
self.success = success;
self.message = message;
self.Last = Last;
I am doing a XMLHttpRequest which is in it's own function. I want to return the price1 var to be used by other functions. But it seems to be lost in the scope. Code below -
var dataControl = (function () {
var getPrice = function(sym){
var xhttp = new XMLHttpRequest();
var response, price1;
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
response = JSON.parse(this.responseText);
price1 = response['Data'];
price1 = price1[0].open;
console.log(price1);
return price1;
}
};
xhttp.open("GET", "https://min-api.cryptocompare.com/data/histominute?fsym=XRP&tsym=USD&limit=60&aggregate=3&e=CCCAGG", true);
xhttp.send();
}
return {
getItem: function () {
getPrice();
}
}
})();
You can use the Callback Paradigm to get the data that you need
var dataControl = (function () {
var getPrice = function (cb) {
cb = (typeof cb === 'function' && cb) || function(){};
var xhttp = new XMLHttpRequest();
var response, price1;
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
response = JSON.parse(this.responseText);
price1 = response['Data'];
price1 = price1[0].open;
console.log(price1);
// Assuming error first callback paradign
cb(null, price1);
}
};
xhttp.open("GET", "https://min-api.cryptocompare.com/data/histominute?fsym=XRP&tsym=USD&limit=60&aggregate=3&e=CCCAGG", true);
xhttp.send();
}
return {
getItem: function (cb) {
getPrice(cb);
}
}
})();
dataControl.getItem(function(err, data){
if(err){
// Handle error
return;
}
console.log(data); // Value of price1
});
I am using AJAX GET to get a local JSON file and it does that, but once i try to return it says undefined.
ScoreHandler = function () {
this.getScores = function() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
var data = JSON.parse(this.responseText);
//This logs object
console.log(data);
return data;
}
};
xmlhttp.open("GET", "JSON/Scores.json", true);
xmlhttp.send();
};
};
HighScores = function (scoreHandler) {
var scoreHandler = scoreHandler;
var scores = this.scoreHandler.getScores();
//This logs undefined
console.log(scores);
}
Just implement a callback for response, something like this
ScoreHandler = function () {
this.getScores = function(callback) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
var data = JSON.parse(this.responseText);
//This logs object
console.log(data);
if(typeof callback === 'function')
callback(data);
//return data;
}
};
xmlhttp.open("GET", "JSON/Scores.json", true);
xmlhttp.send();
};
};
HighScores = function (scoreHandler) {
var scoreHandler = scoreHandler; //why this line use it directly
var scores = this.scoreHandler.getScores(function(data){
console.log("response", data); //you can see the data here
});
//This logs undefined
console.log(scores);
}
I'm using csrfMagic as an automatic CSRF protection for my project. when I include the script in my project XMLHTTPRequest events don't work.
for example 'onprogress' event is not triggered.
my code:
var xhr = new XMLHttpRequest()
xhr.open("POST", "./");
xhr.onreadystatechange = function (e) {
//some code
}
xhr.upload.onprogress = function (e) {
//this event is not triggered
console.log('xhr.upload.onprogress was triggerd');
}
csrfMagic script:
/**
* #file
*
* Rewrites XMLHttpRequest to automatically send CSRF token with it. In theory
* plays nice with other JavaScript libraries, needs testing though.
*/
// Here are the basic overloaded method definitions
// The wrapper must be set BEFORE onreadystatechange is written to, since
// a bug in ActiveXObject prevents us from properly testing for it.
CsrfMagic = function(real) {
// try to make it ourselves, if you didn't pass it
if (!real) try { real = new XMLHttpRequest; } catch (e) {;}
if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) {;}
if (!real) try { real = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {;}
if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP.4.0'); } catch (e) {;}
this.csrf = real;
// properties
var csrfMagic = this;
real.onreadystatechange = function() {
csrfMagic._updateProps();
return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null;
};
csrfMagic._updateProps();
}
CsrfMagic.prototype = {
open: function(method, url, async, username, password) {
if (method == 'POST') this.csrf_isPost = true;
// deal with Opera bug, thanks jQuery
if (username) return this.csrf_open(method, url, async, username, password);
else return this.csrf_open(method, url, async);
},
csrf_open: function(method, url, async, username, password) {
if (username) return this.csrf.open(method, url, async, username, password);
else return this.csrf.open(method, url, async);
},
send: function(data) {
if (!this.csrf_isPost) return this.csrf_send(data);
prepend = csrfMagicName + '=' + csrfMagicToken + '&';
if (this.csrf_purportedLength === undefined) {
this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength + prepend.length);
delete this.csrf_purportedLength;
}
delete this.csrf_isPost;
return this.csrf_send(prepend + data);
},
csrf_send: function(data) {
return this.csrf.send(data);
},
setRequestHeader: function(header, value) {
// We have to auto-set this at the end, since we don't know how long the
// nonce is when added to the data.
if (this.csrf_isPost && header == "Content-length") {
this.csrf_purportedLength = value;
return;
}
return this.csrf_setRequestHeader(header, value);
},
csrf_setRequestHeader: function(header, value) {
return this.csrf.setRequestHeader(header, value);
},
abort: function() {
return this.csrf.abort();
},
getAllResponseHeaders: function() {
return this.csrf.getAllResponseHeaders();
},
getResponseHeader: function(header) {
return this.csrf.getResponseHeader(header);
} // ,
}
// proprietary
CsrfMagic.prototype._updateProps = function() {
this.readyState = this.csrf.readyState;
if (this.readyState == 4) {
this.responseText = this.csrf.responseText;
this.responseXML = this.csrf.responseXML;
this.status = this.csrf.status;
this.statusText = this.csrf.statusText;
}
}
CsrfMagic.process = function(base) {
var prepend = csrfMagicName + '=' + csrfMagicToken;
if (base) return prepend + '&' + base;
return prepend;
}
// callback function for when everything on the page has loaded
CsrfMagic.end = function() {
// This rewrites forms AGAIN, so in case buffering didn't work this
// certainly will.
forms = document.getElementsByTagName('form');
for (var i = 0; i < forms.length; i++) {
form = forms[i];
if (form.method.toUpperCase() !== 'POST') continue;
if (form.elements[csrfMagicName]) continue;
var input = document.createElement('input');
input.setAttribute('name', csrfMagicName);
input.setAttribute('value', csrfMagicToken);
input.setAttribute('type', 'hidden');
form.appendChild(input);
}
}
// Sets things up for Mozilla/Opera/nice browsers
// We very specifically match against Internet Explorer, since they haven't
// implemented prototypes correctly yet.
if (window.XMLHttpRequest && window.XMLHttpRequest.prototype && '\v' != 'v') {
var x = XMLHttpRequest.prototype;
var c = CsrfMagic.prototype;
// Save the original functions
x.csrf_open = x.open;
x.csrf_send = x.send;
x.csrf_setRequestHeader = x.setRequestHeader;
// Notice that CsrfMagic is itself an instantiatable object, but only
// open, send and setRequestHeader are necessary as decorators.
x.open = c.open;
x.send = c.send;
x.setRequestHeader = c.setRequestHeader;
} else {
// The only way we can do this is by modifying a library you have been
// using. We support YUI, script.aculo.us, prototype, MooTools,
// jQuery, Ext and Dojo.
if (window.jQuery) {
// jQuery didn't implement a new XMLHttpRequest function, so we have
// to do this the hard way.
jQuery.csrf_ajax = jQuery.ajax;
jQuery.ajax = function( s ) {
if (s.type && s.type.toUpperCase() == 'POST') {
s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
if ( s.data && s.processData && typeof s.data != "string" ) {
s.data = jQuery.param(s.data);
}
s.data = CsrfMagic.process(s.data);
}
return jQuery.csrf_ajax( s );
}
}
if (window.Prototype) {
// This works for script.aculo.us too
Ajax.csrf_getTransport = Ajax.getTransport;
Ajax.getTransport = function() {
return new CsrfMagic(Ajax.csrf_getTransport());
}
}
if (window.MooTools) {
Browser.csrf_Request = Browser.Request;
Browser.Request = function () {
return new CsrfMagic(Browser.csrf_Request());
}
}
if (window.YAHOO) {
// old YUI API
YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject;
YAHOO.util.Connect.createXhrObject = function (transaction) {
obj = YAHOO.util.Connect.csrf_createXhrObject(transaction);
obj.conn = new CsrfMagic(obj.conn);
return obj;
}
}
if (window.Ext) {
// Ext can use other js libraries as loaders, so it has to come last
// Ext's implementation is pretty identical to Yahoo's, but we duplicate
// it for comprehensiveness's sake.
Ext.lib.Ajax.csrf_createXhrObject = Ext.lib.Ajax.createXhrObject;
Ext.lib.Ajax.createXhrObject = function (transaction) {
obj = Ext.lib.Ajax.csrf_createXhrObject(transaction);
obj.conn = new CsrfMagic(obj.conn);
return obj;
}
}
if (window.dojo) {
// NOTE: this doesn't work with latest dojo
dojo.csrf__xhrObj = dojo._xhrObj;
dojo._xhrObj = function () {
return new CsrfMagic(dojo.csrf__xhrObj());
}
}
}
Your code does not contain a call of send method of XMLHttpRequest. So csrf_magic can't add csrf token to HTTP-request.
Example of a correct upload function:
function upload(file) {
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function(event) {
log(event.loaded + ' / ' + event.total);
}
xhr.onload = xhr.onerror = function() {
if (this.status == 200) {
log("success");
} else {
log("error " + this.status);
}
};
xhr.open("POST", "upload", true);
xhr.send(file);
}
Links:
https://developer.mozilla.org/ru/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest