1
\$\begingroup\$

Today I just completed a personal POST function that emulates jQuery's using $.ajax, but also implementing a possible redirect. I just finished up and it looks like it is functional, but since this is something I want to keep long term and integrate in many projects, I want to make sure it is as functional as possible.

function postR(url, params, redir = false, callback = null) {
    if(redir){
        var form = document.createElement('form');
        form.method = 'post';
        form.action = url;
        for (var key in params) {
            var input = document.createElement('input');
            input.type = 'hidden';
            input.name = key;
            input.value = params[key];
            form.appendChild(input);
        }
        document.body.appendChild(form);
        form.submit();
    }else{
        if(callback && typeof(callback) === "function"){
            $.ajax({
                type: "POST",
                url: url,
                data: params,
                success: function(data){
                    callback(data)
                }
            });
        }else{
            $.ajax({
                type: "POST",
                url: url,
                data: params
            });
        }
    }
}

The purpose for building this was because I wanted to use jQuery's post function to do some JS work with a form before it was submitted, but also wanted to redirect alongside the POST request, like a natural form.

\$\endgroup\$
  • \$\begingroup\$ I have rolled back your last edit. Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers. \$\endgroup\$ – Heslacher Jan 7 '19 at 17:33
1
\$\begingroup\$

Your XMLHttpRequest example looks good, as well as the redirection part. However, to avoid confusion and improve readability, I would divide it into two different functions.

1) Overwriting $.post is not an easy task as it may seem, but as a basic example you can use something similar to the following, which supports error and success callbacks:

/**
 * Send a POST request
 * @param {String} url
 * @param {Object} params
 * @param {Function} success
 * @param {Function} error
 * @returns {XMLHttpRequest}
 */
function post(url, params, success, error) {
    let xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                success && success(xhr.responseText);
            } else {
                error && error(xhr.responseText);
            }
        }
    };

    let formData = new FormData();
    for (let p in params) {
        formData.append(p, params[p]);
    }

    xhr.open('POST', url);
    xhr.send(formData);
    return xhr;
}

2) As I mentioned, the redirection part from your function is good. However, you have an additional option to simulate the redirection. The idea is to load HTML via a XHR request and use it to replace the current document.

/**
 * Simulate a redirection
 * @param {String} url
 * @param {Object} params
 * @returns {XMLHttpRequest}
 */
function postR(url, params) {
    let success = error = function (data) {
        history.pushState('', '', url);
        document.open().write(data);
        document.close();
    };
    return post(url, params, success, error);
}
\$\endgroup\$
  • \$\begingroup\$ Got it. I do have some questions though, like why are we returning xhr at the end? Usually I call the function on its own, and not setting it equal to anything. Would this impact it at all, or is it just additional functionality? \$\endgroup\$ – NebulaCoding Jan 7 '19 at 21:11
  • \$\begingroup\$ @NebulaCoding You can safely remove it, since it does not affect anything. On the other hand, it does not interfere with anything, but sometimes it may seem useful. For example, you can get all HTTP headers in this way: var xhr=post('?',{},function(){console.log(xhr.getAllResponseHeaders())}); (or modify the function and pass the xhr object to the callbacks). By the way, the jQuery always returns the jqXHR object as result of $.post, $.get, or $.ajax. \$\endgroup\$ – Victor Jan 8 '19 at 2:59
  • \$\begingroup\$ Ah, see I didn't know jQuery did that. I figured it would be fine, but just wanted to make sure. \$\endgroup\$ – NebulaCoding Jan 8 '19 at 3:05
3
\$\begingroup\$

I would:

  • Use a FormData instead of creating a form element.
  • Give it a name better than "postR", like... simply "post"?
  • Add an error handler to the $.ajax call
  • Unify both $.ajax calls

Pseudo code (Not tested):

function post(url, params, redir = false, callback = null) {
  var formData = new FormData();

  for (var key in params) {
    formData.append(key, value);
  }

  $.ajax({
    type: "POST",
    url: url,
    data: formData,
    success: function(data) {
      typeof(callback) === "function" && callback(data);
      redir && window.location.reload();
    }
  });
}
\$\endgroup\$
  • \$\begingroup\$ I have it as postR to signify post Redirect, but that's just for me, not worried about keeping it lol \$\endgroup\$ – NebulaCoding Jan 7 '19 at 17:52
  • 1
    \$\begingroup\$ Looking at the redirect, does window.location.reload bring you to the destination of the POST request? To me it looks like it will just reload that page after sending the data, where I am trying to redirect WITH the POST data. \$\endgroup\$ – NebulaCoding Jan 7 '19 at 18:00
  • \$\begingroup\$ @guest271314 Yes - Fixed \$\endgroup\$ – Theone Lucas Jan 7 '19 at 21:05
  • \$\begingroup\$ @NebulaCoding Yes, it will reload, but only after the POST was sent successfully. It have the same effects, try it. \$\endgroup\$ – Theone Lucas Jan 7 '19 at 21:07
  • \$\begingroup\$ Understood. If for whatever reason I couldn't achieve my desired functionality with my other setup, I would use this. \$\endgroup\$ – NebulaCoding Jan 7 '19 at 21:13
0
\$\begingroup\$

I have incorporated some of the things suggested, but I still don't understand exactly how I should do error reporting. I got rid of the second If..Else statement, and also swapped to not use .ajax but XmlHttpRequest(). I did swap to using FormData as well.

function postR(url, params, redir = false, callback = null) {
    if(redir){
        var form = document.createElement('form');
        form.method = 'post';
        form.action = url;
        for (var key in params) {
            var input = document.createElement('input');
            input.type = 'hidden';
            input.name = key;
            input.value = params[key];
            form.appendChild(input);
        }

        document.body.appendChild(form);
        form.submit();
    }else{
        let xhttp, formData;
        xhttp = new XMLHttpRequest();
        formData = new FormData();

        xhttp.onreadystatechange = function(){
            if(this.readyState == 4){
                if(this.status == 200){
                    if(callback){
                        callback(this.responseText);
                    }else{
                        console.log(`POST succeeded`);
                    }
                }else{
                    console.log(`Some error has occurred, error status: ${this.status}, text: ${this.statusText}`);
                }
            }
        };
        xhttp.open('POST', url, true);
        for(let key in params){
            if(params.hasOwnProperty(key)) {
                formData.append(key, params[key]);
            }
        }
        xhttp.send(formData);
    }
}
\$\endgroup\$
0
\$\begingroup\$

It's really not a good idea to "simulate" a page change by messing with the history like is shown in the chosen answer. It might work for one case but as a general function it is a bad idea. I would however employ a helper function for generating DOM elements, and I would use fetch instead of xhr.

function post(url, params, redir = false, callback = null) {
  if (redir) {
    const mkele = (tag, props, parent) => {
      var ele = document.createElement(tag);
      for(prop in props) ele.setAttribute(prop, props[prop]);
      parent.appendChild(ele);
      return ele;
    };
    const form = mkele('form', {method:"POST", action:url}, document.body);
    for (let k in params) mkele('input', {name:k, value:params[k], type:'hidden'}, form);
    form.submit();
  } else {
    const fd = new FormData();
    for (let param in params) fd.append(param, params[param])
    fetch(url, {method:"POST", body:fd}).then(callback);
  }
}

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.