Home >
Please forgive the title - I wasn't quite sure what to name this. It may help if I describe the problem first and hopefully things will make a bit more sense. I was asked by a client to add a simple multi-file uploader for their site. I've done this a few times before and there are a lot of nice options out there. Most recently I've used Uploadify (http://www.uploadify.com/), a jQuery plugin, and it worked very well.
However - all of these plugins/scripts/etc, all suffer from one problem - none of them support the addition of extra fields along with the file. Imagine that every file uploaded to your server has to include a description and a set of keywords. A typical multi-file uploader will only let you specify files to upload. They won't let you provide additional details.
To be fair - this really isn't something folks typically do. Normally you add a multi-file uploader because you quickly want to send up a large number of files. If you are going to need to type in a bunch of junk for each file, why bother? Well, I can (and the client could) still see some merit in providing a way to upload multiple files while still providing additional fields for each file. Here is a quick demo of what I came up with.
I knew that it was easy to add form fields to an existing form with jQuery. I blogged on this earlier this year (Using jQuery to add form field). At that time, I simply created a string of HTML and added it to the end of my form. When I did, a few readers pointed out a more elegant way - using the jQuery clone() method. As you can probably guess, this will take a block of elements and create a copy of them. It's pretty perfect for what I have in mind. Let me show the initial form and then I'll add the JavaScript on top.
The form consists of three fields - file1, keywords1, and description1. The numbers will make more sense later. There is a submit button at the end, and on top I added a button with the label, "Add Another File". This is what will drive the ability to add other files. Now let's look at the code.
The majority of the code here resides within the "ready" block so I'll focus on that. I begin by creating an event handler for the click action on my button. Looking back at the HTML, note I had used a fieldSet to wrap my 3 sets of form fields. This lets me then simply do a clone. Now in theory, I could be done right then and there. However, the cloned items end up with the same names and IDs. In ColdFusion, this will result in values stored as lists, which can be a problem if your values contain commas.
At this point I use a technique that was demonstrated by one of the commenters on my blog, Brian Swartzfager. He begins by doing a find on all input and textareas within the new cloned block: newset.find("input,textarea").each(function(i) {
For each of these, we can take the name value, use a regex to remove the number, and then add a new number back to the end. I'm using a global variable, current, to keep store of the values.
After we modify the clioned entity, the last step is to then simply insert it into the dom: newset.appendTo("#dynamicArea")
And that's it... at least for the client side code. Obviously what you do on the server side is up to you. My ColdFusion code simply used a conditional loop. It looked for the existence of fileN, and N continued to increment. As soon as it did not exist, the loop ended. I used a string to return one block of results back to the user. For example: File x.jpg uploaded. File y.jpg was not processed because you forgot the description. File y.pdf was not processed because it was not an image. I've included the complete code below, with the file upload,management, and database insertion commented out. Hopefully it will help others.
However - all of these plugins/scripts/etc, all suffer from one problem - none of them support the addition of extra fields along with the file. Imagine that every file uploaded to your server has to include a description and a set of keywords. A typical multi-file uploader will only let you specify files to upload. They won't let you provide additional details.
To be fair - this really isn't something folks typically do. Normally you add a multi-file uploader because you quickly want to send up a large number of files. If you are going to need to type in a bunch of junk for each file, why bother? Well, I can (and the client could) still see some merit in providing a way to upload multiple files while still providing additional fields for each file. Here is a quick demo of what I came up with.
I knew that it was easy to add form fields to an existing form with jQuery. I blogged on this earlier this year (Using jQuery to add form field). At that time, I simply created a string of HTML and added it to the end of my form. When I did, a few readers pointed out a more elegant way - using the jQuery clone() method. As you can probably guess, this will take a block of elements and create a copy of them. It's pretty perfect for what I have in mind. Let me show the initial form and then I'll add the JavaScript on top.
<form action="multiupload.cfm" method="post" enctype="multipart/form-data" id="mainForm">
<input type="button" id="adder" value="Add Another File">
<div id="dynamicArea">
<fieldset id="file0" style="margin-bottom:10px">
<table>
<tr>
<td>File:</td>
<td><input type="file" name="file1"></td>
</tr>
<tr>
<td>Keywords:</td>
<td><input type="text" name="keywords1"></td>
</tr>
<tr valign="top">
<td>Description:</td>
<td><textarea name="description1"></textarea></td>
</tr>
</table>
</fieldset>
</div>
<input type="submit">
</form>
The form consists of three fields - file1, keywords1, and description1. The numbers will make more sense later. There is a submit button at the end, and on top I added a button with the label, "Add Another File". This is what will drive the ability to add other files. Now let's look at the code.
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
var current = 1
$(document).ready(function() {
$("#adder").click(function() {
current++
var newset = $("#file0").clone(true)
newset.find("input,textarea").each(function(i) {
var $currentElem= $(this)
//remove the number from the name and id
var curname = $currentElem.attr("name")
curname = curname.replace(/[0-9]+$/,'')
var curid = $currentElem.attr("id")
curid = curid.replace(/[0-9]+$/,'')
$currentElem.attr("name",curname+current)
$currentElem.attr("id",curid+current)
})
newset.appendTo("#dynamicArea")
})
})
</script>
The majority of the code here resides within the "ready" block so I'll focus on that. I begin by creating an event handler for the click action on my button. Looking back at the HTML, note I had used a fieldSet to wrap my 3 sets of form fields. This lets me then simply do a clone. Now in theory, I could be done right then and there. However, the cloned items end up with the same names and IDs. In ColdFusion, this will result in values stored as lists, which can be a problem if your values contain commas.
At this point I use a technique that was demonstrated by one of the commenters on my blog, Brian Swartzfager. He begins by doing a find on all input and textareas within the new cloned block: newset.find("input,textarea").each(function(i) {
For each of these, we can take the name value, use a regex to remove the number, and then add a new number back to the end. I'm using a global variable, current, to keep store of the values.
After we modify the clioned entity, the last step is to then simply insert it into the dom: newset.appendTo("#dynamicArea")
And that's it... at least for the client side code. Obviously what you do on the server side is up to you. My ColdFusion code simply used a conditional loop. It looked for the existence of fileN, and N continued to increment. As soon as it did not exist, the loop ended. I used a string to return one block of results back to the user. For example: File x.jpg uploaded. File y.jpg was not processed because you forgot the description. File y.pdf was not processed because it was not an image. I've included the complete code below, with the file upload,management, and database insertion commented out. Hopefully it will help others.
<cfif not structIsEmpty(form)>
<!--- A simple string that stores results we can report back to the user. --->
<cfset results = "">
<!--- loop, looking for fileX--->
<cfset counter = 1>
<cfset turnedOn = structKeyExists(form, "file1")>
<cfloop condition="#turnedOn#">
<!--- Based on my testing, the file, keywords, and description are required, everything else is cake. --->
<!--- in all cases though, check first for the file. no file - no processing --->
<cfif len(form["file#counter#"])>
<cfset keywords = form["keywords#counter#"]>
<cfset description = form["description#counter#"]>
<cfif len(keywords)>
<!--- file upload --->
<!--- db insert --->
<cfelse>
<cfset results = results & "Upload ###counter# was not processed since you forgot a required value.<br/>">
</cfif>
</cfif>
<cfset counter = counter + 1>
<cfset turnedOn = structKeyExists(form, "file#counter#")>
</cfloop>
</cfif>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
var current = 1
$(document).ready(function() {
$("#adder").click(function() {
current++
var newset = $("#file0").clone(true)
newset.find("input,textarea").each(function(i) {
var $currentElem= $(this)
//remove the number from the name and id
var curname = $currentElem.attr("name")
curname = curname.replace(/[0-9]+$/,'')
var curid = $currentElem.attr("id")
curid = curid.replace(/[0-9]+$/,'')
$currentElem.attr("name",curname+current)
$currentElem.attr("id",curid+current)
})
newset.appendTo("#dynamicArea")
})
})
</script>
</head>
<body>
<cfif structKeyExists(variables, "results")>
<p>
<b>Results of your upload:</b><br/>
<cfoutput>#results#</cfoutput>
</p>
</cfif>
<form action="multiupload.cfm" method="post" enctype="multipart/form-data" id="mainForm">
<input type="button" id="adder" value="Add Another File">
<div id="dynamicArea">
<fieldset id="file0" style="margin-bottom:10px">
<table>
<tr>
<td>File:</td>
<td><input type="file" name="file1"></td>
</tr>
<tr>
<td>Keywords:</td>
<td><input type="text" name="keywords1"></td>
</tr>
<tr valign="top">
<td>Description:</td>
<td><textarea name="description1"></textarea></td>
</tr>
</table>
</fieldset>
</div>
<input type="submit">
</form>
</body>
</html>




Facebook Application Development
Thank You! It seems any time I am looking for an elegant way to handle a new problem, you have just dealt with said problem and posted the solution. Each time you are about a week or two ahead of me.
It is truly appreciated and I hope one day i will have learned enough to be able to share something of value with you.
Thanks again!