0

Creating and Appending a List Using AutoSuggest and JQuery

Coldfusion, JQuery

At work we have a form that emails a link to an audio file to a single address or a list of recipients. I was tasked with creating a way for the logged in user to, in some fashion, have at their disposal a list of the contacts they had sent these emails to in the past. Rather than wanting to go the route of a simple Select tag and looping through records to create the options, I opted to do this with the auto suggest attribute of the cfinput tag. I saw the way google was doing this with their gmail application, and really liked what I saw.

In the begining everything seemed hunkey-dorey. I created my CFC, which I will share later in this post, added the autosuggest attribute to my existing cfinput, and pointed it to my CFC. Voila! I have a working autosuggest field. This would be well and good if I wasn't worried about handeling more than one email address. Unfortunately that is exactly what I needed to do. I knew I would need to delimit the addresses, but wasn't sure how to go about getting the autosuggest to perform a search on the text being entered while ignoring text that had been previously entered. After an hour of struggeling with this I gave in and looked for another route. As a side note, I had asked on the cf-talk mailing list how to use autosuggest with delimiters and Dominic got a wild hair and came up with a great solution. You can read about it on his blog. He also built a custom tag to handle this. Sadly, all of this was done after I had implemented my workaround.

So before I get started let me point out that my workaround can actually be used on any application server. The only thing I used CF for on this was my CFC I used to query and the autosuggest attribute in the cfinput tag. These can easily be replaced with any method of querying a database and returning a string and a litte more jquery to perform the autosuggest.

The emailAutoSuggest.cfc file is pretty straight forward. All it does is query the database using the value being passed in the argument then returns the string.

emailAutoSuggest.cfc:

<cfcomponent displayname="emailAutoSuggest" 
             hint="Auto Suggests Email Addresses" 
             output="true">

	<cffunction name="lookupEmail" 
                    displayname="lookupEmail" 
                    hint="Queries for an email address based on string in form" 
                    access="remote" 
                    output="false" 
                    returntype="String">

		<cfargument name="search" 
                            displayName="search" 
                            type="Any" 
                            hint="passes the form string to the query" 
                            required="false" />

			<!--- define vars --->
			<cfset var data = "">

			<!--- do the searching --->
			<cfquery datasource="#application.ds#" name="data">
			SELECT email
			FROM emailview
			WHERE  UCASE(email) LIKE UCASE('#ARGUMENTS.search#%')
			ORDER BY email
			</cfquery>

                        <!--- clean out any undesirable marks --->
			<cfset data = replace(data.email,'; ','','ALL')>
                        <cfset data = replace(data.email,',','','ALL')>

		<cfreturn data />
	</cffunction>
</cfcomponent>

Here's the form using just the autosuggest attribute. In it's current state you will be able to use autosuggest to look up only the first address, anything typed after that is combined with the previous characters and sent back to the CFC as one big string. So, unless the email address you are looking for really is "me@home.com;you@work.net" this isn't going to be a very useful tool.

theForm.cfm:

<cfform action="#getfilefrompath(cgi.script_name)#" method="post" name="emailForm">
	<input type="hidden" name="emailList" id="emailList" value="" />

	<table cellpadding="0" cellspacing="0" border="0">
		<tr>
			<td>
				Your name:
			</td>
			<td>
				<cfinput type="text"
						 name="senderName"
						 value=""
						 size="32"
						 required="yes"
						 message="You must enter the sender's name" />
			</td>
		</tr>
		<tr>
			<td>
				Your email:
			</td>
			<td>
				<cfinput type="text"
						 name="senderEmail"
						 value=""
						 size="32"
						 required="yes"
						 message="Please enter your email address - it will be used as the reply-to address" />
			</td>
		</tr>
		<tr>
			<td>&nbsp;</td>
		</tr>
		<tr>
			<td>
				Email recipient(s):
			</td>
			<td>
				<cfinput type="text"
						 autosuggest="cfc:CFC.emailAutoSuggest.lookupEmail({cfautosuggestvalue})"
						 name="emailInput"
						 size="32"
						 required="no"
						 message="You must enter at least one email address"> *
				<input type="button" value="Add" id="addEmail" />
			</td>
		</tr>
		<tr><td>&nbsp;</td></tr>
		<tr>
			<td>
				To:
			</td>
			<td>
				<span id="recipients"></span>
			</td>
		</tr>
		<tr>
			<td>
				Subject line:
			</td>
			<td>
				<cfinput type="text"
						 name="lesubject"
						 value="Replace this with your subject line"
						 size="32"
						 required="yes"
						 message="You must enter a subject line">
			</td>
		</tr>
		<tr>
			<td height="30" colspan="2">
				Comments:
			</td>
		</tr>
		<tr>
			<td colspan="2">
				<textarea name="comments" rows="4" cols="37"></textarea>
			</td>
		</tr>
		<tr>
			<td>
				<input type="submit" value="Send" />
			</td>
		</tr>
		<tr>
			<td height="48" colspan="2">
				* Separate multiple email addresses with a comma.
			</td>
		</tr>
	</table>
</cfform>

To create the list of email addresses I turned to JQuery to handle manipulating the data from the autosuggest field. Below is that JQuery script. I'd like to thank aquaone in the #Jquery chat for helping me with my logic and getting rid of the jquery.form plugin.

<!--- JS Libraries --->
<script src="../jquery/jquery.js" type="text/javascript"></script>
<!--- Scripts --->
<script>
	$(document).ready(function(){
		$('#addEmail').click(function() {
			var recValue = $('#emailInput').val();
			$('#recipients').append(recValue + ', ');
			$('#emailList').val($('#emailList').val()+recValue+',');
			$('#emailInput').val('');
		});
	});
</script>

Here's what's happening.

When the user has entered or selected an email address and clicks the Add button JQuery is storing the field value in the variable recValue. Then it appends that value to a span tag with the ID of recipients. If the span was empty to begin with then it begins the list with the first address entered. After that we go ahead and append the same data to the hidden form input's (emailList) value attribute. Again, if no data was in the input to begin with it will create the list. One small difference here though, we are preserving any data that has already been placed in the list and appending any new values to the existing data. This will ensure that all our recipients make it to the value of our form input. Finally, we clear the autosuggest field so it's ready for new data to be entered. You'll also notice that in addition to appending the value of the autosuggest field I am also appending a comma each time a new piece of data is appended to our list. This will help distinguish the email addresses from one another when our form processor receives the data.

So, here's the completed form with the auto suggest and the jquery append.

<!--- JS Libraries --->
<script src="../jquery/jquery.js" type="text/javascript"></script>
<!--- Scripts --->
<script>
	$(document).ready(function(){
		$('#addEmail').click(function() {
			var recValue = $('#emailInput').val();
			$('#recipients').append(recValue + ', ');
			$('#emailList').val($('#emailList').val()+recValue+',');
			$('#emailInput').val('');
		});
	});
</script>

<cfform action="#getfilefrompath(cgi.script_name)#" method="post" name="emailForm">
	<input type="hidden" name="emailList" id="emailList" value="" />

	<table cellpadding="0" cellspacing="0" border="0">
		<tr>
			<td>
				Your name:
			</td>
			<td>
				<cfinput type="text"
						 name="senderName"
						 value=""
						 size="32"
						 required="yes"
						 message="You must enter the sender's name" />
			</td>
		</tr>
		<tr>
			<td>
				Your email:
			</td>
			<td>
				<cfinput type="text"
						 name="senderEmail"
						 value=""
						 size="32"
						 required="yes"
						 message="Please enter your email address - it will be used as the reply-to address" />
			</td>
		</tr>
		<tr>
			<td>&nbsp;</td>
		</tr>
		<tr>
			<td>
				Email recipient(s):
			</td>
			<td>
				<cfinput type="text"
						 autosuggest="cfc:CFC.emailAutoSuggest.lookupEmail({cfautosuggestvalue})"
						 name="emailInput"
						 size="32"
						 required="no"
						 message="You must enter at least one email address"> *
				<input type="button" value="Add" id="addEmail" />
			</td>
		</tr>
		<tr><td>&nbsp;</td></tr>
		<tr>
			<td>
				To:
			</td>
			<td>
				<span id="recipients"></span>
			</td>
		</tr>
		<tr>
			<td>
				Subject line:
			</td>
			<td>
				<cfinput type="text"
						 name="lesubject"
						 value="Replace this with your subject line"
						 size="32"
						 required="yes"
						 message="You must enter a subject line">
			</td>
		</tr>
		<tr>
			<td height="30" colspan="2">
				Comments:
			</td>
		</tr>
		<tr>
			<td colspan="2">
				<textarea name="comments" rows="4" cols="37"></textarea>
			</td>
		</tr>
		<tr>
			<td>
				<input type="submit" value="Send" />
			</td>
		</tr>
		<tr>
			<td height="48" colspan="2">
				* Separate multiple email addresses with a comma.
			</td>
		</tr>
	</table>
</cfform>

I don't have a demo posted yet, but I'll try and get one up later this weekend. Good luck to you all in your form creating challenges... I know I've had my fill for the week!

Dominic said:
 
Nice. I thought about suggesting something along those lines but figured you have an imagination (always narks me when people start saying 'why do that when you can do this...')!

Incidentally, have you seen the way facebook does it? You could do something similar with your span containing the email list; each email appears as a 'globule' with a little delete cross on it. That way you can intuitavely add and remove the emails from the list.

Dom
 
posted 190 days ago
View Replies (1) || Add Comment Reply to: this comment OR this thread
 
.: HIDE REPLIES :.
Steve said:
 
Yeah, I thought about adding in the ability to edit the list of email addresses before the form is submitted, but ended up leaving that out in the interest of time and wanting to go home and sleep ;)
 
posted 190 days ago
Add Comment Reply to: this comment OR this thread
 

Search