Ask a Jedi: Example of modal dialog editing with jQuery

This post is more than 2 years old.

Steve asks:

I'm getting onto the jquery / CF integration a bit late. When I get some time I really want to go through all of your tutorials. What I was trying to find was a simple "edit" type form that would be a modal pop up window from a page of items.

For example, say you have a product listing of widgets - and next to each widget is an "edit" link that would open a modal jquery window with some form fields on it - populated by a CFC from a db query and then updated via a CFC from the modal window which then closes on update.

This seems like a pretty good "real world" tutorial but in just browsing through your stuff, and plenty of other sites, I'm not seeing one that really explains this concept.

So - there are obviously a thousand different ways we could implement something like this. Here is an example I came up with. I began by creating a simple, static ColdFusion script that did nothing (nothing Ajax-y anyway) at all.


<cfquery name="getart">
select	artid, artname, description, price
from	art
</cfquery>

<style>
.artdiv {
	padding: 5px;
	margin: 5px;
	background-color: #80ff80;
}
</style>

<cfoutput query="getart">
	
	<div class="artdiv">
		<h2>#artname#</h2>
		<p>
		Price: #dollarFormat(price)#
		</p>
		<p>
		#description#
		</p>
	</div>
	
</cfoutput>

I run a quick query against the cfartgallery database and display each piece of art. I display the name, price, and description for each one. Here is a quick screen shot so you can see what I mean.

Admit it - you love my design, don't you? I call this the "Sickly Green" theme. Anyway, I always try to start simple and then progressively add to it. So let's look at the first iteration of my solution.


<cfquery name="getart" maxrows="10">
select	artid, artname, description, price
from	art
</cfquery>

<html>
<head>
<script src="jquery-ui-1.8.7.custom/js/jquery-1.4.4.min.js"></script>
<script src="jquery-ui-1.8.7.custom/js/jquery-ui-1.8.7.custom.min.js"></script>
<link rel="stylesheet" href="jquery-ui-1.8.7.custom/css/overcast/jquery-ui-1.8.7.custom.css" />
<script>
$(document).ready(function() {

	$(".artdiv").click(function() {
		//based on which we click, get the current values
		//first, the name
		var name = $("h2", this).text();
		var price = $("p:first", this).data("price");
		var desc = $("p:last", this).text();
		desc = $.trim(desc);
		
		$("#namefield").val(name);
		$("#pricefield").val(price);
		$("#descriptionfield").val(desc);
		
		$("#editForm").dialog({
			buttons: {
				"Save": function(){
					console.log('save clicked');
				}
			}
		});
	});
	
});
</script>
<style>
.artdiv {
	padding: 5px;
	margin: 5px;
	background-color: #80ff80;
}
#editForm {
	display:none;	
}
</style>
</head>

<body>

<cfoutput query="getart">
	
	<div class="artdiv">
		<h2>#artname#</h2>
		<p data-price="#price#">
		Price: #dollarFormat(price)#
		</p>
		<p class="description">
		#description#
		</p>
	</div>
	
</cfoutput>

<div id="editForm" title="Edit Art">
	<p>
	<b>Name:</b><br/>
	<input type="text" id="namefield">
	</p>
	<p>
	<b>Price:</b><br/>
	<input type="text" id="pricefield">
	</p>
	<p>
	<b>Description:</b><br/>
	<textarea id="descriptionfield"></textarea>
	</p>
	
</div>

</body>
</html>

Ok, we've got a lot going on here new so let's tackle it from the top. You will notice that I've added both jQuery and jQuery UI. jQuery UI will be used for the dialog control.

Now look at th JavaScript. We want "click to edit" and I decided on simply listening to the click handler for the entire div of the art piece. This is probably not the way I'd do it normally. I'd probably add a simple Edit link to the lower right corner, or perhaps upper right corner. Point is - you can do this anyway you like. At minimum I should add some CSS to make it obvious you can click to edit.

So the next thing we need to do is create a dialog. This will hold the form that we will use to edit the art piece. Now stay with me here as we are going to to back and forth a bit. I need a way to get the data, right? Well I can use jQuery selectors filtered by the current selector (the div you clicked on) to get the information. The name is in my h2 tag. That's easy enough to get. My price is a bit more tricky. Did you notice how I used dollarFormat? I don't want the "pretty" value but the original value. So I added a data-price attribute to my paragraph tab and fetch it out using jQuery's data function. Finally the description is fetched via the last paragraph. Three different examples and again - you could do this any number of other ways as well.

So once we have the values, we can then launch the dialog. If you scroll to the bottom of the page you will see a hidden div there with the form. I set the form field values and than use the dialog() function to show it. I love how easy jQuery UI makes things for me. Note I also added a Save button. It isn't doing anything yet but I can confirm it is firing with a console message. Ok - hopefully you are with me so far. Here is another screen show.

Alright - now let's kick it up to 11. So far we've gotten a dialog form working. It's dynamic so that the values you see in the dialog are dependent on the piece of art you click on. We've got a save button but it isn't doing anything. To complete the application we need to send the form data to the server and update the display. Here is my third and final iteration.


<cfset getArt = new artservice().getArt()>

<html>
<head>
<script src="jquery-ui-1.8.7.custom/js/jquery-1.4.4.min.js"></script>
<script src="jquery-ui-1.8.7.custom/js/jquery-ui-1.8.7.custom.min.js"></script>
<link rel="stylesheet" href="jquery-ui-1.8.7.custom/css/overcast/jquery-ui-1.8.7.custom.css" />
<script>
//credit: http://www.mredkj.com/javascript/numberFormat.html
function dollarFormat(nStr){
	nStr += '';
	x = nStr.split('.');
	x1 = x[0];
	x2 = x.length > 1 ? '.' + x[1] : '';
	var rgx = /(\d+)(\d{3})/;
	while (rgx.test(x1)) {
		x1 = x1.replace(rgx, '$1' + ',' + '$2');
	}
	return "$" + x1 + x2;	
}

$(document).ready(function() {

	$(".artdiv").click(function() {
		var initialDiv = this;

		//based on which we click, get the current values
		var artid = $(this).data("artid");
		var name = $("h2", this).text();
		var price = $("p:first", this).data("price");
		var desc = $("p:last", this).text();
		desc = $.trim(desc);

		$("#artid").val(artid);		
		$("#namefield").val(name);
		$("#pricefield").val(price);
		$("#descriptionfield").val(desc);
		
		$("#editForm").dialog({
			buttons: {
				"Save": function() {
					var thisDialog = $(this);
					$.post("artservice.cfc?method=saveart", 
					{
						id:$("#artid").val(),
						name:$("#namefield").val(),
						price:$("#pricefield").val(),
						description:$("#descriptionfield").val()
					}, 
					function() {
						//update the initial div
						$("h2", initialDiv).text($("#namefield").val());
						var price = $("#pricefield").val();
						$("p:first", initialDiv).data("price", price);
						price = parseInt(price).toFixed(2);
						$("p:first", initialDiv).text("Price: "+dollarFormat(price));
						$("p:last", initialDiv).text($("#descriptionfield").val());
						$(thisDialog).dialog("close");
					});
				}
			}
		});
	});
	
});
</script>
<style>
.artdiv {
	padding: 5px;
	margin: 5px;
	background-color: #80ff80;
}
#editForm {
	display:none;	
}
</style>
</head>

<body>

<cfoutput query="getart">
	
	<div class="artdiv" data-artid="#artid#">
		<h2>#artname#</h2>
		<p data-price="#price#">
		Price: #dollarFormat(price)#
		</p>
		<p class="description">
		#description#
		</p>
	</div>
	
</cfoutput>

<div id="editForm" title="Edit Art">
	<input type="hidden" id="artid">
	<p>
	<b>Name:</b><br/>
	<input type="text" id="namefield">
	</p>
	<p>
	<b>Price:</b><br/>
	<input type="text" id="pricefield">
	</p>
	<p>
	<b>Description:</b><br/>
	<textarea id="descriptionfield"></textarea>
	</p>
	
</div>

</body>
</html>

There's a few changes here and there so let me try to address them one by one. First off - note that I've removed the initial query and replaced it with a CFC call. Going into the JavaScript, ignore that new function for now, and go on down to the artdiv click area. I added a new call to the get ID. (Once again using the data feature.) The save now runs a CFC method called saveart, sending the data from my form. The result for now is a bit simple. I assume everything worked ok and don't worry about validation. I write my values back into the initial div (note I made a variable out of it) and for the most part this is simple. Price isn't quite so easy though. I've got two places where price is used. First in a non-formatted way in the data portion and secondly in a nice formatted version. While there are many plugins out there for number formatting (and currency formatting too), I went with a simple function I found via Google. All in all though the end result is to simply copy the form values from the dialog back into the view.

And that's it. I'd put up a live example of this but I'd have to worry about spammers hitting the edit field. I did zip up all the files (including the CFC if you want to see it) and attached it to this blog entry. Enjoy!

Download attached file.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by andy matthews posted on 1/3/2011 at 1:22 AM

Can't wait for you to check out the web app that I've been working on for the last month. Our modal usage is slick as a greased pig. Keep your eye open for the new http://goba.mobi

Comment 2 by James Moberg posted on 1/3/2011 at 9:27 PM

For simple inline editing like this on admin forms (w/o lots of validation), I've been using the Jeditable jQuery Plugin.
http://www.appelsiini.net/p...

It creates editable form fields on-the-fly and posts the data to the server via ajax. It requires a lot less code and only uses a uniquely coded ID and identifiable class per editable field.

Comment 3 by Raymond Camden posted on 1/3/2011 at 9:29 PM

That's a slick little plugin - thanks for sharing it James.

Andy - be sure to let me know when it goes live!

Comment 4 by James Moberg posted on 1/3/2011 at 9:32 PM

I've been using the CFJS jQuery library for DollarFormat. It adds lots of CF-like functions to jQuery and the syntax is "$.{functionname()}" (ie, $.DollarFormat(price)). When you are using a CF IDE, the hints will provide you with insight into the javascript function (at least on HomeSite+.)
http://cfjs.riaforge.org/

Comment 5 by Raymond Camden posted on 1/3/2011 at 9:37 PM

HomeSite+ - wow, now that Nadel has stopped using it I think you may be the last one around. ;)

Comment 6 by Phillip Senn posted on 1/3/2011 at 10:25 PM

Here's what I use when I want to load jQuery UI:

&lt;cfparam name="url.Theme" default="smoothness">
&lt;cfoutput>
&lt;link rel="stylesheet" href="http://ajax.googleapis.com/..." type="text/css" media="all" />
&lt;/cfoutput>
&lt;script src="http://www.google.com/jsapi">&lt;/script>
&lt;script type="text/javascript">
google.load("jquery", "1");
google.load("jqueryui", "1");
&lt;/script>

Comment 7 by Raymond Camden posted on 1/3/2011 at 10:26 PM

Please don't post escaped code - we escape it already. ;)

Comment 8 by James Moberg posted on 1/4/2011 at 1:06 AM

I use AutoHotKey (Windows) to manage most of my custom hotkey/macros (and they work regardless of the editor I am using.) I also have a lot of snippets and use Evernote to manage code samples. I've tried to move to CFEclipe and Builder, but it just slowed me down compared to how I'm accustomed to programming. (I have direct physical access to most of the servers I work with anyhow.) I also disliked the various line number, line wrap and FTP issues I initially encountered and figured I just keep waiting and I haven't ever taken the time to reevaluate.

Comment 9 by Alex Lovel posted on 1/4/2011 at 8:21 PM

Ray - can you expound a bit on this as to how you'd use tr and td to pass the data in via a click on an edit link? For example:
<tr>
<th>Name</th>
<th>Price</th>
<th>Description</th>
<th></th>
</tr>
<cfoutput query="getArt">
<tr>
<td>#name#</td>
<td>#price#</td>
<td>#description#</td>
<td><a id="#artid# href="">edit</a></td>
</tr>
</cfoutput>

Comment 10 by Raymond Camden posted on 1/4/2011 at 8:23 PM

Interesting - let me write up an example.

Comment 11 by MikeG posted on 1/5/2011 at 1:42 AM

@phillip - Even the CDN is sometimes slow or unavailable and even blocked in some countries. If you are using google.load or even a straight reference to the hosted js files for fallback you should also implement something like this.

if(typeof jQuery == ‘undefined’)
{
// load local version….
}
http://bit.ly/y8IMo

Comment 12 by Phillip Senn posted on 1/5/2011 at 3:55 AM

Thanks Mike.
Duly noted.

Comment 13 by Raymond Camden posted on 1/5/2011 at 4:37 AM

Alex: http://www.coldfusionjedi.c...

Hope this helps.

Comment 14 by Steve Logan posted on 1/5/2011 at 6:45 AM

Quick question - when you post the data with jquery:

$.post("artservice.cfc?method=saveart",

how can you access a CFC that's in a different directory that is not a sub directory of the current.

In this example let's say you had a CF mapping defined "art" and you could access those CFCs via cfinvoke or other methods using: <cfinvoke component="art.artists" method="qryArtists">?

Comment 15 by Raymond Camden posted on 1/5/2011 at 9:18 AM

CF901 added the ability to hit CFC methods in CFCs available via CF mappings. But only via "their" stuff, like for example, cfdiv. It does not work via jQuery.

My answer is you don't want to do it. Period. If you want to run a CFC for Ajax stuff, put it under web root where it belongs.

Comment 16 by Simon posted on 1/16/2011 at 7:14 AM

Thanks for the great help and guidance.

I wondering about using something similar - but instead of a modal or table, using a cfdiv to display the output - but I can't quite get the bind element to work. I'd like to have a list of items that when clicked update the content of a cfdiv

Thanks again - any advice appreciated.
Simon

Comment 17 by Raymond Camden posted on 1/16/2011 at 7:18 AM

I do not recommend mixing jQuery (or another js framework), with the built in CF AJAX UI items. In theory though what you want could be done by using jQuery to listen to the click even for the items and then fire ColdFusion.navigate to change the contents of the cfdiv. You wouldn't need bindings.

Comment 18 by Simon posted on 1/16/2011 at 7:23 AM

Thanks for the (very) prompt reply Ray - and for the pointer.
Cheers
Simon

Comment 19 by Sebastiaan posted on 2/5/2011 at 1:46 PM

What's up with all the beards? Did I miss something?

Comment 20 by andy matthews posted on 2/5/2011 at 6:33 PM

You sure did. It's the beardpocalypse. The aliens don't like beards in their exam rooms and they don't know human physiology well enough yet to realize that they can be shaved off. So they're only picking ppl for their probing that don't have beards.

Plus they look BAD. ASS.

Comment 21 by Tim posted on 11/20/2012 at 2:01 AM

This is very cool, Ray!! Thanks for posting.

One question. I am working on a search tool, in a pop up such as this that will return a list of store locations based upon zip code entered. Submitting that to a cfc is fairly easy. What would be the easiest way to update the page with a returned query of results (will not be more than 3 rows)?

Thanks!

Comment 22 by Raymond Camden posted on 11/20/2012 at 2:04 AM

Your question is kinda broad. You said already you can talk to a CFC and get results. So you get that part. If you are asking how to update the DOM, isn't that simple in jQuery? You take your result, generate html, and do something like $("#somediv").html(somestring)? Or am I missing something?