6

I'm having a problem with FormData, it was working a couple days ago but now it doesn't work, it submits all the inputs except the submit button. Here's my login form.

<form action="" method="post" name="login_user">
    <label for="username">Username/e-mail <span class="error" id="user-error"></span></label>
    <input type="text" name="username" id="username" required>
    <br>
    <label for="passwd">Password <span class="error" id="passwd-error"></span></label>
    <input type="password" name="passwd" id="passwd" required>
    <br>
    <p><input type="checkbox" checked name="remember"> Remember me <span class="forgot"><a href="<?php echo SITE_URL; ?>/reset">Forgotten password</a></span> </p>
    <input type="submit" id="login" value="Login" name="login">
    <p>Don't have an account? <a href="<?php echo SITE_URL; ?>/register/">Sign up</a></p>
</form>

JS for login, uses MVC.

//ajax login
var login = document.forms.namedItem('login_user');
if (login) {
    login.addEventListener('submit', function (e) {
        if (validatePassword('passwd', 'passwd-error')) {
            var data = new FormData(login);
            var userlogin = new XMLHttpRequest();
            var btn = document.getElementById('login');
            btn.value = 'Login, Please wait...';

            userlogin.open('POST', url + 'login/login_user_ajax', true);
            userlogin.onload = function (event) {
                if (userlogin.status == 200) {
                    var result = JSON.parse(userlogin.responseText);
                    if (result.results == 'success.') {
                        alert('logged in'); //change this later
                    } else {
                        document.getElementById('cred-error').innerHTML = result.results;
                    }
                    btn.value = 'Login';
                }
            };
            userlogin.send(data);
        }
        e.preventDefault();
    }, false);

The login method in my controller, the button is not detected.

public function login_user_ajax() {
    $this->login_user(1);
}

private function login_user($ajax  = '')
{
    if (filter_has_var(INPUT_POST, 'login')) {
        $new_user = $this->model('User');
        $new_user->setDb(Controller::getDb());
        $new_user->set_site(SITE_URL);
        $user = trim(filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING));
        $passwd = trim(filter_input(INPUT_POST, 'passwd', FILTER_SANITIZE_STRING));
        if ($new_user->login($user, $passwd)) {
            if ($ajax) {
                $site_data['results'] = 'success.';
                echo json_encode($site_data);
                return true;
            }else {
                $this->redirect(SITE_URL . '/edit');
            }
        } else {
            if (!$ajax){
            return 'The username or password is incorrect.';
            }else {
                $site_data['results'] = 'The username or password is incorrect.';
                echo json_encode($site_data);
                return false;
            }
        }
    }else {
        print_r($_POST);
    }
}

I get this when I print_r $_POST, with no button.

enter image description here

Cœur
  • 37,241
  • 25
  • 195
  • 267
Junius L
  • 15,881
  • 6
  • 52
  • 96
  • That's how it's supposed to work, the submit button isn't submitted? – adeneo Oct 23 '16 at 11:05
  • @adeneo but it was working. – Junius L Oct 23 '16 at 11:06
  • 1
    @adeneo: With a normal form submission, the submit button's name and value are sent with the form if that button was used to cause the submission. So for instance, if you have a form with four different submit buttons, all with the name `btn`, and each with a different value, if you use a button to submit the form, the data will have `btn=the-value-of-the-clicked-button` in it. – T.J. Crowder Oct 23 '16 at 11:07
  • @T.J.Crowder - good point, I should probably have noted that when using `formData`, it's not submitted – adeneo Oct 23 '16 at 11:17

3 Answers3

2

Because you're not actually using the default submit (instead you're doing ajax), you need to add the clicked button yourself. One easy way to do this is to add a hidden input to your form with the name you want the button to have, and then have all the buttons in the form use this click handler:

function clickHandler() {
    this.form.theHiddenInput.value = this.value;
}

That way, if a button was used to submit the form, the button's handler sets the value of the hidden input prior to the submit.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
0

I had a similar problem with my SwitchToggle component that I built with a <button> element. Even if the element had a name it wasn't included in my FormData when submitting the form.

I ended up adding a visually hidden <input type="checkbox"> in the markup with the same name and value property as the SwitchToggle component. This input does nothing (it is not clickable and not visual) except showing up in FormData. This example is in React but the approach is applicable to all frameworks or vanilla JavaScript.

const SwitchToggle = (props) => {
  const { id, name, className } = props;
  const [enabled, setIsEnabled] = React.useState(props.enabled);

  return (
    <>
      <input
        style={{opacity: 0.05}}
        name={name}
        checked={enabled}
        type={"checkbox"}
      />
      <button
        className={`switch-toggle ${enabled ? 'on' : 'off'}`}
        onClick={(e) => {
          e.preventDefault();
          setIsEnabled(!enabled);
        }}
      />
    </>
  );
};

Full example on Codepen.

Since my component is On/Off I used a checkbox input but you might as well use a text input with a string value.

Calsal
  • 1,375
  • 14
  • 25
0

Just stumbled upon this and came up with a very elegant solution for my situation:

// ...
const form = document.querySelector("#form");
form.addEventListener("submit", event => {
    const formData = new FormData(form);
    if (event.submitter && event.submitter.getAttribute("name")) {
        formData.append(form.submitter.getAttribute("name"), form.submitter.getAttribute("value"));
    }
    // do your fetch ...
});
Philipp Wrann
  • 1,751
  • 3
  • 19
  • 29