0

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?

GROVER.
  • 4,071
  • 2
  • 19
  • 66

3 Answers3

3

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.

Community
  • 1
  • 1
adeneo
  • 312,895
  • 29
  • 395
  • 388
  • I'll test this out! One moment :) – GROVER. Feb 14 '17 at 08:08
  • Ok cool! It fixed the recurring alerts for `readystatechange`! However, the returned responseText is: `Undefined index: valueA`. (Check my edited post) – GROVER. Feb 14 '17 at 08:13
  • That's to be expected, you're not sending www-urlencoded data, but an object, stringified as JSON. jQuery converts objects to www-urlencoded for you, native methods don't. – adeneo Feb 14 '17 at 08:15
  • How would I fix this? :) – GROVER. Feb 14 '17 at 08:16
  • You'll need a "serializer" somewhat similar to what jQuery uses. I'll add one – adeneo Feb 14 '17 at 08:17
  • Thanks! Do you have any other tips on improving this `function`? Usability wise? :) – GROVER. Feb 14 '17 at 08:18
  • It seems fine, how much you want to include in your "ajax" function depends on how you're using it, and what you're sending. – adeneo Feb 14 '17 at 08:24
  • Still getting the undefined index error. Is there anything I need to do in PHP? – GROVER. Feb 14 '17 at 08:30
  • Looks like it should be `$_POST["valueA"]`, you can `var_dump($_POST)` to see what's in there – adeneo Feb 14 '17 at 08:36
  • I just used `valueA` as an example :) but yeah, I'll try the dump as well – GROVER. Feb 14 '17 at 08:38
  • When I `var_dump($_POST)`, it spits out `array(0){ }` – GROVER. Feb 14 '17 at 08:40
  • As long as you can echo something back, the ajax call works, you just have to figure out what's inside `$_POST`, and how to access it. Try it all together -> https://jsfiddle.net/780Ls7q1/ testing that, shows the correct data being sent, so it should work ? – adeneo Feb 14 '17 at 08:44
  • I checked the console for the fiddle and it says the request failed :/ – GROVER. Feb 14 '17 at 08:56
  • Yes, the fiddle fails, because there's no PHP or server responding, but you can still see the data being sent, and that the request works, I mostly posted it to show everything together. – adeneo Feb 14 '17 at 09:21
  • Oh! :) what's the use of `prefix` btw? It doesn't seem to be used – GROVER. Feb 14 '17 at 09:22
1

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);
}
GROVER.
  • 4,071
  • 2
  • 19
  • 66
SPlatten
  • 5,334
  • 11
  • 57
  • 128
1

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

Joel
  • 348
  • 2
  • 9