wForms - Server-side handling of the Repeat Behavior (part 1)
This documentation was updated on June 28th 2005 (fixed code sample)
This post is part of the wForms Documentation. wForms is an open-source javascript library that adds commonly needed behaviors to traditional web forms without the need for any programming skill.
For additional help, visit the wForms forum.
The repeat behavior generates form fields dynamically and this makes the server-side processing a bit more complex than with your usual web form. This is a two parts problem: (1) how to retrieve the submitted information and (2) how to pre-fill a form with a repeated element. This post deals with the first and before we start, here’s a short definition: The ‘repeated element’ refers to the element with the ‘repeat’ class (usually a <div>, a <fieldset> or a <tr>).
The Row Count
The wForms extension generates a hidden ‘row count’ field for each repeated element of the form. The name of the field is derived from the id of the repeated element (and you probably want to set that id attribute if you haven’t done so already).
For instance:
<fieldset id=”someid” class=”repeat” >
… some fields…
</fieldset>
wForms will generates the following markup:
<input type=”hidden” id=”someid-RC” name=”someid-RC” value=”" />
The value of the field is a number indicating the number of time the element has been repeated.
The Row Index suffix
In order to maintain the uniqueness of field names, wForms appends a dash followed by the row index to each repeated field.
| Original row: | <input type=”text” name=”somename” /> |
| Second row: | <input type=”text” name=”somename-2″ /> |
| Third row: | <input type=”text” name=”somename-3″ /> |
The first row is left unchanged. This serves two purposes: First, you don’t have to adopt a specific syntax for field names and second, the form can still be processed normally if javascript was unavailable on the client.
Now, it is important to know that there can be gaps in the sequence. For instance, if the user deletes a row before submitting the form, you’ll end up with:
| Original row: | <input type=”text” name=”somename” /> |
| Third row: | <input type=”text” name=”somename-3″ /> |
The field named ’somename-2′ is missing. In this situation the row count still indicates 3.
This is by design. It would be rather complicated to adjust the counter and renumber all field names in the browser each time a row is deleted.
In effect, the row count gives the upper bound of the loop and the presence of the field must be tested at each iteration.
PHP Code Sample
Here’s the corresponding code in PHP (from memory, let me know if it has any problem):
<?php
// first value:
$value = $_POST["somename"];
// subsequent values:
if( array_key_exists("someid-RC",$_POST)) {
for($i=2;$i < = (int) $_POST["someid-RC"];$i++) {
if( array_key_exists ("somename-".$i,$_POST)) {
$value = $_POST["somename-".$i];
}
}
}
?>
Code sample updated (see comments below)
You’re welcome to share code samples for different environments, or continue reading: how to prefill a form with a repeated element.
Update: Comments are now closed for this post, but you can go to the wForms forum if you have any question or comment. Thanks !
June 24th, 2005 at 6:02 am
Have you considered structures like so for repeating fields….
In python, I’m using formencode to decode form values into values like…
{’name’: [ {’first’:'tom’,’ ‘last’:’smith’}, {’first’:'chloe’, ‘last’:’smith’} ]
June 24th, 2005 at 8:57 am
Kevin, I suppose your html didn’t go through the comment system. Please post again using html entities (< and >) for your markup.
June 25th, 2005 at 12:55 am
Thanks, cedsav.
Have you considered structures like so for repeating fields….
<input name=”name-1.first” value=”tom” > <input name=”name-1.last” value=”smith” >
<input name=”name-2.first” value=”chloe” > <input name=”name-2.last” value=”smith” >
In python, I’m using formencode to decode form values into values like…
{’name’: [ {’first’:’tom’,’ ‘last’:’smith’}, {’first’:’chloe’, ‘last’:’smith’} ]
June 25th, 2005 at 2:10 am
Kevin, thanks for your suggestion. I can see how it simplifies the server-side handling of the form. On the other hand, it is more constraining for the web developer, as he needs to name his fields according to that convention.
wForms requires no particular naming convention and leaves the original field unchanged. This, in effect, forces the developer to write server-side code that works even when javascript is disabled on the client.
June 28th, 2005 at 3:32 am
Original row:
Second row:
Third row:
$v){
do some…
}
?>
June 29th, 2005 at 9:35 am
The command:
for($i=1;$i < (int) $_POST[”someid-RC”];$i++)
should be replaced by:
for($i=2;$i <= (int) $_POST[”someid-RC”];$i++)
The variable $i starting with 2 is good because it’ll never be a “somename-1″ field. The <= instead of < is needed, or the last field will never be reached.
June 29th, 2005 at 10:26 am
Henrique, you’re right. Thanks for pointing that out, I’ll update the documentation.
June 29th, 2005 at 10:29 am
samsmith, by the way, your comment didn’t go through. Can you post it again ?
July 26th, 2005 at 1:16 am
i always worked with data grids in mind, so what about:
<input name=”myRow[0]myField” id=”myRow_0_myField” value=”hello” />
<input name=”myRow[0]myField2″ id=”myRow_0_myField” value=”hello” />
<input name=”myRow[1]myField” id=”myRow_0_myField” value=”hello” />
<input name=”myRow[1]myField2″ id=”myRow_0_myField” value=”hello” />
so you will receive an array, better for iterate trough rows and fields, with all the info you will need, as number of elements.
i developed my own framework in a fashion not very common, grid oriented.
i’ll test your code later, looks interesting…
July 26th, 2005 at 8:28 pm
El_vartauy,
Thanks for your suggestion. I think the [] characters are invalid in (X)HTML in a name attribute. It still works of course, but I’d rather stick to a standard compliant code. The other point is that, as I said in a comment above, I try to make wForms flexible, so no specific syntax is required in the XHTML.
July 27th, 2005 at 10:04 pm
Hey, very, very nice function. Works very well with server side code, both on submit and write.
I incorporated it into my form with the simple business logic that all repeated ‘items’ would be entered
into a single field, using a delimiter to separate. I used ;, but if that does not work given your programming environment,
or the expectation that ; might be entered by user, you can define a ‘delimit’ string
of random characters and use that as the parse point. The relevent code is below…sorry that
I dont have php equivalent….please no angry ASP posts
Lots of ways to slice this, for instance, I allowed the user to change each entry, but you could just print to page
the value and assume that if they want to change it, they could just ‘remove’ then add a new one.
(I hope this works, I ran the code below through a ‘cleaner’ since I wasn’t sure if the code would be allowed without)
(I can post or forward the actual code if anyone needs it native. It should be convertable into PHP pretty easily)
***************************
Build form for submit:
(I’ll assume you have a variable, fld, that represents the data in the column where you store the information)
(fld could also just be some variable you build. Again, ; is my delimiter, could just as well be jije3998t6845hj&U$IUh)
<table>
<%
(Dim fld - already set to column value)
Dim delimit : delimit = ";"
Dim doloop : doloop = false
if fld = "" or (fld <> "" AND InStr(fld,delimit) = 0) then
doloop = false
else
doloop = true
end if
if Not doloop then
%>
<tr class="repeat">
<td>
<select id="fld_unique_name" name="fld_unique_name" class="">
<option value=""><———– Select ———–></option>
<option value="Something One">Something One</option>
<option value="Something Two">Something Two</option>
<option value="Something Three">Something Three</option>
</select><br/></td>
<td></td>
</tr>
<%
else
Dim i
Dim RC
Dim CLS
Dim arrayFld : arrayFld = Split(fld,delimt)
for i=0 to UBound(arrayFld)
RC = "-" & CStr(Eval(i+1))
CLS = "class=""removeable"""
if i = 0 then RC = ""
if i = 0 then CLS = "class=""repeat"""
%>
<tr <%=CLS%>>
<td>
<select id="fld_unique_name<%=RC%>" name="fld_unique_name<%=RC%>" class="">
<option value=""<%if arrayFld(i)="" then Reseponse.write " checked"%>><———– Select ———–></option>
<option value="Something One"<%if arrayFld(i)="Something One" then Reseponse.write " checked"%>>Something One</option>
<option value="Something Two"<%if arrayFld(i)="Something Two" then Reseponse.write " checked"%>>Something Two</option>
<option value="Something Three"<%if arrayFld(i)="Something Three" then Reseponse.write " checked"%>>Something Three</option>
</select><br/></td>
<td></td>
</tr>
<%
next
end if
%>
</table>
***************************
Code after submit:
(I assume here that you have an open and updateable recordset, RS)
(Dim RS ‘as recordset)
Dim delimit : delimit = ";"
Dim fld : fld = Request("fld_unique_name")
if fld = "" then
RS("fld") = null
else
for each item in request.form
if left(item,len("fld_unique_name"))="fld_unique_name" then fld = fld & delimit & request.form(item)
next
RS("fld") = fld
end if
July 29th, 2005 at 8:39 pm
We use Zope for our app server…based on Python.
For forms we can use this:
<input type=text name=formFields.username:record:list>
If I have 3 fields all using the same name I get this returned:
{’formFields’: {’username’: [’Allen’, ‘Bob’, ‘Fred’]}}
This will return a dictionary object named ‘fields’ that contains a dictionary named ‘username’ that contains a list of the usernames submitted.
Looping over this list then eliminates the need to add sequence numbers to the form fields.
I am sure this will work in other languages but simple to do with Zope.
July 30th, 2005 at 1:58 am
Ian, thanks for sharing your ASP code. Glad it works well.
For those who’d like to keep their field name unchanged to benefit from server-side features (php’s [] or Python’s notation, as stated by Allen above), you should be able to do a small modification to wForms:
Around line 242 in wForms.js you’ll find the following line:
// Prepare id suffix
var suffix = “-” + rowCount.toString();
If you change it to:
var suffix = “”;
it should work the way you want but I have not tested it.. so it might turn out to be more complex than that. Let me know if you have any problem with it.
August 4th, 2005 at 3:21 am
Hola, actualmente estoy utilizando el formulario para repetir los datos, pero se me presento que necesito realizar una actualizacion de ciertos datos que tengo almacenados en la base de datos, la pregunta es:
¿Como cargo el formuario con los datos de la base y poder modificar la informacion con este mismo esquema?
Desde ya muchas gracias.
August 4th, 2005 at 3:32 am
Fabricio. Sorry, Yo no hablo espanol.. Are you asking how to populate the form with previously submitted data ?
August 4th, 2005 at 8:19 am
Nowadays I am using the form to repeat the information, but now I need to load some fields from the informartion base. ¿How do I load the form with the information of the base, and be able to modify the information with the same scheme?
Thank yo very much
August 4th, 2005 at 8:38 am
tanks, yes i need populate the form with previously submitted data.
August 4th, 2005 at 8:44 am
sorry, i know the answer.
Thank you very much.
August 18th, 2005 at 2:16 am
Cedsav, EXCELLENT little site! I like it. Could you create a simple, example PHP form processor for me? Or if someone else has one I cold snag and play with. I don’t know what I’m doing
I’m getting confused by the repeat function still, and I’ve read everything you have on it twice! My problem is that I’m new to PHP, and I’m new to forms…
so I’m a little slow… THANKS!
August 18th, 2005 at 3:15 am
Scott,
If you just need to retrieve field names and values, here’s a very basic form processor:
foreach ($_POST as $varname => $varvalue) {
if(is_array($varvalue))
echo ” $varname = “.implode(”, “, $varvalue);
else
echo ” $varname = $varvalue”;
But usually, you know the name of a field and you want to retrieve its value, so you just do:
$value = $_POST[’name_of_the_field’];
The example in the post shows how to retrieve all the submitted values of a field within a wForms Repeated group.
Let me know if you need to populate a form with a repeat element, that’s more complicated but I have an example I can post.
August 18th, 2005 at 10:23 pm
Yes, I am using the repeat element in my form, if you could post an example for me. Thanks!
August 24th, 2005 at 3:13 am
Hey Cedsav, just wondering if you got my last message. Thanks!
August 26th, 2005 at 11:08 pm
Scott, more information is now available here.
October 6th, 2005 at 4:25 pm
wonderful script you have. I would like to incorporate with what I am doing right now
I have two rows with the class=repeat . I use ASP(VBscript) and would want to post the data to a stored procedure in
SQL Server. I am working on in right now but I would earnestly appreciate any Ideas that would direct me in the right direction
especially in the area of catching the posted data when rows are added. thanks in advance
October 10th, 2005 at 3:49 am
like scott has said — has anyone done a generic form processing script which will handle the repeat behaviour? i have a form which has a repeat row containing 5 inputs. when the form is submitted i need to generate an email with each row as a separate line in the email. i know how to do this with static rows as i enter the values in by hand, but not with automatically generated rows! can anyone help me?
October 11th, 2005 at 3:49 am
Matt,
The code sample in this post shows how to retrieve the values of repeated fields. Creating the email’s body is quite similar to what you would normally do.
For instance, let’s you have a repeated input called ‘lastname’.
<div class=”repeat” id=”lastnamerow” >
<input type=”text” name=”lastname” />
</div>
In PHP, you retrieve the value like this:
$lastname = $_POST[”lastname”];
To add that info to your email:
$emailbody .= “Last Name: “. $lastname . “\n”;
To get subsequent values for the same field
if( array_key_exists(”lastnamerow-RC”,$_POST)) {
for($i=2;$i < = (int) $_POST[”lastnamerow-RC”];$i++) {
if( array_key_exists (”lastname-”.$i,$_POST)) {
$lastname = $_POST[”lastname-”.$i];
$emailbody .= “Last Name:” $lastname . “\n”;
}
}
}
November 12th, 2005 at 1:13 am
Would someone like to convert this to ASP?
November 16th, 2005 at 2:12 am
Hi Ced
This looks exactly like what I need for something I have in mind, but although I’m happy working with PHP I’m on shaky ground with javascript.
I’m developing a game which requires users to enter their instructions each turn via a form; I’m hoping you can either explain, or point me to a good place to read up on, how to do the following with your functions:
Using the switch behaviour, I want to be able to load a different set of fields from a database table based on which option the user chooses. Is this achievable, and if so, how?
In the game, the user will have a certain amount of resources to ’spend’ each turn, and each instruction costs a different amount. Is it possible to display a counter which would decrease based on the instructions they choose?
Thanks!
James
November 18th, 2005 at 1:11 am
JamesD.. to answer your first question, wForms doesn’t load parts of the form dynamically. With the Switch behavior you can show/hide sets of fields, but they all need to be loaded with the form.
For your second question, you would have to develop that functionality. It looks pretty specific to your own application.
December 3rd, 2005 at 2:28 am
Any examples in perl to get subsequent values for the same field into an array?
December 8th, 2005 at 4:20 am
Hi Tuck, sorry I’m not a perl coder.. but if you have anything to share, please send it in. If you need help with the logic, I can help you. Just email me.
December 10th, 2005 at 10:16 am
ced,
is there a way to keep the “name=somename” even when the row count increments?
Fantastic work. Thanks a lot.
Tuck
December 13th, 2005 at 8:57 am
Tuck, you would need to modify some code in wForms.js.
From a quick look, I think you’ll need to replace line 327 by line 325
and remove line 353.
(make sure you’ve the last version of wForms: v0.99.24)
I can give you more details if you’re willing to try that.
December 21st, 2005 at 11:32 am
Hi, its a fantastic function…!
but I need to know how many times has been repeated the form, there is some like an attribute or statement to know that?
thanks very much…
December 23rd, 2005 at 1:50 am
cedsav,
I’ve implemented the repeat behavior on a ColdFusion-based site I’m developing, and it works great.
One thing I had to do, was change the row counter from - to _ because CF doesn’t like variable names with dashes in them.
I have one issue that I’d like your advice on: I’m using the tinyMCE rich-text editing tool, and it doesn’t work when “repeated”. It uses ids like mce_1, mce_1_parent, mce_1_bold, etc. When repeated, these become mce_1_2, mce_1_parent_2, etc. I think I can get it to work if the I can get the repeat behavior to not duplicate the span (mce_1_parent) that contains the table and IFrame that hold the MCE editor. If I can repeat only the textarea field, then I can have tinyMCE recreate the editing environment.
Any thoughts on the best way to get the wForms Repeat behavior to not repeat that node?
Thanks, Steve
December 23rd, 2005 at 10:30 pm
Edgar, just read the ‘row count’ paragraph in this post… or maybe I didn’t understand your question ?
Steve,
I’m not familiar with tinyMCE, can you post a link to a test page ?
Maybe you could move the editor out of the repeated section, an use javascript to reposition it at the right place..
December 24th, 2005 at 1:07 am
cedsav,
I posted an example with further explanation here:
http://cecf1.unh.edu/sjtest/tinyMCE_repeat.cfm
Thanks for any ideas you might have. The crux is, I’d like to prevent the repeat behavior from duplicating one particular node and its child nodes (this is the node that the tinyMCE js creates for the WYSIWYG editing iframe.)
December 31st, 2005 at 8:52 am
Steve, thanks for the test case.
I created a customization file for you. Get it here:
tinyMCE / wForms customization file
Load it after wForms.js It works in Firefox. I haven’t tested it with anything else, so let me know how it goes.
January 3rd, 2006 at 12:08 am
Cedric,
Thanks, the customization works great. I’ve posted an updated example that works with your customization at:
http://cecf1.unh.edu/sjtest/tinyMCE_repeat_custom.cfm
View source to see the customization script Cedric provided.
Happy New Year,
Steve
February 7th, 2006 at 2:38 am
Does anyone know if I can implement this using files for the form field types. It seems like implementing this using file fields does not actually upload the file but just sends the tmp image through the string query.
Any ideas how this can be done. I am trying to set up something where people can upload an unlimited number of files in a form and this would be the perfect solution if only files would work correctly….
Thanks
February 11th, 2006 at 4:10 am
Sean, can you post a link to your form ? I’ll check it out