A little bit of a jQuery / Ajax problem

I been picking my brain for a good part of today and I’m kind of stumped in the this registration form that I doing in jQuery, Ajax and PHP. I have it working in php and about 90 percent in the JQuery and Ajax portion as well. I get it where it validates for null, checks to see if the username is already taken and validates the password. Problem is when I go to submit it.

Here’s the important HTML portion:

<section class="container registerPage"> <form id="register_form" class="span6" action="register.php" method="post"> <fieldset class="registerFieldset"> <legend class="registerLegend" data-status="fail" ><?php echo isset($message) ? $message : 'Registration Form'; ?></legend> <label for="username" class="registerLabelStyle">Username: </label> <input id="username" type="text" name="username" value="" tabindex="1"> <label class="passwordClass registerLabelStyle" for="password">Password: </label> <input id="password" type="password" name="password" tabindex="2"> <br><br> <button class="enableSubmit" type="button" value="">Please Fill in Form!</button> <input class="submitBtn" type="submit" name="submit" value="Submit" tabindex="3"> </fieldset> </form> </section> <section class="container"> <h2 class="result">Status OK</h2> </section>

Here’s the JQuery(JavaScript) and Ajax portion:
[php] function saveData(myData) {
$.ajax({
type: “post”,
url: “process.php”, // PHP file that checks agains the database table:
data: myData,
success: function (info) {

    error_code = parseInt(info);
    switch (error_code) {
      case 100:
        message = "All input fields required, please re-enter!";
        break;
      case 200:
        message = "Username is unavailable, please re-enter!";
        break;
      case 300:
        message = "'Password must be at least 8 characters, must contain at least 1 lowercase letter, 1 uppercase letter and 1 digit.'";
        break;
      case 400:
        message = "Status OK";
        break;

      default:
        message = "Error, please re-enter!";
    }
    $('.result').text(message);
    console.log(error_code, message);
  }
}); // End of Ajax Function:

}

/* Validate username functin so that data is correct /
function checkUsername() {
$username = $(’#username’).val(); // Grab the username once user has entered it in form:
var params = {username: $username}; // Set parameters
var myData = jQuery.param(params); // Set parameters to corret AJAX format
/
Check the database table against user’s username */
saveData(myData);

} // End of checkUsername Function:

/* Validate Password */
function checkPassword() {

$password = $('#password').val(); // Grab the password once user has entered it in form:
var params = {password: $password}; // Set parameters
var myData = jQuery.param(params); // Set parameters to corret AJAX format	
saveData(myData);

} // End of checkPassword Function:

function insertData() {
var $username = $(’#username’).val(); // Grab username from form:
var $password = $(’#password’).val(); // Grab password from form:
var $submit = ‘submit’;
var params = {username: $username, password: $password, submit: $submit}; // Set parameters
var myData = jQuery.param(params); // Set parameters to corret AJAX format
/* Simple Ajax request to save username, password */
$.ajax({
type: “post”,
url: “process.php”, // PHP file that checks agains the database table:
data: myData,
success: function (info) {
$(’.result’).text(“Data Successfully Entered!”);
$(’#username’).val(’’); // Clear the input username input field:
$(’#password’).val(’’); // Clear the input password input filed:
displayTimer(6);
}
}); // End of Ajax Function:

} // End of insertData Function:

/* Check to see if username is availability (Validate) */
$("#username").on(“focusout”, function () {
checkUsername();
}); // End of Focus Out of Username input Function:

/* Validate Password */
$(’#password’).on(“focusout”, function () {
checkPassword();
}); // End of Focus Out of Password input Function:

/* Submit button is click */
$submit.on(“click”, function (e) {
e.preventDefault(); // Prevent the submit button from firing:

}); // End of Submit Button Function:[/php]

The problem is the username and password input fields checks it on focusout, meaning that when the user goes to the next field it validates it via Ajax. Now I can get it to submit, but I don’t want some clever person doing a bypass by having the fields validate then quickly changing it so it doesn’t but inserting into the database. I hope you get what I trying to say. Worse comes to worse I can just have the submit but work normally. That way server-side validate will take place no matter what, but I would like to get it to work properly using 100% JQuery (JavaScript).

I solved it, in case anyone is wondering I had to fix a little bit of the jQuery:
[php] function insertData() {
var $username = $(’#username’).val(); // Grab username from form:
var $password = $(’#password’).val(); // Grab password from form:
var $submit = ‘submit’;
var params = {username: $username, password: $password, submit: $submit}; // Set parameters
var myData = jQuery.param(params); // Set parameters to corret AJAX format
/* Simple Ajax request to save username, password */
$.ajax({
type: “post”,
url: “process.php”, // PHP file that checks agains the database table:
data: myData,
success: function (info) {
console.log(‘status’,info);
if ( info === ‘success’ ) {
$(’.result’).text(“Data Successfully Entered!”);
$(’#username’).val(’’); // Clear the input username input field:
$(’#password’).val(’’); // Clear the input password input filed:
displayTimer(6);
} else {
$(’.result’).text(‘Error: Something is wrong please try again!’);
$(’#username’).val(’’);
$(’#password’).val(’’);
}
}
}); // End of Ajax Function:

} // End of insertData Function:[/php]

And fix the php itself:
[php]/* Check username to see if it is empty or is already used. */
if (isset($username) && !isset($submit)) {
$status = check_null($username);
if ($status === ‘empty’) {
echo 100;
} else {
$status = check_for_duplicates($username, $pdo);
if ($status === ‘fail’) {
echo 200;
} else {
echo 400;
}
}
}

/* Check password to see if it is empty or meets the right criteria. */
if (isset($password) && !isset($submit)) {
$status = check_null($password);
if ($status === ‘empty’) {
echo 100;
} elseif ($status === ‘success’) {
$status = check_password($password);
if ($status === ‘success’) {
echo 400;
} else {
echo 300;
}
}
} // End of If $password statement:

/* If user’s profile data is correct and submit button is clicked */
if (isset($submit) && $submit === ‘submit’) {
if (check_null($username) !== ‘empty’ && check_for_duplicates($username, $pdo) === ‘success’ ) {
if (check_null($password) !== ‘empty’ && check_for_duplicates($password, $pdo)== ‘success’ ) {
$status = insert_data($username, $password, $pdo);
echo $status;
}
}
}[/php]

I have a few nit-picky bugs in it still, mostly the logical kind - However it works! ;D
Sometimes it pays to post your problems on a forum even it you end up answering them yourself. :wink:

Just pitching in my 2 cents on the code. A lot of this is conventions/practises, so I’m not saying my comments are what is correct. But I think it’s worth considering.

You use camelCase everywhere except here:
[php]id=“register_form”[/php]

[hr]

There are quite a few unnecessary long names and classes, like
[php]

<legend class="registerLegend" <label class="passwordClass registerLabelStyle"[/php]

I can’t see any reason to need to append Class or Style, as we know CSS classes are exactly that. In additon we know it’s a fieldset, a label or whatever.

I expect you now have this:

#register_form { ... } .registerFieldset { ... } .registerLegend { ... } .passwordClass { ... } .registerLabelStyle { ... }

I would suggest:

[code]// general styles for all forms
form { … }
fieldset { … }
legend { … }
label { …}

// styles spesific for the register form
form#register { … }
#register fieldset { … }
#register legend { … }
#register label { … }[/code]

In general I don’t think you want to create custom classes if you don’t need them. Ie the class “submitBtn” is totally redundant.

The html with the above changes could look like

[php]

Registration Form
<label for="username">Username:</label>
<input id="username" type="text" name="username" value="" tabindex="1">

<label for="password">Password:</label>
<input id="password" type="password" name="password" tabindex="2">

<button type="button" value="">Please Fill in Form!</button>
<input type="submit" name="submit" value="Submit" tabindex="3">
[/php]

In addition I removed spaces (in labels) and
as you used them for layout, which isn’t considered good convention.

I also removed the label class “password”, as you can select this spesific label in css like this

#register label[for='password'] { color: red; }

I’m not sure what the enableSubmit button is there for, so I just left it as is.

[hr]

Javascript time!

I’m a bit confused about the naming. saveData and insertData both post to the server, but only saveData handle returned errors (in the success callback…?)

I’d refactor this quite a bit, so first of all the server side return errors (not http status 200 ok), and perhaps set the server side to return the error message instead of using some custom error code thing.

I’m also a bit confused about the “checkUsername” and “checkPassword” functions, they don’t seem to check anything.? They just get the data from the form and send them to the server.

[hr]

I did a refactor of your code and ended up with this. Feel free to use it as you wish, either discard it fully or we can discuss it :slight_smile:

HTML
[php]<!doctype html>

Registration Form Username: Password: Please Fill in Form!

Status OK

	<script src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
	<script src="app.js"></script>
</body>
[/php]

CSS

[code]form {
// general styles for all forms
}

fieldset {
// general styles for all fieldsets
}

legend {
// general styles for all legends
}

input.valid {
border-color: green;
}

input.invalid {
border-color: red;
}

input[type=‘submit’] {
// general styles for all submits
}

form#register fieldset {
// spesific styles for register form fieldsets
}

form#register legend {
// spesific styles for register form legends
}

form#register label[for=‘password’] {
// spesific styles for register form password label
}

form#register input[type=‘submit’] {
// spesific styles for register form submit
}[/code]

JS
[php](function() {

/* Form submit handler */
$( "#register" ).submit(function( event ) {
	/* We don't want the browser to handle this submission */
	event.preventDefault();

	/* $(this) is depending on the scope we're in.
	 * Currently $(this) is the form we're "inside" */
	$.post( "ajax.php?action=save", $(this).serialize() )
	  .done(function() {
	  	/* Do this if we get a 200 OK response */
	    $( "#register input[type='text'], #register input[type='password']" ).each(function() {
	    	/* Here $(this) is the current field we have selected */
	    	$(this).val('');
	    });
	  })
	  .always(function(response) {
	  	/* Do this on both success and error */
	    $( "#result" ).text("Performed save: " + response.message).html();
	  });
});

/* Form field change handler */
$("#register input[type='text'], #register input[type='password']").on("focusout", function () {
/* Here we set field equal to $(this) (the current field) this way
 * we can get the the field into the constrained scopes below. */
	var field = $(this);

	$.post( "ajax.php?action=validateUnique", $(this).serialize() )
	  .done(function() {
	  	/* Do this if we get a 200 OK response */
	    field.removeClass('invalid');
	    field.addClass('valid');
	  })
	  .fail(function() {
	  	/* Do this if we get any error */
	    field.removeClass('valid');
	    field.addClass('invalid');
	  })
	  .always(function(response) {
	    $( "#result" ).text("Performed unique check: " + response.message).html();
	  });

});

})()[/php]

PHP
[php]<?php
/* set content type so javascript understands this should be parsed as json

  • and not html, this gives us automatic translation into arrays/object on
  • the client side */
    header(‘Content-type: application/json’);

/* get ajax action */
$action = isset($_GET[‘action’]) ? $_GET[‘action’] : null;

/* validate ajax action /
switch ($action) {
case ‘save’:
case ‘validateUnique’:
/
valid ajax action, run it! /
$action();
break;
default:
/
invalid ajax action, show error! */
error(‘Invalid action’, 400);
}

function save() {
if (!isset($_POST[‘username’]) || !isset($_POST[‘password’])) {
error(‘Missing param(s)’, 400);
}

/* Pseudo code as it depends how you handle DB stuff
 *
 * $sql = 'INSERT INTO user SET username = ?, password = ?';
 * $user = query($sql, [$_POST['username'], $_POST['password']]);
  • if ($user) {
    • error(‘Not saved!’);
  • } else { /
    output('Saved: ’ . $_POST[‘username’] . ’ / ’ . $_POST[‘password’]);
    /
    }
    */
    }

function validateUnique() {
$sql = 'SELECT id FROM user WHERE ';
$params = [];

if (isset($_POST['username'])) {
	$sql .= 'username = ?';
	$params[] = $_POST['username'];

} else if (isset($_POST['password'])) {
	$sql .= 'password = ?';
	$params[] = $_POST['password'];
}
/* Pseudo code as it depends how you handle DB stuff
 *
 * if ($params) {
 *   $users = query($sql, $params);
  • if ($users) {
  • error('Not unique!');
    
  • } else { /
    output(json_encode($_POST) . ’ is unique!’);
    /
    }
  • }
    */
    }

/* Displays error message /
function error($message, $code = 500) {
/
Set response code so javascript understands this is not a 200 OK
see: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes */
http_response_code($code);
output($message, [], ‘error’);
}

/* Output data in correct format */
function output($message = ‘’, $data = [], $status = ‘success’) {
echo json_encode([
‘status’ => $status,
‘data’ => $data,
‘message’ => $message
]);
}[/php]

Thank You Jim, I gave you karma for this one.

First thanks for showing me how to better stylize the form in css, I should have thought about top-down after all it’s Cascading Style Sheets ;D

I have a couple of questions if you don’t mind (They’re probably stupid ones)?

The errors in php that are thrown are they some kind of uniform standard error code(s) (I don’t if I’m wording that correctly), by that I mean is there some kind of governing body that determines them (I’m assuming there is)? An if so where could I find out the more info on the different error codes is so?

Secondly, how would this affect my php code that deals with graceful degradation (JavaScript Disabled)?

Thanks Once Again,
John

There is no such thing as stupid questions :slight_smile:

See this code above:
[php] function error($message, $code = 500) {
/* Set response code so javascript understands this is not a 200 OK

try to run the above, there is no real validation logic added, but it should be pretty easy to iron it out. of course you will not get live field validation on the client side without javascript. but posting the form should work and you can do validation then :slight_smile:

One thing to note is that I try to avoid fetching fields directly from the DOM, like this:
[php]var $username = $(’#username’).val();[/php]

I’ve always liked it better to use the form submit handler where you get the actual form object, and work with that. That way if you change the form content you don’t have to change it all over the client side.

And these http status codes is nothing more than what you see on a daily basis. 404 not found, 500 internal server error, etc. And if you open the console in the browser you’ll see that most requests return 200 OK :slight_smile:

Well actually I should had written it this way

[php]var $username = $(’#username’);[/php]

That way I wouldn’t be changing it all over the client side, all I would have to do is go up the var section at the top and change that if I needed to. I also get a little sloppy with my code when I trying to debug and I get tired. :slight_smile:

Jim, I have a quirky error (I believe it’s in jQuery)

First my php file ajax.php :

[php]<?php
header(‘Content-type: application/json’);
require_once ‘lib/includes/utilities.inc.php’;
/* set content type so javascript understands this should be parsed as json

  • and not html, this gives us automatic translation into arrays/object on
  • the client side */

/* get ajax action */
$action = isset($_GET[‘action’]) ? $_GET[‘action’] : NULL;

/* validate ajax action /
switch ($action) {
case ‘save’:
save($pdo);
break;
case ‘validateUnique’:
/
valid ajax action, run it! /
$action();
break;
default:
/
invalid ajax action, show error! */
error(‘Invalid action’, 400);
break;
}

function save($pdo) {
if (empty($_POST[‘username’]) || empty($_POST[‘password’]) ) {
error(‘Missing param(s)’, 400);
}
else
{
/* Hash the Password with password_hash (PHP 5.5 or greater) /
/
PHP 5.3, 5.4 - https://github.com/ircmaxell/password_compat/blob/master/lib/password.php */
$password_hash = password_hash($_POST[‘password’], PASSWORD_BCRYPT, array(“cost” => 15));

  try {
    /* Set the query with the user's profile using prepared statements */
    $query = 'INSERT INTO users ( username, password ) VALUES ( :username, :password )';
    /* Prepare the statement using PDO prepare method */
    $stmt = $pdo->prepare($query);
    /* Execute statement along with the prepared values */
    $result = $stmt->execute(array(':username' => $_POST['username'], ':password' => $password_hash));
    /* If a result is return back then return "success" back */
    if ($result) {
      output('Saved ' . $_POST['username'] . ' / ' . $_POST['password'] );
    } else {
      error('Not Saved!');
    }
  } catch (PDOException $e) { // Report the Error!	
    echo "DataBase Error: The user could not be added.<br>" . $e->getMessage();
  } catch (Exception $e) {
    echo "General Error: The user could not be added.<br>" . $e->getMessage();
  }  
}

}

/* Displays error message /
function error($message, $code = 500) {
/
Set response code so javascript understands this is not a 200 OK
see: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes */
//http_response_code($code);

output($message, [], ‘error’);
}

/* Output data in correct format */
function output($message = ‘’, $data = [], $status = ‘success’) {
echo json_encode([
‘status’ => $status,
‘data’ => $data,
‘message’ => $message
]);
}[/php]

the strange things is when I comment out the http_response_code($code) it works and when I don’t i get url 400 Bad Request in the JavaScript console.

Here’s the js file:
[php]$(function() {
$("#register").submit(function(event) {
event.preventDefault(); // Prevent submit button from firing:
console.log($(this).serialize());
$.post(“ajax.php?action=save”, $(this).serialize())
.done(function() {
$("#register input[type=‘text’], #register input[type=‘password’]").each(function() {
$(this).val(’’);
});
}).always(function(response) {
console.log(response);
$(".result").text(response.message).html();
});

});

}); // END OF DOCUMENT READY FUNCTION:
[/php]

I’ve done google searches, but to no avail.

Open up the developer console / inspector in your browser, fire off a request (should show up in the console or network tab depending on which browser it is), then highlight it and see if the request data (params), and the response look alright. You should get the error message from the server side in the response so should be possible to figure out where it goes wrong there.

Btw. in your php file you currently echo out two error messages, it’s better to use the error function that outputs json data so JS understands it :slight_smile:

OK, I turn the echo into error functions and this is what I get in the console window in Chrome:

POST http://localhost/registration_demo/ajax.php?action=save 400 (Bad Request) when I don’t comment this

[php]/* Displays error message /
function error($message, $code = 500) {
/
Set response code so javascript understands this is not a 200 OK
see: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes */

http_response_code($code); // Not Commented out:

output($message, [], ‘Error’);
}[/php]

It’s looks like JavaScript is freaking out when username and/or password isn’t set, for it works when I go save it. An it spits out the error message when I comment //http_response_code($code); out. Very Strange.

BTW - Thanks for helping me.

it recognizes it, for when I do a console.log(response); in js, I see this in the console window:

responseText: “{“status”:“Error”,“data”:[],“message”:“Missing param(s)”}”

You don’t need to console.log it, just look at what you get as a response to the request.

[hr]

This is the only code you have throwing that error:

[php] function save() [
if (!isset($_POST[‘username’]) || !isset($_POST[‘password’])) {
error(‘Missing param(s)’, 400);
}
…[/php]

So you probably aren’t sending the form fields properly.

[hr]

If you have the complete code (github?) I can try it and see what’s wrong

Unfortunately I don’t have it on Github, for some strange reason I can never figure out github. I must be too old for this…joking. :slight_smile: it is show up in the Network as 400 Bad Request but in Red…Hmmm. My form looks OK? As a matter of fact I did it the way you said to. Hmmm…I’ll take a break for awhile, maybe a lightbulb will click on.

I got it to work. Well I got kind of of a work around for it, but I’m sure I can optimize it some more.

[php]$(function() {
$("#register").submit(function(event) {
event.preventDefault(); // Prevent submit button from firing:
console.log($(this).serialize());
$.post(“ajax.php?action=save”, $(this).serialize())
.done(function() {
$("#register input[type=‘text’], #register input[type=‘password’]").each(function() {
$(this).val(’’);
});
}).fail(function(xhr, textStatus, errorThrown){
console.log(xhr.status, textStatus, errorThrown);
var myResponse = JSON.parse(xhr.responseText);
console.log(‘myResponse.message’, myResponse.message);

    console.log("xhr.responseText",xhr.responseText, 'textStatus', textStatus, 'errorThrown', errorThrown);
    $(".result").text(errorThrown + " Message " + myResponse.message);
  }).always(function(response) {
    console.log("response", response);
    if (response.status === 'Success') {
      $(".result").text("Status " + response.status + ' Message ' + response.message);
    }
  });

});

}); // END OF DOCUMENT READY FUNCTION:
[/php]

Yeah. The reason we set the content type was so JS would parse it automatically. If you want to then zip up the files (with a db dump) and I can try to have a look :slight_smile:

Thanks for the offer to help, but I believe I got it to work (the code isn’t shown here) correctly and it works pretty darn good. I will be posting it once I get polished and I agree getting the JS to parse is a life saver…well at least a tad bit easier than doing it the other way around. A lot less confusing. I aslo took out the error codes all together and just used the validation if it was good or not. Made it a lot easier. success - passes validation / error - fails validation.

Example of what I’m talking about:

[php] /* Call the function that checks to see if username is unique */
$result = checkForDupicates($pdo, $data[‘username’]);

/* If result is true, then spit out error stating username is unavailable */
if ($result) {
return json_encode([‘type’ => ‘error’, ‘text’ => ‘Username is unavailable, please try again!’]);
}[/php]

Then in my jQuery:
[php] /* Load json data from server and output appropiate message */
if(response.type === ‘error’) {
$password.val(’’);
$verify.val(’’);
$status.css({
‘background-color’ : ‘red’
});
output = response.text; // Display the error message:
$status.animate({width: ‘toggle’}, 350); // Slide the error/success container div down:
displayTimer(12);
} else { // Display to screen if it is successful:
$status.css({
‘background-color’ : ‘green’
});
$(’#register input[type=“text”], #register input[type=“password”]’).each(function() {
$(this).val(’’); // Clear input fields:
});
$username.focus(); // Set Focus on first input tag:
output = response.text; // Grab the success message from php:
$status.slideDown(500);
displayTimer(6); // Call Timer and only display it for so many seconds:
}
$message.text(output); // Set the text in the p tag with the result class:

  [/php]

A good step in the right direction then (y)

I would still recommend you iron out the status code stuff, atm you seem to return a “200 OK” and instead rely on a returned param (type) to check if the request was successful or not. If you get status codes working (as in my example) you return 200 OK only if the request was ok, and something else if not. Jquery understands these codes and executes the correct callback, making the code a bit more structured and better to read.

[php]$.post( “ajax.php?action=validateUnique”, $(this).serialize() )
.done(function() {
/* Do this if we get a 200 OK response /
field.removeClass(‘invalid’);
field.addClass(‘valid’);
})
.fail(function() {
/
Do this if we get any error */
field.removeClass(‘valid’);
field.addClass(‘invalid’);
})
.always(function(response) {
$( “#result” ).text("Performed unique check: " + response.message).html();
});[/php]

I got the status code to work, what drives me crazy is jQuery spitting an error code highlighted in red. Maybe I’m being nit picky about it.

Where? Into the page? If it’s the request in the console then it’s supposed to be red if it failed, it’s easy to pick up on (y). If it shows like it was successful its kinda counter intuitive.

Sponsor our Newsletter | Privacy Policy | Terms of Service