Best way to validate a sign-up form without refreshing the page

I want my code to check if the username/email are available or already in the database without the page being refreshed. If it’s available the data will get added to the database and I will get redirected to a different page.

I already tried two ways one with jQuery which works but I can’t redirect myself to a different page, and the second one with Ajax which doesn’t seem to work at all, I’ll add the code I used on the 2 ways at the end, please feel free to point out anything that can be improved in term of security.

HTML code:

<form name="myForm" id="myForm" action="includes/user-signup.php"method="post">
 <input id="signup-username" type="text" required pattern="[a-zA-Z].{2,}" name="username" placeholder="Username">
 <input id="signup-pwd" type="password" name="pwd" required pattern="(?=.*\d)(?=.*[a-z]).{9,}" title="Password must contain at least 9 characters, including numbers." placeholder="Password">
 <input id="signup-email" type="text" name="email" required pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,3}$" placeholder="Email">
 <input id="signup-submit" type="submit" name="submit" value="SING UP" class="btn-login">
</form>

PHP code:

if (isset($_POST['submit'])){
include_once 'dbh.inc.php';

  $username = mysqli_real_escape_string($conn, $_POST['username']);
  $pwd = mysqli_real_escape_string($conn, $_POST['pwd']);
  $email = mysqli_real_escape_string($conn, $_POST['email']);

if(empty($username) || empty($pwd) || empty($email)){
  echo "Fill in all Fields!";
}else {
   if(!filter_var($email, FILTER_VALIDATE_EMAIL)){
     echo "Email is invalid!";
   }else{
      $stmt = $conn->prepare("SELECT * FROM users WHERE username=?");
      $stmt->bind_param("s", $uid);
      $uid = $username;
      $stmt->execute();
      $result = $stmt->get_result();
      $usernamecheck = mysqli_num_rows($result);
      $rowNum = $result->num_rows;
    if($rowNum > 0){
        echo "Username is taken!";
    }else{
        $stmt = $conn->prepare("SELECT * FROM users WHERE email=?");
        $stmt->bind_param("s", $uemail);
        $uemail = $email;
        $stmt->execute();
        $result = $stmt->get_result();
        $usernamecheck = mysqli_num_rows($result); 
        $rowNum = $result->num_rows;
    if($rowNum > 0){
        echo "Email is taken";
    }else{
        $hashedPwd = password_hash($pwd, PASSWORD_DEFAULT);
        $stmt = $conn->prepare("INSERT INTO users (username, pwd, email) VALUES (?, ?, ?)");
    $stmt->bind_param("sss",$uid, $password, $theemail);
    $uid = $username;
    $password = $hashedPwd;
    $theemail= $email;
    $stmt->execute();
    $result = $stmt->get_result();
    header("location: ../user-login.php");
        }
        }
     }
    }
 }else{
header("location: ../user-signup.php");
exit();
}

Ajax I used which didn’t work, nothing gets submited to database:

$(document).ready(function() {
	$("#myForm").submit(function(event){
		 event.preventDefault();
		 var username = $("#signup-username").val();
		 var pwd = $("#signup-pwd").val();
		 var email = $("#signup-email").val();
		 $(".signup-submit").text('Processing...');
		 $.ajax({
		 type: "POST",
	     url: "includes/user-signup.php",
		 data: "username="+ username + "&pwd="+ pwd + "&email="+ email,
		success: function(res) {
    if (res == "joined") {
      console.log(res);
      $(".signup-submit").text('Joined');
      $("input").val('');
      window.location.replace("user-login.php");
    } else if(res == "Email Match") {
      alert(data);
      $(".signup-email").val('');
    } else if (res == "failed to join") {
      console.log(res);
      $(".signup-submit").text('Sign Up');
    } else {
      // Stay Null
       }
  }
});
});
});

jQuery I tried, which submit data correctly to the database but instead of refreshing the page at the end it loads the new page next to the old one in .form-message and I don’t know how to work around it:

 $(document).ready(function() {
 $("#myForm").submit(function(event){
     event.preventDefault();
  var username = $("#signup-username").val();
  var pwd = $("#signup-pwd").val();
  var pwd = $("#signup-email").val();
 $(".form-message").load("includes/user-signup.inc.php",{
  username: username,
  pwd: pwd,
  email:email
  });
  });
  });

I added this to my PHP code for the jQuery to work:

$("#signup-username, #signup-pwd, #signup-email").removeClass("input-error");
var errorEmpty = "<?php echo $errorEmpty; ?>";
var errorValid = "<?php echo $errorValid; ?>";
if (errorEmpty == true $$ errorValid == true){
$("#signup-username, #signup-pwd, #signup-email").addClass("input-error");
 if (errorEmpty == false && errorValid == false){
$("#signup-username, #signup-pwd, #signup-email").val("");
}

Hey there!

Combining front-end and back-end isn’t always that easy.
Lets tackle each issue individually.

Redirecting to another page

You are asking for the response to equal 'joined'. But where in the php file do you echo 'joined'?
You should work with HTTP status codes. This way you avoid working with comparing strings and combining front-end and back-end is a lot easier. 201 stands for created. See here for more info: http://racksburg.com/choosing-an-http-status-code/

So in the last else, we can write the response status for successfully creating a user:

}else{
    http_response_code(201);
    echo 'works!';
}

note: You can only set the header before responding. So set the code before using echo.

In Javascript, you can compare status codes with this:

success: function(res, status, xhr) {
    if(xhr.status == 201) {
    // success 

POST request with form data

You are asking for isset($_POST['submit']). You do not set that with your ajax request.
You can use postman to test if your endpoints work correctly. I would suggest to drop the check entirely and only check for the three inputs.

Security

Prepared statements are already a good way to avoid any SQL injection.
However, you need to do the same validation on the back-end. Anyone can send any data to any endpoint using ajax themselves in the browser or using HTTP testing clients such as postman. So make sure you also validate your user

Going further

You shouldn’t stop there. You could already suggest that the user does exist while the user is filling in the inputs. There are many ways to enhance user submission.

By the way, if you put the script after the form, you do not need any document.ready event.

If you use a function for your php validation, you do not need to deeply nest if/else/ifelse.

<?php

$username = $_POST['username'];
$pwd = $_POST['pwd'];
$email = $_POST['email'];

function checkRegistration($username, $pwd, $email) {
    if(empty($username) || empty($pwd) || empty($email)){
        return "Fill in all Fields!";
    }
    if(!filter_var($email, FILTER_VALIDATE_EMAIL)){
        return "Email is invalid!";
    }
    // get user
    if($rowNum > 0){
        return "Username is taken!";
    }
    // check email
    if($rowNum > 0){
        return "Email is taken";
    }
    http_response_code(201);
    return 'works!';
}

Also, you do not need to manually concatenate all inputs for your form data. There is a jquery function that already does that for you:

$.ajax({
     type: "POST",
     url: "submit.php",
     data: $(this).serialize(),

Notice, that not even jQuerys $(this).serialize() doesn’t take post submit into account. Which is why you should only check for inputs, not for the form to be submitted.

Best,
Martin

2 Likes

The bottom line is, if you have barely any control about the information architecture, you need to figure out what you can do to asynchronously fetch data. This is a huge step in any project I have worked on.
I decided to write about this problem more in-depth with live examples.
Most of the time you have no control and rely on other back-end devs, or you can implement it yourself. Only if you are aware of the methods to retrieve the data you need, you can make this a front-end problem, otherwise it is a back-end problem :slight_smile:

Best,
Martin

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.