Link to home
Start Free TrialLog in
Avatar of Bruce Gust
Bruce GustFlag for United States of America

asked on

How do I validate this form content with a callback?

I've got a form that will be uploading a zip file in addition to some other text data.

Before the form is submitted, I need to first ensure that the zip file that's getting ready to be posted doesn't have a name that's already in the database. So, as part of my validation, I'm posting the basename of the zip file to an api that returns a value that indicates whether or not it's a duplicate or not. Provided it's not a duplicate, my validation process then goes on to make sure that the other fields have been properly filled out.

Here's the snag...

I'm assuming that I need to structure this as a callback, but I don't know how. Here's my code:

<?php
session_start(); 
if (@$_SESSION['auth'] != "yes")                   
{
header("Location: default.php");
exit();
}

require_once('header.php');

?>

<style>

#picture_gallery {
	width:200px;
	height:200px;
	background-color:#ffffff;
	border:1px solid #ccc;
	border-radius:10pt;
	background-color:#ffffff;
	position:fixed;
	margin-left:400px;
	display:none;
	margin-top:100px;
	z-index:1000;
	}
	
	#picture_frame {
		height:25px; 
		background-color:#ccc; 
		border-top-left-radius:10pt; 
		border-top-right-radius:10pt; 
		width:100%; 	
	}
	
}

</style>
<div id="picture_gallery">
	<div id="picture_frame">
		<div style="float:right; margin-right:10px; padding-top:3px;"><a id="close" href="#" style="text-decoration:none;">&#8855;</a></div>
		<div id="gallery" style="width:100%; text-align:center; padding-top:30px;"></div>
	</div>
</div>	
	
<table class="center" style="width:900px;">
	<tr>
		<td class="TitleText">
		<b>Product Display Page</b>
		</td>
	</tr>
	<tr>
		<td>&nbsp;<BR>
		</td>
	</tr>
	<tr>
		<td class="MainText">
		<P>
		Here's the product you just selected. Make your changes and then click, "submit."
		<br><br>
		To view the image that corresponds to this product, click on the "Photo" link. Also, "carousel" refers to the products that are featured on the index page. 
		<br><br><span style="background-color:yellow;">Height, width, length are measured in inches, weight is measured in pounds. You do not have to add "inches" or "pounds" to the value. That will automatically be done by the system. <b>This only pertains to you if you have a UPS account and you ship products from your home</b>.</span>
		<br><br>
		To return to the Product List, click <a href="product_list.php">here</a>.
		<P>
		<?php include ("help.php"); ?>
		</td>
	</tr>
	<tr>
		<td>
		&nbsp;<BR>
		</td>
	</tr>
	<tr>
		<td>
		<HR>
		</td>
	</tr>
	<tr>
		<td>&nbsp;<BR>
		</td>
	</tr>
	<tr>
		<td style="text-align:center;">
			<div style="width:800px; margin:auto; height:auto; padding:10px; border:1px solid #ccc; border-radius:10pt; box-shadow:10px 10px 5px #ccc;">	
				<table style="width:auto; margin:auto; background-color:#ffffff; border:1px solid #cccccc; border-radius:10px; padding:10px;" border="0">
				<form enctype="multipart/form-data" action="product_edit.php" method="post">
					<tr>
						<td style="background-color:#ccc; text-align:center;" colspan="2">
							<table style="width:auto; margin:auto;">
								<tr>
									<td style="border-left:1px solid #fff;">&nbsp;Product Type:</td>
									<td style="border-left:1px solid #fff;">&nbsp;<a href="#" class="tooltip" data-tooltip-content="#third_party_vendor" style="text-decoration:underline;">3rd Party Vendor</a></td>
									<td><input type="checkbox" name="third_vendor" value="Y" id="vendor"></td>
									<td style="border-left:1px solid #fff;">&nbsp;<a href="#" class="tooltip" data-tooltip-content="#downloadable_product" style="text-decoration:underline;">Downloadable</a></td>
									<td><input type="checkbox" name="downloadable" value="Y"  id="downloadable"></td>
									<td style="border-left:1px solid #fff;">&nbsp;<a href="#" class="tooltip" data-tooltip-content="#hardcopy_product" style="text-decoration:underline;">Hardcopy</a></td>
									<td><input type="checkbox" name="hardcopy" value="Y"  id="hardcopy"></td>
								</tr>
							</table>
						</td>		
					</tr>
					<tr>
						<td colspan="2">&nbsp;<br></td>
					</tr>
					<tr>
						<td>Name</td>
						<td><input type="text" style="width:532px;" name="name" id="name"></td>
					</tr>	
					<tr id="download_url">
						<td class="MainText" >Download</td>
						<td><input name="download" type="file" size="66" id="download_folder"></td>
					</tr>	
					<tr>
						<td colspan="2" style="text-align:center;">
							<input type="hidden" name="id" id="product_id" value="8">
							<input type="image" src="images/submit.jpg" style="width:85px;" value="submit" id="validate">
						</td></form>
					</tr>					
				</table>
			</div>
		</td>
	</tr>
</table>

<script>

$(document).ready(function() {
			
	/*
	here is where we're going to make sure that if the download checkbox is checked, the user has entered a zip file and then make sure there isn't already another zip file with the same name
	*/
	$('#validate').click(function(event) {
		
		var download_folder=$('#download_folder').val();
		var product_id=$('#product_id').val();
		var error="";
		
		if($('input[name="downloadable"]').is(":checked")) {
			if(download_folder.length==0)
			{
				event.preventDefault();
				$('#download_folder').css("border", "1px solid red");
				alert("Don't forget to enter your product's zip file.");
			}
			else
			{
				//the download box has been checked AND there's a valid zip file to be uploaded
				var checkZip = {
					'product_id':product_id,
					'current_download': download_folder
				}
				$.post("zip_check.php", checkZip, function(Drumstick) 
				{	
					if (Drumstick == "1") 
					{
						event.preventDefault();
						alert("You already have a zip file in the system with the same name as the one you're getting ready to upload. Change the name of the zip file and try again!");
						
						$('#download_folder').css("border", "1px solid red");	
					}
					else
					{
						//there's a unique zip file to be uploaded so now we validate and post the rest of the "stuff"
						//alert("it's a unique zip file");
						$('#download_folder').css("border", "");	
					}
				});	
			}
		}
		else
		{
			event.preventDefault();
			alert("download button isn't checked");
		}
	});
	
	$('.tooltip').tooltipster({
		theme: 'tooltipster-shadow',
		arrow: true,
		interactive: true
	});
	
	//functionality for photo display
	$('.picture_display').click(function() {
		event.preventDefault();
		var picture_id=$(this).data("picture_id");	
		$('#picture_gallery').show();
		$.get( "photograph.php?id="+picture_id, function( data ) {
		  $( "#gallery" ).html( data );
		});
	});
	
	$('#close').click(function() {
		event.preventDefault();
		$('#picture_gallery').hide();
	});
	
});

</script>
		
 <?php require_once('footer.php'); ?>

Open in new window


Line #154 is where the trouble is. The "download" button has been checked, the user has included a zip file to be uploaded so we're cooking with grease...

Now, I need to go out to the database and make sure the zip file doesn't have a name that's already in the database. If there is a duplicate, line #158 attempts to bring everything to a halt so the alert can be published and we can reign everything in, but the form continues to be posted.

I'm thinking that's because JQuery hasn't waited for the for the data to come back from the api and it's already posted the form, regardless of the alert that says you've got a duplicate. In other words, I need to position line #154 as a callback.

Yes?

How do I get that done?
Avatar of Zakaria Acharki
Zakaria Acharki
Flag of Morocco image

Hi Bruce,

You're doing well, you need just to prevent the form from submitting after clicking the submit button immediately by adding the preventDefault() like:

$('#validate').click(function(event) {
   event.preventDefault();

   ...
});

Open in new window

Avatar of Bruce Gust

ASKER

OK, but when I do this:

	$('#validate').click(function(event) {
		event.prevenDefault();
		var download_folder=$('#download_folder').val();
		var product_id=$('#product_id').val();
		var error="";
		
		if($('input[name="downloadable"]').is(":checked")) {
			if(download_folder.length==0)
			{
				$('#download_folder').css("border", "1px solid red");
				alert("Don't forget to enter your product's zip file.");
			}
			else
			{
				//the download box has been checked AND there's a valid zip file to be uploaded
				var checkZip = {
					'product_id':product_id,
					'current_download': download_folder
				}
				$.post("zip_check.php", checkZip, function(Drumstick) 
				{	
					if (Drumstick == "1") 
					{
						alert("You already have a zip file in the system with the same name as the one you're getting ready to upload. Change the name of the zip file and try again!");
						
						$('#download_folder').css("border", "1px solid red");	
					}
					else
					{
						//there's a unique zip file to be uploaded so now we validate and post the rest of the "stuff"
						//alert("it's a unique zip file");
						$('#download_folder').css("border", "");	
					}
				});	
			}
		}
		else
		{
			alert("download button isn't checked");
		}
	});

Open in new window


...and I don't have the "downloadable" check box clicked, I would expect the alert on line #39 to pop up. Instead, it submits the form.

Why?
you need your ajax call to be synchronous instead asynchronous.
replace :
				$.post("zip_check.php", checkZip, function(Drumstick) 
				{	
					if (Drumstick == "1") 
					{
						event.preventDefault();
						alert("You already have a zip file in the system with the same name as the one you're getting ready to upload. Change the name of the zip file and try again!");
						
						$('#download_folder').css("border", "1px solid red");	
					}
					else
					{
						//there's a unique zip file to be uploaded so now we validate and post the rest of the "stuff"
						//alert("it's a unique zip file");
						$('#download_folder').css("border", "");	
					}
				});

Open in new window

by :
				$.ajax({ 
                                       method: 'POST',
                                       async: false,
                                       url: "zip_check.php", 
                                       data: checkZip,  
                                       success: function(Drumstick)  {	
					   if (Drumstick == "1") 
					   {
						event.preventDefault();
						alert("You already have a zip file in the system with the same name as the one you're getting ready to upload. Change the name of the zip file and try again!");
						
						$('#download_folder').css("border", "1px solid red");	
					   }
					   else
					   {
						//there's a unique zip file to be uploaded so now we validate and post the rest of the "stuff"
						//alert("it's a unique zip file");
						$('#download_folder').css("border", "");	
					   }
				    }                                
                                });

Open in new window


another method would be to use a normal button, so not a submit button. and this is your callback function to submit the form so you don't need to use a synchronous ajax call
I was thinking about using a normal button, but i hesitated because I wasn't sure how JQuery handles <form enctype="multipart/form-data..."

If I'm uploading a zip file, does JQuery have a special way of handling that? I'm sure it does, but I've not done that myself yet...
Also, leakim971, should the code determine that it's a unique zip file, this part of the code:

else
					   {
						//there's a unique zip file to be uploaded so now we validate and post the rest of the "stuff"
						//alert("it's a unique zip file");
						$('#download_folder').css("border", "");	
					   }

Open in new window


...will actually do another round of form validation. So, it will look something like this:

else
					   {
						//there's a unique zip file to be uploaded so now we validate and post the rest of the "stuff"
						//alert("it's a unique zip file");
						$('#download_folder').css("border", "");	
					   }
                                           if(price.length==0)
		                           {
			                   $('#price').css("border", "1px solid red");
			                   if(error.length>0)
			                   {
			                   error=error.concat("\nDon't forget to enter a value for the product's price.");
			                    }
			                    else
			                    {
				             error="Don't forget to enter a value for the product's price.";
			                     }
                                             if(error.length>0){
alert(error);
}

Open in new window


Will that work?

Also, I always understood JQuery to be an asynchronous code by default. How is it that you can dictate things in a way where it becomes synchronous?
I was thinking about using a normal button, but i hesitated because I wasn't sure how JQuery handles <form enctype="multipart/form-data..."

there's absolutely NO difference using the normal or the submit button
what is submitted is your form
the submit button fire the event to submit the form
the normal doesn't do that
so your form doesn't change between normal or submit button
now, you submit the form (fire the event) using javascript instead a submit button

How is it that you can dictate things in a way where it becomes synchronous?
this line :
 async: false,

more info here, search for "async" : https://api.jquery.com/jquery.ajax/
Hi Bruce,

IMO the problem here has nothing to do with sync & async request you're already waiting for the response what is good enough for your code to work you need just to submit the form when the zip is ok, here's tested working code :

$(document).ready(function() {
  $('#validate').click(function(event) {
    event.preventDefault();
    
    var form = $(this).closest('form');
    var download_folder = $('#download_folder').val();
    var product_id      = $('#product_id').val();

    if ( $('input[name="downloadable"]').is(":checked") ) 
    {
      if (download_folder.length == 0) {
        $('#download_folder').css("border", "1px solid red");
        alert("Don't forget to enter your product's zip file.");
      } else {
        var checkZip = {
          'product_id': product_id,
          'current_download': download_folder
        }

        $.post("zip_check.php", checkZip, function(Drumstick) {
          if (Drumstick == "1") 
          {
            alert("You already have a zip file in the system with the same name as the one you're getting ready to upload. Change the name of the zip file and try again!");

            $('#download_folder').css("border", "1px solid red");
          } else {
            $('#download_folder').css("border", "1px solid blue");
            form.submit();
          }
        });
      }
    } else {
      alert("download button isn't checked");
    }
  });
});

Open in new window


Here's a live sample that mock the valid zip response.
Here's another sample that mock the invalid zip response.
Zakaria!

We're close, but we're still not there...!

First off, I've got add some additional validation criteria. Nothing major, just want to make sure my user has added some additional info.

What I have below doesn't throw any errors, but the form never gets submitted and I don't know why. I've got the additional validation criteria included so this is a bonafide model of where I need to go.

What am I missing? Why does it not submit the form? I'm thinking it's because I invoke the "event.preventDefault()" right away, consequently everything has been brought to a halt.

What do you think?

  $('#validate').click(function(event) {
    event.preventDefault();
    
    var form = $(this).closest('form');
	var name= $('#name');
    var download_folder = $('#download_folder').val();
    var product_id      = $('#product_id').val();

    if ( $('input[name="downloadable"]').is(":checked") ) 
    {
      if (download_folder.length == 0) {
        $('#download_folder').css("border", "1px solid red");
        alert("Don't forget to enter your product's zip file.");
      } else {
        var checkZip = {
          'product_id': product_id,
          'current_download': download_folder
        }

        $.post("zip_check.php", checkZip, function(Drumstick) {
          if (Drumstick == "1") 
          {
            alert("You already have a zip file in the system with the same name as the one you're getting ready to upload. Change the name of the zip file and try again!");

            $('#download_folder').css("border", "1px solid red");
          } 
		});
		
		$('#download_folder').css("border", "1px solid blue");
		
		if(name.length==0)
		{
		$('#name').css("border", "1px solid red");
		if(error.length>0)
		{
		error=error.concat("\nDon't forget to enter a value for the product's name.");
		}
		else
		{
			error="Don't forget to enter a value for the product's name.";
		}
		}
		else
		{
			$('#name').css("border", "");
		}
		form.submit();

      }
    } else {
      alert("download button isn't checked");
    }
  });

Open in new window

are you using a jquery validation plugin which may prevent your form to be sumbitted until this form is "valid" ?

we're assuming  "#validate" is the id of the "submit" button not a "normal" button, please confirm.
as said previously, if you use form.submit(); please use a "normal" button
You're missing just a .val() in the following line :

if(name.length==0)

Open in new window


Must be :

if(name.val().length==0)

Open in new window


But I've rearranged the code a little bit, so the name will be validated first then the zip file, check the live fiddle below.

Working live fiddle

NOTE: You need just to replace the mock API I'm using by your call "zip_check.php".
@leakim is right in the last comment since we'll not use the submit action and we go through the click event then submit manually, we can use just a normal button or I'll go for an img instead of the submit button like :

<img src="https://support.fundly.com/hc/article_attachments/115002307031/Submit.jpg" id="validate">

Open in new window


Just make sure to add the CSS for it like :

#validate{
  cursor: pointer;
  width:85px;
}

Open in new window


Then we can remove the line that prevents the default action (submit) since we don't need it anymore :

event.preventDefault(); //No need for it anymore

Open in new window


Here's an updated fiddle using img
I'm not using a JQuery validation plugin. #validate is the id of the button...
ASKER CERTIFIED SOLUTION
Avatar of leakim971
leakim971
Flag of Guadeloupe image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Zakaria and leakim...

This worked:

$(document).ready(function() {
			
	/*
	here is where we're going to make sure that if the download checkbox is checked, the user has entered a zip file and then make sure there isn't already another zip file with the same name
	*/
	
$('#validate').click(function(event) {
  event.preventDefault();

  var form = $(this).closest('form');
  var name = $('#name');
  var download_folder = $('#download_folder');
  var product_id = $('#product_id').val();

  if ( $('input[name="downloadable"]').is(":checked") ) {
    if (name.val().length == 0) {
      name.css("border", "1px solid red");
      alert("Don't forget to enter a value for the product's name.");
    } else {
      name.css("border", "");

      if (download_folder.val().length == 0) {
        download_folder.css("border", "1px solid red");
        alert("Don't forget to enter your product's zip file.");
      } else {
        download_folder.css("border", "");

        var checkZip = {
          'product_id': product_id,
          'current_download': download_folder.val()
        }

        $.post("zip_check", checkZip, function(Drumstick) {
          if (Drumstick == "1") {
            download_folder.css("border", "1px solid red");

            alert("You already have a zip file in the system with the same name as the one you're getting ready to upload. Change the name of the zip file and try again!");
          } else {
            form.submit();
          }
        });
      }
    }
  } else {
    alert("Download button isn't checked");
  }
});

Open in new window


I think you're both recommending the same line of reasoning for the solution and I want to try and explain it back to you because I want to understand WHY it works and not just THAT it works.

My understanding of JavaScript, as far as its core characteristics, includes the fact that it's a non-blocking coding language meaning that it doesn't wait for any one code block to finish before it moves on to the next block of code (please feel free to correct my terminology at any point). That's why callbacks are so important in that you can use them to compel the order in which your code fires.

When I first set out to build the form that you've been helping me with, I assumed that that the portion of code that reached out to the database to determine whether or not the zip file being uploaded wasn't a duplicate was, by default, a callback.

Why?

Because of the "non-blocking" characteristic of JavaScript. Correct?

I'm envisioning a kid on a skateboard rushing through the code not waiting for anything to finish before he moves on to the block of syntax. I assumed that was the reason that I was having trouble. The form was being submitted despite the fact that the zip file was a duplicate because the code had already moved past that point.

The code / concept that you both seem to be pointing to makes sense, yet I don't understand why there isn't a problem because even with the IF clause on line #34, wouldn't the fact that you're having to wait for a response from "zip_check.php" be something that JavaScript isn't going to wait for and will therefore proceed to the "else" clause.

Can you explain to me WHY it works?

Thanks so much!
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
So, based on your explanation, it would see that the primary reason your code works is because you put the asynchronous part of the code last.

Yes?
No, It works because it prevents the submit at the start when the button clicked then go through the validations and submit when all is fìlled and the zip is ok using "form.submit();".

I just prefer to put the validations in the interface order, but we can validate the zip first then when we got the response go and validate the other fields inside the success callback that will work too.
for the record : http://leak.im/29151219/

<!DOCTYPE html>
<html>
<head>
    <style type="text/css">
        .error {
            border: 1px solid red;
        }
        .zipFilenameNotAlreadyUsed {
            border: 1px solid blue;
        }
    </style>
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script>
        jQuery(function($) {
            $('#validate').click(function(event) {
                event.preventDefault();
                var form = $(this).closest('form');
                var errors = new Array();
                $('#name,#download_folder,#product_id').removeClass("error");

                if(!$('input[name="downloadable"]').is(":checked")) {
                    $('input[name="downloadable"]').addClass("error");
                    errors.push("download button isn't checked");
                }

                var download_folder = $('#download_folder').val();
                if(!download_folder.length) {
                    $('#download_folder').addClass("error");
                    errors.push("Don't forget to enter your product's zip file.");
                }

                var product_id = $('#product_id').val();
                if(!product_id) {
                    $('#product_id').addClass("error");
                    errors.push("Don't forget to enter a value for the product's name.");
                }

                if(!$.trim($('#name').val()))  {
                    $('#name').addClass("error");
                    errors.push("Don't forget to enter a value for the product's name.");
                }

               if(errors.length == 0) {
                    var checkZip = {
                        'product_id': product_id,
                        'current_download': download_folder
                    };
                    $.post("zip_check.php", checkZip, function(Drumstick) {
                        if(Drumstick == "1") {
                            $('#download_folder').addClass("error");
                            alert("You already have a zip file in the system \nwith the same name as the one you're getting ready to upload.\nChange the name of the zip file and try again!");
                        }
                        else {
                            $('#download_folder').addClass("zipFilenameNotAlreadyUsed");
                            form.submit();
                        }
                    });
               }
               else
                   alert(errors.join("\n"));
            });
        });
    </script>
</head>
<form action="https://www.experts-exchange.com/questions/29151219/How-do-I-validate-this-form-content-with-a-callback.html" method="get" enctype="multipart/form-data">
    <input type="checkbox" name="downloadable" checked="checked" /><br>
    <input type="text" id="product_id" value="1234" /><br>
    <input type="text" id="name" value="somefile.txt" /><br>
    <input type="text" id="download_folder" value="/path/to/file/" /><br>
    <button type="button" id="validate">SUBMIT</button>
</form>
</html>

Open in new window


zip_check.php :
<?php
    echo $_POST["current_download"] == "/path/to/file/" ? "1" : "0";

Open in new window

Gentlemen!

I'm a lot closer than where I was when I first started this, but I'm still short of what I need.

If you're willing, here's another question that's piggy backing off what you've helped me with here: https://www.experts-exchange.com/questions/29152260/What-is-Going-Wrong-With-This-Form-Validation-Scenario.html#questionAdd

Your input is most welcome!

Thanks!