xmlhttprequest similar to ajax - javascript

I'm attempting to send and receive data from an input to PHP through an XHR request. I have successfully managed to create a connection to PHP without passing data as a parameter within the send method.
However, if I attempt it, I receive the error.
Here is the JavaScript (updated!):
function serialize(obj, prefix) {
var str = [],
p;
for (p in obj) {
if (obj.hasOwnProperty(p)) {
var k = prefix ? prefix + "[" + p + "]" : p,
v = obj[p];
str.push((v !== null && typeof v === "object") ?
serialize(v, k) :
encodeURIComponent(k) + "=" + encodeURIComponent(v));
}
}
return str.join("&");
}
function xhrRequest(data, method, url, callback){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200){
callback(xhr.responseText);
} else {
callback(null);
console.log("XHR Request Failed");
}
}
}
xhr.open(method, url, true);
xhr.send(JSON.stringify(data));
}
// Calling xhrRequest
xhrRequest({ valueA: input.value }, "POST", "post.php", function(data){
alert(data);
});
PHP is just an echo of the value to make sure it was passed (updated!):
if(isset($_POST["value"])){
echo $_POST["value"];
} else {
echo "no value set";
}
I am aware that you can pass parameters like this "valueA=" + input.value within the send method, but it seems really unnecessary (especially if there are multiple values).
So, how would I get this to work? What are some improvements / changes I might be able? to make.
Apologies if it seems very obvious, but I learnt jQuery before vanilla JavaScript, unfortunately. So I am trying to learn the vanilla way, and am used to how jQuery works.
Thanks! :)
EDIT:
Using #adeneo's technique does in fact semi-work! However, using the updated PHP, I alwasy receive "No value set". Why is the value not passing, even when I use "valueA=" + input.value?

The problem is that onreadystatechange fires multiple times during a request, you can't just use an if/else clause as it will fire four times before the status is 4 (states 0-3).
It should look like
function xhrRequest(data, method, url, callback) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
callback(xhr.responseText);
} else {
callback("XHR Request Failed"); // the error
}
}
}
xhr.open(method, url, true);
xhr.send(JSON.stringify(data));
}
// Calling xhrRequest
xhrRequest({ valueA: input.value }, "POST", "post.php", function(data){
alert(data);
});
To properly serialize the object to www-urlencoded data, you could use this one, borrowed from here
function serialize(obj, prefix) {
var str = [],
p;
for (p in obj) {
if (obj.hasOwnProperty(p)) {
var k = prefix ? prefix + "[" + p + "]" : p,
v = obj[p];
str.push((v !== null && typeof v === "object") ?
serialize(v, k) :
encodeURIComponent(k) + "=" + encodeURIComponent(v));
}
}
return str.join("&");
}
var data = serialize({ valueA: input.value });
xhrRequest(data, "POST", "post.php" ...
etc, or even add it to the xhrRequest function for convenience.

Here is a script I wrote a long time ago:
var objXMLHttp;
try{
objXMLHttp = new XMLHttpRequest();
} catch(e){
var xmlHttpVersions = new Array('MSXML2.XMLHTTP.6.0'
,'MSXML2.XMLHTTP.5.0', 'MSXML2.XMLHTTP.4.0'
,'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP'
,'Microsoft.XMLHTTP');
for(var i = 0; i < xmlHttpVersions.length && objXMLHttp == null; i++) {
try{
objXMLHttp = new ActiveXObject( xmlHttpVersions[i] );
} catch(e){
void(0);
}
}
}
if(objXMLHttp != undefined){
objXMLHttp.onreadystatechange = function() {
/*Your response handler here*/
}
}
To send a request to the server using either the 'POST' method or the 'GET' method:
if(strMethod == "POST"){
objXMLHttp.open(strMethod, strAddr, true);
objXMLHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
bjXMLHttp.send(strData);
} else {
objXMLHttp.open(strMethod, strAddr + strData, true);
objXMLHttp.send(null);
}

I would just write a function to convert your data object to a string formatted in the way send expects, namely "name1=value1&name2=value2".
function serialize (data) {
var result = "";
for (var key in data) {
result += (encodeURIComponent(key) + "=" + encodeURIComponent(data[key].toString()) + "&");
}
return result.substring(0, result.length - 1);
}
Then the final code becomes
function xhrRequest (data, method, url, callback) {
var xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.onreadystatechange = function () {
if (xhr.status == 200 && xhr.readyState == 4) {
callback(xhr.responseText);
} else {
callback("XHR Response Failed");
}
}
xhr.send(serialize(data));
}
It might also be good to consider XMLHttpRequest onload and onerror events as described here:
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
The readyState increments as the request is made so because you throw the error whenever readyState != 4 you'll always see your callback receiving the error, even if there is no error.
Check out this reference:
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState

Related

Async xhr and callback

I have a problem with waiting for DOM elems to exist.
First of all, I make an XHR to my backend and get some info from there:
$(document).ready(function() {
var searchParam, searchStr;
// some values to vars
loadTags(15,highlightAndSearchTags(searchParam,searchStr));
});
The functions are here:
function highlightAndSearchTags(searchParam, searchStr) {
if (searchParam == 'tags') {
var selectedTags = searchStr.split(',');
console.log($("#my_favorite_latin_words").children().length); // sometimes returns 0, sometimes returns number of <span> in the div (see loadTags())
for (var i = 0; i < selectedTags.length; i++) {
$("#" + selectedTags[i]).toggleClass("tag-selected");
}
}
}
function loadTags(showedTagsLength, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', apiUrl + "tags/", true);
xhr.withCredentials = true;
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status != 200) {
console.log(xhr.responseText);
}
else {
tagList = JSON.parse(xhr.responseText);
tagList = tagList.results;
for (var i = 0; i < showedTagsLength; i++) {
$("#my_favorite_latin_words").append("<span id=\'" + tagList[i].tag_pk + "\'>" + tagList[i].name + "</span>");
}
}
setTimeout(callback, 1); //found this trick somewhere on stackoverflow
}
};
xhr.send();
}
As you can see there is a callback which is executed after 1ms timeout (I found this trick somewhere on stack a while ago), but then another function does not see the appended elements from time to time.
I have also tried
callback.call()
with no luck so far.
Can anybody advise how to wait for the elements correctly in this case?
loadTags(15,function(searchParam,searchStr){highlightAndSearchTags(searchParam,searchStr)});
As multiple comments already mentioned, you have to wrap it into a function so that it isnt called when you call the loadTags function
You are not passing any callback function. You are immediately invoking the function and passing the returned value of highlightAndSearchTags function which is undefined.
An anonymous function can be created and passed as
loadTags(15,function(){
highlightAndSearchTags(searchParam,searchStr)
});
loadTags(15,highlightAndSearchTags(searchParam,searchStr));
This code will execute your function highlightAndSearchTags immediately and the result value will be sent instead of your callback, if you want to use it as a callback, you need to only pass the function name like:
loadTags(15, highlightAndSearchTags);
If you need to pass your searchParam and searchStr parameters, add them as parameters:
loadTags(15, highlightAndSearchTags, searchParam, searchStr);
When your tags are loaded, you can directly call your callback with the searchParam and searchStr parameters you added to your loadTags function:
function loadTags(showedTagsLength, callback, searchParam, searchStr) {
var xhr = new XMLHttpRequest();
xhr.open('GET', apiUrl + "tags/", true);
xhr.withCredentials = true;
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status != 200) {
console.log(xhr.responseText);
}
else {
tagList = JSON.parse(xhr.responseText);
tagList = tagList.results;
for (var i = 0; i < showedTagsLength; i++) {
$("#my_favorite_latin_words").append("<span id=\'" + tagList[i].tag_pk + "\'>" + tagList[i].name + "</span>");
}
}
callback(searchParam,searchStr);
}
};
xhr.send();
}
Another approach could also be to wrap your callback in an self-executing anonymous function. This will prevent the highlightAndSearchTags to be executed immediately so you can call it later when your tags are loaded:
loadTags(15, function() { highlightAndSearchTags(searchParam, searchStr); });

Find current SharePoint user with Javascript code

Hy
I need to find the current user from my SharePoint.
I have tried many things :
SP.Utilities.PrincipalInfo.get_loginName()
_spPageContextInfo.userId
...
At all times, I have the same result Undefined =(
When using CSOM API to retrieve current user object,wrap your code inside
SP.SOD.executeOrDelayUntilScriptLoaded method to make sure that the specified code is executed after SharePoint JS library (sp.js) is loaded:
SP.SOD.executeOrDelayUntilScriptLoaded(function(){
//your code goes here..
}, 'sp.js');
How to retrieve current user object using CSOM API
function getCurrentUser(success,error)
{
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var currentUser = web.get_currentUser();
ctx.load(currentUser);
ctx.executeQueryAsync(function(){
success(currentUser);
},
error);
}
Usage
SP.SOD.executeOrDelayUntilScriptLoaded(function(){
getCurrentUser(
function(currentUser){
console.log(currentUser.get_loginName());
},
function(sender, args)
{
console.log('Request failed ' + args.get_message() + ':'+ args.get_stackTrace());
});
}, 'sp.js');
The answer is probably here. The only thing i changed is getting LoginName instead of Title:
https://stackoverflow.com/a/21002895/1680288
var userid= _spPageContextInfo.userId;
var requestUri = _spPageContextInfo.webAbsoluteUrl + "/_api/web/getuserbyid(" + userid + ")";
var requestHeaders = { "accept" : "application/json;odata=verbose" };
$.ajax({
url : requestUri,
contentType : "application/json;odata=verbose",
headers : requestHeaders,
success : onSuccess,
error : onError
});
function onSuccess(data, request){
var loginName = data.d.LoginName;
alert(loginName);
}
function onError(error) {
alert("error");
}
If you are getting undefined.. Maybe you are not authenticated or did not include some relevant javascript files in your master page.
Without jquery:
var userid= _spPageContextInfo.userId;
var requestUri = _spPageContextInfo.webAbsoluteUrl + "/_api/web/getuserbyid(" + userid + ")";
function createXMLHttp() {
//If XMLHttpRequest is available then using it
if (typeof XMLHttpRequest !== undefined) {
return new XMLHttpRequest;
//if window.ActiveXObject is available than the user is using IE...so we have to create the newest version XMLHttp object
} else if (window.ActiveXObject) {
var ieXMLHttpVersions = ['MSXML2.XMLHttp.5.0', 'MSXML2.XMLHttp.4.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp', 'Microsoft.XMLHttp'],
xmlHttp;
//In this array we are starting from the first element (newest version) and trying to create it. If there is an
//exception thrown we are handling it (and doing nothing ^^)
for (var i = 0; i < ieXMLHttpVersions.length; i++) {
try {
xmlHttp = new ActiveXObject(ieXMLHttpVersions[i]);
return xmlHttp;
} catch (e) {
}
}
}
}
function getData() {
var xmlHttp = createXMLHttp();
xmlHttp.open('get', requestUri , true);
xmlHttp.setRequestHeader("Content-Type", "application/json;odata=verbose");
xmlHttp.setRequestHeader("accept", "application/json;odata=verbose");
xmlHttp.send(null);
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState === 4) {
if (xmlHttp.status === 200) {
var data = JSON.parse(xmlHttp.responseText);
var loginName = data.d.LoginName;
alert(loginName);
} else {
}
} else {
//still processing
}
};
}
getData();

Tranfer data from local variable to global in javascript

I have some problem with transfer of variable outside the function.
It's seems to be very simple but I have some problem with it.
var myJson;
var url = "https://openbook.etoro.com/api/Markets/Symbol/?name=" + symbol;
var xhr = (window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
xhr.onreadystatechange = XHRhandler;
xhr.open("GET", "proxy.php?url=" + url, true);
xhr.send(null);
function XHRhandler() {
if (xhr.readyState == 4) {
var json;
if (JSON && JSON.parse) {
json = JSON.parse(xhr.responseText);
} else {
eval("var json = " + xhr.responseText);
}
console.log(json);
myJson= json;
xhr = null;
}
}
console.log(myJson);
What I need is to pass the data from local variable json to global myJson;
But when i do console.log(myJson) i get undefined.
What is the problem?
Thank you
Try moving the statement console.log(myJson); inside your if condition or alternately initialize your variable with some value. It seems your statement is getting called before it is getting populated with any value.
The XMLHttpRequest is async so it is not done yet when you try to write the myJson variable to console. Wrap it in a function and call that function after the XMLHttpRequest is completed instead.
var myJson;
var url = "https://openbook.etoro.com/api/Markets/Symbol/?name=" + symbol;
var xhr = (window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
xhr.onreadystatechange = XHRhandler;
xhr.open("GET", "proxy.php?url=" + url, true);
xhr.send(null);
function XHRhandler() {
if (xhr.readyState == 4) {
var json;
if (JSON && JSON.parse) {
json = JSON.parse(xhr.responseText);
} else {
eval("var json = " + xhr.responseText);
}
console.log(json);
myJson= json;
xhr = null;
writeToConsole();
}
}
function writeToConsole() {
console.log(myJson);
}

Ajax Class error with accessing class variable in onreadystatechange function

I am currently writing a JavaScript Ajax class and have encountered an error. In the function processRawData() I can't seem to access the class variable xhr by using this.xhr. I get "cannot read property of readyState of undefined. I have currently fixed this problem by passing in the xhr value when setting the reference to the onreadystatechange function however that seems unneeded as I should be able to access the xhr value without doing so.
function Ajax()
{
this.xhr = this.createXmlHttpRequest();
}
Ajax.prototype.createXmlHttpRequest = function()
{
if (window.XMLHttpRequest) {
try {
return new XMLHttpRequest();
} catch (e) {
throw new Error("Couldn't create XmlHttpRequest : " + e);
}
} else {
try {
return new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {
throw new Error("Couldn't create XmlHttpRequest : " + e);
}
}
}
Ajax.prototype.request = function(type, url, params, dataType, callback)
{
if (this.xhr.readyState === 0 || this.xhr.readyState === 4) {
var isGetWithParams = (type === "GET") ? ((params !== null) ? url + params : url) : url
this.xhr.open(type, isGetWithParams, true);
this.xhr.onreadystatechange = this.processRawData(dataType, callback);
var passInParams = (type === "GET") ? null : ((params !== null) ? params : null);
this.xhr.send(passInParams);
}
}
Ajax.prototype.processRawData = function(dataType, callback)
{
return function()
{
if (this.xhr.readyState === 4 && this.xhr.status === 200) {
switch (dataType) {
case "text":
var data = this.xhr.responseText;
break;
case "xml":
default:
var data = this.xhr.responseXML;
}
callback(data);
}
}
}
Looks like your problem might be because in processRawData() you are returning another function and referencing this.xhr.readyState, but 'this' now references the returning function and not the Ajax class. Try:
Ajax.prototype.processRawData = function(dataType, callback){
var that = this; //'that' references the current Ajax instance
return function()
{
if (that.xhr.readyState === 4 && that.xhr.status === 200) {...

XMLHttpRequest not adding header - "X-Requested-With: XMLHttpRequest"

I have an ajax call where I used jQuery.ajax() to make a request to an mvc action. This all worked fine. However due to some forms having a file control I changed it from using jQuery.ajax() to using the XMLHttpRequest to send it using the HTML5 File API.
Since making this change the MVC action method no longer see's it as an ajax request. Using Fiddler2 I have noticed that it no longer adds the "X-Requested-With: XMLHttpRequest" to the request and I assume this is the problem.
The form I am trying to send does not have a file input in it, only normal textboxes etc, but I was trying to keep the method generic to deal with both. The following is the code I am using to send the ajax request:
// get the edit tender form
var $Form = $Button.closest('form');
var Url = $Form.attr('action');
var AjaxRequestObject = new XMLHttpRequest();
var FormDataToSend = new FormData();
$Form.find(':input').each(function () {
if ($(this).is('input[type="file"]')) {
var files = $(this)[0].files;
if (files.length > 0) {
FormDataToSend.append(this.name, files[0]);
}
} else {
FormDataToSend.append(this.name, $(this).val());
}
});
AjaxRequestObject.open('POST', Url, true);
AjaxRequestObject.onreadystatechange = function () {
if (AjaxRequestObject.readyState == 4) {
// handle response.
if (AjaxRequestObject.status == 200) {
if (!AjaxErrorExists(AjaxRequestObject.responseText, )) {
alert("success");
console.log(AjaxRequestObject.responseText);
}
else {
alert('failure');
}
}
else {
alert('failure');
}
}
};
AjaxRequestObject.send(FormDataToSend);
This code was provided following a problem I had which Darin Dimitrov provided the solution to, so I could send the file inputs by ajax.
Any ideas why this request would not send the header for an ajax call?
X-Requested-With is automatically added by jQuery. You can just as easily add it yourself with AjaxRequestObject.setRequestHeader(). Docs
I was having troubles with detecting if my request was ajax. So, maybe this sample will save someone a minute or two:
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('GET', URL, true); // `true` for async call, `false` for sync.
// The header must be after `.open()`, but before `.send()`
xmlhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xmlhttp.onreadystatechange = function() {
// 4th state is the last:
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { ... }
};
xmlhttp.send();
Tested with Flask.
You can override natively all XMLHttpRequest.open method calls and add in it X-Requested-With header like:
(function () {
// #author https://github.com/stopsopa jfdsa78y453cq5hjfd7s877834h4h3
if (window.XMLHttpRequest.prototype.onOpen) {
return console.log('XMLHttpRequest.onOpen is already defined');
}
function over(method, on, off) {
var old = window.XMLHttpRequest.prototype[method];
if (!old.old) {
var stack = [];
window.XMLHttpRequest.prototype[on] = function (fn) {
if (typeof fn === 'function') {
stack.push(fn);
}
}
window.XMLHttpRequest.prototype[off] = function (fn) {
for (var i = 0, l = stack.length ; i < l ; i += 1 ) {
if (stack[i] === fn) {
stack.splice(i, 1);
break;
}
}
}
window.XMLHttpRequest.prototype[method] = function () {
var args = Array.prototype.slice.call(arguments);
var ret = old.apply(this, args);
for (var i = 0, l = stack.length ; i < l ; i += 1 ) {
stack[i].apply(this, args);
}
return ret;
}
window.XMLHttpRequest.prototype[method].old = old;
}
}
over('open', 'onOpen', 'offOpen')
XMLHttpRequest.prototype.onOpen(function () {
this.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
});
}());

Categories