Doing form Post in Spry (2)

Last week or so I blogged about doing form postings with Spry. I was asked to provide a bit more documentation so I thought I'd show a slightly more detailed example.

First lets start with a very simple form:

<form>
number one: <input type="text" name="one" id="one"><br>
number two: <input type="text" name="two" id="two"><br>
</form>

This form has two simple text fields named one and two. I want to use Spry to send the values of these two fields to the server. First lets add a simple button:

<input type="button" value="Add" onClick="doAddPost()">

This simply fires off a JavaScript function. I'm going to break the function up and explain each and every line to make it as clear as possible. First, start the function.

function doAddPost() {

We need to know where we will be posting the form, so next I define the URL:

var url = "moon.cfm";

Now I need to grab the values I want to post. Normally with a submit button you don't have to worry about this. The browser simply sends all the form fields. In this case though I have to specify the fields manually. First I'll grab the value of the form field, one, using the Spry/Prototype $() shortcut:

var one = $("one").value;

Then I'll grab the value from the second form field:

var two = $("two").value;

The form post data must be sent like a query string: foo1=value1&foo2=value2. Again, I have to do this by hand:

var formData = 'one='+one+'&two='+two;

Next I encode any values in the string, like spaces or other special characters:

formData = encodeURI(formData);

So the last thing we do is run the Spry code that will handle the form post. I talked about this more in the last post so I won't spend a lot of time on it.

Spry.Utils.loadURL('POST', url, true, resFunc, {postData: formData, headers: {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}});

The first argument defines the type of request (GET or POST). The second argument is the URL value I defined earlier. The third argument defines if the call is asynchronous or not. The fourth argument defines a function to run with the result of the HTTP call. Lastly there is a structure of arguments that define the request. Again I have to thank Keith for figuring this out.

So here is the function again all in one code block:

function doAddPost() {
   var url = "moon.cfm";
   var one = $("one").value;
   var two = $("two").value;   
   var formData = 'one='+one+'&two='+two;
   formData = encodeURI(formData);
   Spry.Utils.loadURL('POST', url, true, resFunc, {postData: formData, headers: {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}});
}

So how do we handle this server side? We have a few options. As I mentioned, the loadURL function lets you define code to run with the result. So whatever the server returned I can work with in JavaScript. This can be either a string or XML or WDDX. To make it easier I'll just return a simple string:

<cfsetting enablecfoutputonly=true>
<cfparam name="form.one" default="0">
<cfparam name="form.two" default="0">

<cfif isNumeric(form.one) and isNumeric(form.two)>
   <cfoutput>#form.one+form.two#</cfoutput>
<cfelse>
   <cfoutput>0</cfoutput>
</cfif>

Obviously there isn't anything too complex here, just the addition of two numbers. I take the result and simply output it directly to the client requesting the data. With me so far?

Now let's return to JavaScript and work with the result. I had specified the a function named resFunc would handle the result. The function is all of 4 lines:

function resFunc(request) {
   var result = request.xhRequest.responseText;
   $("result").innerHTML = "Result was: " + result;   
}

Spry automatically passes a collection of data back. The information I'm interested in resides in xhRequest.responseText. Once I have that, I can write out the result in the browser.

So I had mentioned more than once I wanted to make this process a bit simpler. Here is my first draft at it:

function doFormPost(url,formlist,resfunc) {
   var formdata = '';
   var formarray = formlist.split(',');
   for(var i=0; i < formarray.length; i++) {
      formdata+='&'+formarray[i]+'=';
      var fValue = $(formarray[i]).value;
      formdata+=fValue;
   }
   formData = encodeURI(formdata);
   Spry.Utils.loadURL('POST', url, true, resfunc, {postData: formdata, headers: {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}});
}

This lets you pass in a url and a list of form fields to post. The third argument is optional. So to do a Spry post you can do this instead of the button and custom function I had before:

<input type="button" value="Add3" onClick="doFormPost('moon.cfm', 'one,two',resFunc)">

As I said though this is just a first draft. Right now it assumes just text fields.

Comments

Thank you sooooo much Ray, I look forward to more :) I'm printing and putting the jug on now.
# Posted By Mark | 1/30/07 3:49 AM
Putting the jug on?
# Posted By Raymond Camden | 1/30/07 5:32 AM
Hmmmmmm.... jug... last time I heard someone "putting the jug on" they were placing it under the spigot of a STILL!!! (grins)

Anyway... nice stuff, Ray... between Spry and Flex2 my brain has been pretty stuffed the last few weeks. I HATE being behind the learning curve.
# Posted By Sid Wing | 1/30/07 7:57 AM
Now now, no still here lol. I'm in New Zealand so I guess it's a saying we have that you folk don't. It means I'm boiling some water for a cup of coffee or tea :) Nothing sinister, I promise.

Thanks again Ray, you are a life saver. Before I try, will this pass multiple checked check boxes to coldfusion the normal way?
# Posted By Mark | 1/30/07 12:51 PM
This sample code only works with text fields. I'm going to update it eventually to work with all form fields.
# Posted By Raymond Camden | 1/30/07 1:10 PM
Ray, Thanks to your tutorial and a little googling, I got this puppy working, it's made my day.

// delete images action function
function deleteImages() {
var url = "img-del.cfm";
var vaultImgFile = document.imgForm.vaultImgFile;
    var formData = "vaultImgFile=";
       for (i = 0; i < vaultImgFile.length; i++){
      if (vaultImgFile[i].checked == true){
         formData = formData + vaultImgFile[i].value + ",";
      }//ends IF
   }//ends FOR
   // strip last comma
   formData = formData.substr(0,formData.length-1);
   // encode for spry
formData = encodeURI(formData);
Spry.Utils.loadURL('POST', url, true, delComplete, {postData: formData, headers: {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}});
}
function delComplete(request) {
   var result = Spry.Utils.encodeEntities(request.xhRequest.responseText);
   $("respContent").innerHTML = "Result was: " + result;
   // reset status text area
   setTimeout("clearVaultImageResults()",10000);
   // reload image list
   dsVaultImages.setURL("images.cfm?nocache="+d.getSeconds());
   dsVaultImages.setXPath('images/image');
   dsVaultImages.loadData();
}

Cheers.
Mark.
# Posted By Mark | 1/30/07 6:14 PM
Extract form field value this way can be time consuming. That's why I wrote a JavaScript library for forms that, among other features, let you extract and "serialize" field's value from any form. You can see it in action here
http://www.massimocorner.com/libraries/form/sample...

Hope it could help

Massimo
# Posted By Massimo Foti | 1/30/07 11:30 PM
Excellent!!!

I can understand it all but for one part that will be obvious to anyone with a brain and knowledge of Java,

"Spry automatically passes a collection of data back. The information I'm interested in resides in xhRequest.responseText. Once I have that, I can write out the result in the browser."

HOW to I display the reasult?

I have it all working
see http://www.coolabah.com/spry/demos/gallery/test.cf... (underneath the large photo.

But how can I display the actual result????
# Posted By Kate Juliff | 2/16/07 6:21 PM
There are a few ways to display something. One way is like so:

Assume foo is the id of a div:

<div id="foo"></div>

Then in JS you can do:

var mydiv = document.getElementById("foo");
mydiv.innerHTML = "some stirng";

This will write out some string as the contents of the div.
# Posted By Raymond Camden | 2/16/07 7:56 PM
OK. I think I'm missing something.

On my page I have these functions as per your example
function doFormPost(url,formlist,resfunc) {
var formdata = '';
var formarray = formlist.split(',');
for(var i=0; i < formarray.length; i++) {
formdata+='&'+formarray[i]+'=';
var fValue = $(formarray[i]).value;
formdata+=fValue;
}
alert(formdata);
formData = encodeURI(formdata);
Spry.Utils.loadURL('POST', url, true, resfunc, {postData: formdata, headers: {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}});
}
function resFunc(request) {
var result = request.xhRequest.responseText;
$("result").innerHTML = "Result was: " + result;



}


doFormPost is called onclick.

Given THAT code, how do I display "result?
# Posted By Kate Juliff | 2/16/07 8:14 PM
OK I have figured it out to an extent. BUT I'm having trouble with the last parameter of Spry.Utils.loadURL.

In the code snippet below I am trying to pass a value to Spry.Utils.loadURL that will them be passed to resfunc as request. But I cannot work out how to do this.

The function that calls Spry.Utils.loadURL works ok., I test that it is receiving the correct value by using alert(value). But how do I get this value into the argument list of Spry.Utils.loadURL so I can display it?


function doFormPost(url,photoId,resfunc) {
formData = encodeURI(photoId);
Spry.Utils.loadURL('POST', url, true, resfunc, {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"});

}



function resFunc(request) {
var mydiv = document.getElementById("foo");
var result = request.xhRequest.responseText;
$("result").innerHTML = "Result was: " + result ;
mydiv.innerHTML = result;



}


function HandleThumbnailClick(id, photoId)
{

StopSlideShow();
doFormPost('moon.cfm', photoId,resFunc);
dsPhotos.setCurrentRow(id);
ShowCurrentImage();
}
# Posted By Kate Juliff | 2/17/07 6:32 PM
I think if you want to do this you need to store it in a global javascript variable.
# Posted By Raymond Camden | 2/17/07 9:42 PM
But in your example it isn't in a global javascript variable.
# Posted By Kate Juliff | 2/17/07 10:00 PM
I must be misunderstanding you. You want to do a form post, and when you, remember a value such that when the form post is done you can retrieve it. Is that not true.
# Posted By Raymond Camden | 2/17/07 10:11 PM
I am trying to do exactly what you illustrate in
http://ray.camdenfamily.com/index.cfm/2007/1/29/Do...

but instead of passing a form variable, I'm trying to post an argument to the function .

I'm concerned with thew last argument -
{postData: formData, headers: {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}}

where I do not have "formdata", but a variable passed to the function.
# Posted By Kate Juliff | 2/17/07 10:25 PM
formdata is simply a query string I build manually of form values. You can use other values instead, like the one you passed in. But it must be in query string form:

name=value&name=value& etc
# Posted By Raymond Camden | 2/17/07 10:37 PM
I'm sorry, but I don't understand.

In function doFormPost(url,photoId,resfunc) {
formData = encodeURI(photoId);
Spry.Utils.loadURL('POST', url, true, resfunc, {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"});

}

How do I pass the contents of photoId
# Posted By Kate Juliff | 2/17/07 10:57 PM
You have a few issues here. First, you didn't follow what I said about name=value (query string) format. You can't just pass a value like photoid. It MUST look like a query string. So if you want to pass photoId, you have ot pass a name for the value as well:

var formData = 'photoId='+photoId;

This says - pass the value photoId, but also give it a name of photoId. In your CFM code that processes this form post, you would use form.photoID.

Then you must encode the value:

formData = encodeURI(formData);

Lastly, you removed the part of loadURL that contained formData:

Spry.Utils.loadURL('POST', url, true, resFunc, {postData: formData, headers: {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}});

See how I pass formData as the value to postData?
# Posted By Raymond Camden | 2/17/07 11:13 PM
A liked your post, its almost what im looking for, maybe you can help-me

I need to send post params.

I can send using this for example:
var dsPhotos = new Spry.Data.XMLDataSet("/photos.php", "/gallery/photos/photo", { method: "POST", postData: "galleryid=2000&offset=20&limit=10", headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" } });

Using post data it works,ok, or i can send using your example too, ok, it works.

But in my aplication a create a spry DataSet, and i dont trigger onload, i do that after a few actions.

So, i need to send a post params after i create the Dataset, because i use DataSet.loadData() after some time

But, your example should work, but i couldant populate a spry dataset with my xml result format

Could you help me??
# Posted By Marcelo Macedo | 4/23/07 10:40 AM
I'm sorry, but I'm having a hard time understanding your English. Everything after "But in my ..." is a bit greek to me. Can you reword?
# Posted By Raymond Camden | 4/23/07 10:55 AM
=D=D=D=D

Really sory, let´s try again

I need to send post params.

I can send using this for example:
var dsPhotos = new Spry.Data.XMLDataSet("/photos.php", "/gallery/photos/photo", { method: "POST", postData: "galleryid=2000&offset=20&limit=10", headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" } });

Using post data it works,ok, or i can send using your example too, ok, it works.


My problem is:
I dont know hot set Post Params, after define a spry DataSet
ex:
var myDs = new Spry.Data.XMLDataSet()
function triggerLater(){
myDs.setXPath("Node/ChildNode")
myDs.setURL(myxml.aspx?aLotOfParams=somevalue);
myDs.loadData()
}

those params ('aLotOfParams') must be POST, because they have a lot of info.

But in your example we heve that function:
function resFunc(request) {
var result = request.xhRequest.responseText;
$("result").innerHTML = "Result was: " + result;
}

than ok, i receive my xml, and i can see it, but i need to populate it in my spry data set
Somethink like:

function resFunc(request) {
var result = request.xhRequest.responseText;
mySpryDataSet=request
}

if you have time,could you help me?
# Posted By Marcelo Macedo | 4/23/07 12:27 PM
So are you saying, how do I do a post after my spry data is loaded? If so you simply need to use a dataset observer. The docs cover this and show you how you could run X after data has been loaded.
# Posted By Raymond Camden | 4/23/07 1:18 PM
test
# Posted By Marcelo Macedo | 4/25/07 7:21 AM
No, its not that, by know i made some progress

there is the source: http://www.wl.com.br/spryXMLv2.zip

So, i have problems to parse my xml data, it works fine in firefox, but not in ie6, do you know why?

Another thing, i just could use it in Firefox when i removed all the white spaces from my XML, Is there some way to keep using spaces??

Thanks for your help!
# Posted By Marcelo | 4/25/07 7:25 AM
Ray, this is a great post, thank you for it!

I went ahead and modified the example slightly and incorporated Massimo's serializer with it. If anyone is interested in downloading the example, please go to:

http://blog.webexterminator.com/index.cfm/2007/12/...

Thanks again Ray and Massimo, you guys are the best!
# Posted By H Jaber | 12/15/07 1:45 PM
You might check out the Spry.Utils.submitForm function. It grabs all of the form variables and submits very nicely.
# Posted By Matthew Penn | 6/7/08 7:39 PM
Spry.Utils.submitForm is a better solution since it handles <select>, radion buttons and checkboxes too. Just be aware it has a bug, it doesn't properly handles <select> tags whenever their value is an empty string. You can see it in action here:

http://www.massimocorner.com/spry/extractParamsFro...

On a side note, Prototype is affected by a similar bug. Actually, it's worst, since it returns different value for IE and FF. At least Spry is wrong, but consistent :-)
# Posted By Massimo Foti | 6/8/08 1:16 AM
Thanks - yea - I definitely recommend submitForm now. This post is rather old. :)

@MF - Did you report the bug?
# Posted By Raymond Camden | 6/8/08 10:03 AM
Yes, I reported the bug
# Posted By Massimo Foti | 6/8/08 2:52 PM
@Ray
Realizing that this is an OLD post, when I tried to implement I found that you may need to update this line:

First I'll grab the value of the form field, one, using the Spry/Prototype $() shortcut:

To:
First I'll grab the value of the form field, one, using the Spry Spry.$() shortcut:

The updated versions of the js files no longer support just $(), it is now Spry.$() - or atleast, by changing from $() to Spry.$() it worked
# Posted By MikeG | 12/1/08 4:15 PM
Good point. I'm feeling a bit lazy though so I assume folks will scan the comments. :) I've got way too many blog posts to make a habit out of updating them all. ;)
# Posted By Raymond Camden | 12/1/08 4:36 PM