Submit a form with ajax

formhandler is returning 200 OK so it’s not interpreted as an error

Right, OK I see so it returns 200 OK even when the form isn’t submitted. So how would I then address this issue (getting the errors to display in their own spans below the field they’re referring to rather than in that span.success?)
thanks

How about I check if msg is an array and if it isn’t I send the message to the span.success and if it isn’t I create another container, another span?

Nah, then you’re hacking around a perfectly working system. As said, just have the formhandler output 200 OK for all successful requests and anything else for unsuccessful requests, then jquery will know what code block to execute.

Wherever you echo the errors in formhandler.php
[php]http_response_code(400); // you seem to be missing this line
echo json_encode($errors);[/php]

[hr]

That requires more logic. Then you could i.e create an array where you have one key for each field, and errors in an array inside that key. So in JS you could then iterate/loop over each field, and then each error in that field.

thanks for coming back to me.

No that’s there, at the very bottom of formhandler.php:
[php] if ($mail->send()) {
echo json_encode(‘Thank you! The form has been submitted!’);
exit();
}

 // You may want to log this somewhere
 // $mail->ErrorInfo;
 
 $errors[] = 'Could not send email, please try again later';    

}

http_response_code(400);
echo json_encode($errors);[/php]

OK so maybe I misunderstood: when you say “an error” you don’t mean a validation error, but like something other than a 200OK code. At the moment whenever I press the submit button, even if I haven’t filled any of the input fields I still get a 200OK message

OK, that sounds complicated though.Usually when I need to get inputs in a jquery array I do something like this:
var elems = $(’#form_id’).find(’:input’);
but you’re effectively saying something like this in the js file (not sure how to use an array as a key):

var elems = [ { "field":"title",//this is the same as the value of the name attribute "error":"Select a title" } { "field":"firstName", "error":"Not a valid first name" } { "field":"lastName", "error":"Not a valid first name" } { "field":"emailAddress", "error":"Not a valid first name" } { "field":"website", "error":"Not a valid first name" } { "field":"message", "error":"Not a valid first name" } ];

That is strange, having that there should make sure the errors are returned with a 400 BAD REQUEST. If you post the entire formhandler file I can try running it here to see what’s (not) happening.

That won’t give you the select field though

I’m thinking you can return something like this from the formhandler instead of the simple $errors array you have now

[ { "field":"title",//this is the same as the value of the name attribute "errors":[] }, { "field":"firstName", "error": ["Not a valid first name"] }, { "field":"lastName", "error": [] }, { "field":"emailAddress", "error": ["Not a valid email address"] }, { "field":"website", "error": [] }, { "field": "message", "error": [] } ]

Hi thanks for that.

Oh fantastic, thanks for that. I’m sure it’s something stupid I’m doing, because I have to admit that I’m pretty new to these concepts (first time I use php, first time I do server validation and I don’t really work too much with ajax)…I don’t seem to be able to attach the file, so here is the code formhandler.php:
[php]<?php
//require ‘PHPMailer-master/PHPMailerAutoload.php’;
//header(‘Content-Type: application/json’);

$errors = array();

$title = (isset($_POST[‘title’]) && ($_POST[‘title’] != ‘0’)) ? $_POST[‘title’] : null;
$firstName = (isset($_POST[‘firstName’]) && !(empty($_POST[“firstName”]))) ? $_POST[‘firstName’] : null;
$lastName = (isset($_POST[‘lastName’]) && !(empty($_POST[“lastName”]))) ? $_POST[‘lastName’] : null;
$emailAddress = (isset($_POST[‘emailAddress’]) && !(empty($_POST[“emailAddress”]))) ? $_POST[‘emailAddress’] : null;
$website = isset($_POST[‘website’]) ? $_POST[‘website’] : ‘’;
$message = (isset($_POST[‘message’]) && !(empty($_POST[“message”]))) ? $_POST[‘message’] : null;

if ($title === null) {
$errors[] = ‘You must select a title’;
}

if ($firstName === null) {
$errors[] = ‘You must enter a first name’;
}

if ($lastName === null) {
$errors[] = ‘You must enter a last name’;
}

if ($emailAddress === null || filter_var($emailAddress, FILTER_VALIDATE_EMAIL) === false) {
$errors[] = ‘You must enter a valid email address’;
}

if ($website !== ‘’) {
if(strpos($website, ‘://’) === false) {
$website = ‘http://’ . $website;
}

 if (filter_var($website, FILTER_VALIDATE_URL, array('flags' => null)) === false) {
     $errors[] = 'You must enter a valid website address';
 }    

}

if ($message === null) {
$errors[] = ‘You must enter a message’;
}

if (empty($errors)) {
require DIR . ‘/PHPMailer-master/PHPMailerAutoload.php’;

 //SMTP needs accurate times, and the PHP time zone MUST be set
 //This should be done in your php.ini, but this is how to do it if you don't have access to that
 date_default_timezone_set('Etc/UTC');
 
 /* $mail = new PHPMailer;
 $mail->isSMTP();
 $mail->Host = 'smtp.gmail.com';
 $mail->Port = 587;
 $mail->SMTPSecure = 'tls';
 $mail->SMTPAuth = true;
 $mail->Username = "[email protected]";
 $mail->Password = "xxxxxxxxxxx";
 
 $mail->setFrom($emailAddress, $firstName . ' ' . $lastName);
 $mail->addAddress('[email protected]', 'Bassa Basso');
 $mail->Subject = 'New request';
 $mail->Body = $message . "\r\n\r\nWebsite: " . $website; */
 
$mail = new PHPMailer;
$mail->Timeout       =   30; // set the timeout (seconds)

// $mail->isSMTP();
$mail->SMTPAuth = true; // enable SMTP authentication
$mail->SMTPSecure = “tls”; // sets the prefix to the servier
$mail->Host = “smtp.live.com”; // sets hotmail as the SMTP server
$mail->Port = 587; // set the SMTP port for the hotmail server
$mail->Username = "[email protected]"; // hotmail username
$mail->Password = “Asshabatt84”; // hotmail password
$mail->setFrom($emailAddress, $firstName . ’ ’ . $lastName);
$mail->addAddress(‘[email protected]’, ‘Giasone Attinio’);
$mail->Subject = ‘New request’;
//$mail->Body = $message . "\r\n\r\nWebsite: " . $website;
$mail->Body = "Title: " . $title . "\r\n\r\nFirst Name: " .$firstName . "\r\n\r\nLast Name: " . $lastName . "\r\n\r\nEmail address: " .$emailAddress . "\r\n\r\nMessage: " . $message . "\r\n\r\nWebsite: " . $website;

/* $mail = new PHPMailer;
$mail->isSMTP();
$mail->SMTPAuth   = true;                   // enable SMTP authentication
$mail->SMTPSecure = "tls";                  // sets the prefix to the servier
$mail->Host       = "shareduk.hostxnow.com";        // sets hotmil as the SMTP server
$mail->Port       = 465;                    // set the SMTP port for the hotmail server
$mail->Username   = "[email protected]";      // hotmail username
$mail->Password   = "xxxxxxxxx";           // hotmail password
//$mail->SetFrom('[email protected]', 'First Last');
//$mail->AddReplyTo("[email protected]","First Last");
//$mail->Subject    = "PHPMailer Test Subject via smtp (hotmail), basic";
//$mail->AltBody    = "To view the message, please use an HTML compatible email viewer!"; // optional, comment out and test
$mail->Body = "Title: " . $title . "\r\n\r\nFirst Name: " .$firstName . "\r\n\r\nLast Name: " . $lastName . "\r\n\r\nEmail address: " .$emailAddress . "\r\n\r\nMessage: " . $message . "\r\n\r\nWebsite: " . $website;   
	   */
 if ($mail->send()) {
     echo json_encode('Thank you! The form has been submitted!');
     exit();
 }
 
 // You may want to log this somewhere
 // $mail->ErrorInfo;
 
 $errors[] = 'Could not send email, please try again later';    

}

http_response_code(400);
echo json_encode($errors);
[/php]
Here is the form in index.php

[code]








Title*


Please select
Mr
Mrs
Ms
Sir




First name*

			</div>
		</div>
		<div class="control-group">
			<label class="control-label" for="lastName">Last name*</label>
			<div class="controls">
				<input id="lastName" type="text" name="lastName" value="" >									
			</div>
		</div>
		<div class="control-group">
			<label class="control-label" for="emailAddress">Email address*</label>
			<div class="controls">
				<input id="emailAddress" type="text" name="emailAddress" value="" >									
			</div>
		</div>
		<div class="control-group">
			<label class="control-label" for="website">Website</label>
			<div class="controls">
				<input id="website" type="text" name="website" value="">									
			</div>
		</div>
		<div class="control-group">
			<label class="control-label" for="message">Enter your message*</label>
			<div class="controls">
				<textarea id="message" name="message" ></textarea>									
			</div>
		</div>							
		<div class="control-group">
			<div class="controls">
				<div class="button">
					<!-- <a href="#">Submit</a> -->
					<input type="submit" name="submit" value="Submit">
				</div>
			</div>
		</div>
		
	</form>
</div>
[/code] and here is the form_en.js [code]$(function(){ $(document).on("submit", "#contactForm", function(e){ e.preventDefault();
      $form = $(this);
      
      $.post("../formhandler.php", $form.serialize())
      .then(function (msg){
          $form[0].reset();
		  console.log("inside then " + msg);
         $("span.success").html(msg).show().delay(2500);//fadeOut(1000)
     })
	 .done(function(e){
		console.log("inside done " + e);
	 })
     .fail(function (jqXHR) {
         var errors = JSON.parse(jqXHR.responseText);
         $("span.error").html(errors.join('<br>')).show().delay(2500);
		 console.log("inside fail " + e);
     });
 });

});[/code]

It looks ok and works fine here - 400 bad request and fail block is executed if any errors are returned from formhandler.php. Which web server are you using?

Weird. I’m with hostxnow, this is the webserver information taken using a website called http://browserspy.dk/webserver.php
Screenshot here (let me know if you’re unable to load it) : http://s24.postimg.org/bp1ekg6c5/php_error.jpg
Just to clarify, in case we’re not understanding each other: The validation errors (you must select a title, you must enter a first name, etc etc) are appearing when you attempt to submit a form with no info in it, but they are appearing inside a span with a class of success, and I, perhaps erroneously, expected them to appear inside a span with a class of error instead. And when I attempt to submit the empty form, if I look inside the NET console in firebug I get a 200OK after I click submit even if the form is empty, whereas I would have expected to get a different code (other than 200OK) if the form is empty and doesn’t submit. Hope this clarifies it.
For the array error returned by the script discussed before, should I start coding it?

Here is another screenshots, showing you what’s in the net panel in firebug. The first 200OK comes when I attempt to submit the form with no data in it. The second one with all the data filled in correctly:
http://s8.postimg.org/b05v1xrit/form_net.jpg

Yeah I’m not sure why it’s still returning 200 OK. When the form has any errors it returns 400 BAD REQUEST fine in both apache and nginx here… :\

ah. Could it be the webserver then? Or perhaps something wrong I’ve done within index.php? Have you seen that happening on the live site http://antonioborrillo.co.uk/agency_test/test/en/index.php? Maybe it’s worth me trying it on a different server?

Actually, no I don’t think I can put it anywhere else, my webspace doesn’t support php, so I can’t. Maybe - as I need to sort this - would you be able to send me your files and I will compare them to mine and hopefully I will spot some problems in mine?

Is your environment >= PHP 5.4?
http://php.net/manual/en/function.http-response-code.php

If not you might want to change that http_response_code call to one of the alternatives on the page above.

No idea, I can ask the hosting company, but when I tested the server using browser spy it returned php 5.5.30
I have an idea, why don’t we leave this aside for one moment and perhaps try to solve the other two problems:
1)get the error messages to appear below every input field, in their own span
2)code the script so that we can get either the english version or the italian version of the error messages?
You said we wanted to return the data in this fashion:
[php] [
{
“field”:“title”,//this is the same as the value of the name attribute
“errors”:[]
},
{
“field”:“firstName”,
“error”: [“Not a valid first name”]
},
{
“field”:“lastName”,
“error”: []
},
{
“field”:“emailAddress”,
“error”: [“Not a valid email address”]
},
{
“field”:“website”,
“error”: []
},
{
“field”: “message”,
“error”: []
}
][/php]
what changes to the php script and the js script do I have to make to get that?

AH, got some good news. You were right, there had to be an issue with the webserver or something.
I got in touch with the hosting company. Basically the default version of php they’re using is php 5.5(native), whatever that means, so I went in the control panel and changed that to 5.5, and lo and behold, the issue is gone! Now if there is an error in the form I get a 400 error as I should. They said they’ve recently installed CloudLinux, if that’s any relevant.
So back to the other two issues then, the errors under each field and the language. How should we proceed?
thanks

language

translations.php
[php]// split request url by /
$urlBits = explode(’/’, $_SERVER[‘SCRIPT_NAME’]);

// if you change the url (page.com/agency_test/test/en) you will need to change which position the language is in
$lang = isset($urlBits[3]) && $urlBits[3] === ‘it’ ? ‘it’ : ‘en’;

require DIR . ‘/languages/’ . $lang . ‘.php’;[/php]

languages/en.php
[php]$translate = array(
‘INVALID_FIRST_NAME’ => ‘Not a valid first name’,
‘INVALID_EMAIL’ => ‘Not a valid email address’
);[/php]

formhandler.php
[php]// add to top
require DIR . ‘/translations.php’;

// …

if ($firstName === null) {
$errors[] = $translate[‘INVALID_FIRST_NAME’];
}[/php]

[hr]

errors
(requires language fix above)

formhandler.php
[php]// initialize array
$errors = array(
array(‘name’ => ‘title’, ‘errors’ => array()),
array(‘name’ => ‘firstName’, ‘errors’ => array()),
array(‘name’ => ‘lastName’, ‘errors’ => array()),
array(‘name’ => ‘emailAddress’, ‘errors’ => array()),
array(‘name’ => ‘website’, ‘errors’ => array()),
array(‘name’ => ‘message’, ‘errors’ => array())
);

// …

// Note that this setup allows you to have multiple errors for each field.
So instead of just saying “invalid”, you could add “invalid”, “too short”, etc.

if ($title === null) {
$errors[‘title’][‘errors’][] = $translate[‘INVALID_TITLE’];
}

if ($firstName === null) {
$errors[‘firstName’][‘errors’][] = $translate[‘INVALID_FIRST_NAME’];
}[/php]

html
[php]


First name*




[/php]

js
[php] $(function(){
$(document).on(“submit”, “#contactForm”, function(e){
e.preventDefault();

        $form = $(this);
        
        $.post("../formhandler.php", $form.serialize())
        .then(function (msg){
          $form[0].reset();
          $form.find(".error").hide();
          $("span.success").html(msg).show().delay(2500);//fadeOut(1000)
      })
      .fail(function (jqXHR) {
          var fields = JSON.parse(jqXHR.responseText);
          for (var i = 0, l = fields.length; i < l; i++) {
            $field = $("#"+fields[i].name);
            $field.closest("span.error").html(fields[i].errors.join('<br>')).show();
          }
      });
  });

});[/php]

[hr]

Note, haven’t tested the code. quite busy today so just wrote it directly in the editor here. Should give you a good idea of a potential direction.

Big note: you’re mixing pretty advanced stuff (internationalization, ajax, etc) with a very simple setup. You’d be much better off using a proper framework. Atm we’re nesting this very tightly together, so if you change the url, the html layout, the field names, etc the code will break. Normally you’d want to separate the concerns as much as possible so the view won’t mess too much (if any) with the back end.

Thanks ever so much mate, I appreciate you’re busy and you’ve got more important things to do than listening to me, so thanks again for all your help and patience so far, I really appreciate it.
I’m integrating the changes, and I have a few questions:
1)there seems to be an issue in the js, as you’re still referring to “errors” in it, but it’s not been declared anywhere and therefore the code generates an error:

.fail(function (jqXHR) { var fields = JSON.parse(jqXHR.responseText); for (var i = 0, l = fields.length; i < l; i++) { $field = $("#"+fields[i].name); $field.closest("span.error").html(fields[i].errors.join('<br>')).show(); }
2)In terms of folder structure, I hope I got it right, this is what I have now with all the changes made

agency_test/test /en index.php cookies.js /it index.php cookies.js /images burger_menu.png ... /PHPMailer-master ... PHPMailerAutoload.php ... /languages en.php it.php form_en.js this contains the ajax call formhandler.php translations.php
Thanks, and happy Christmas if we don’t speak before then!

Yeah the code creating the fields/errors array in php doesn’t match what the js expect anymore. I just winged it and sadly didn’t wing it very well. I’ll get back to you! It just requires some tuning of how we create the fields/errors array in PHP and how we use that in JS afterwards.

In Norway we actually celebrate on christmas eve (today!), so we’ll have to get back to this later on :slight_smile: Merry christmas!

No worries mate, have a great time!

Sponsor our Newsletter | Privacy Policy | Terms of Service