Storing lots of input fields in one list

Jake Howlett, 21 May 2001

Category: Forms; Keywords: inputs multi value

How many times have you created a form that has a collection of fields that all gather information that is related. What I mean by this is something similar to the form shown in the image below. Here, all the fields are related by the fact that they gather data for a list of "Results" and have field names like Results1, Results2, Results3 etc.

Image

In design mode a form like this will look something like the screen below. Designing it is a laborious task of adding field after field. Changes to the form can be just as time consuming.

Image

It is not uncommon for forms like this to have groups of fields where there are 30, 40, 50 or more in each. Nothing wrong with this in itself. It is just that whenever I find myself doing it there is always that nagging voice at the back of my head saying "What are you doing? There must be another way!".

Is there another way?

There is indeed. What I'm about to describe makes use of this little known fact: you can have a number of text-input fields in a web form all with the same name. Consider the HTML from a form like below:

Result 1: <input type="text" name="Results"><br />
Result 2: <input type="text" name="Results"><br />
Result 3: <input type="text" name="Results"><br />
Result 4: <input type="text" name="Results"><br />

I'm not sure whether or not this is "legal" in the HTML specification but Domino doesn't have a problem with it, as long as there is a Notes Field on the form with the same name. The trick is to hide this Notes field and make it Multi-Valued. When the form is submitted Domino adds the value from each of the individual input fields as a new entry in the list in the Notes field.

So, how would we design a form like this in Domino Designer. Well, the screen below shows a table that contains a "Results List". In it we have used pass-thru HTML to create an input field with the same name in each cell.

Image

Now let's take another look at the first image that shows the form as it will appear in the browser. Notice that, for the sake of demonstration, I have added values to some of the fields. Let's see how Domino stores this data. After submitting the above form and opening up the Document Properties for the resulting document we see something like this:

Image

Notice that there is only one field called Results stored in the document rather than an ugly looking list of fields, all with the similar names. The values stored in this field match those entered, with blank entries where we left the input elements empty. We could use @Trim in the field's Input Translation to remove them. However, as we shall later, these blanks are an essential part of the way this will work.

Wait a minute, that won't work!

Indeed, the more astute amongst you, who haven't already dismissed this as tosh and stopped reading, may well have noticed there is a problem with the above method. It's all very well creating all these empty input fields when they are on a new form but what happens when we open the newly created document in read mode!? In the above case all we will get is a set of empty fields and no data. The field that is storing the information is hidden and the design doesn't take account of displaying it.

What we need to do is take account of three different scenarios, each involving the use of different HTML:

  1. Creating a New Document
  2. Reading a Document
  3. Editing a Document

So, how else can we do it?

First thing to do is replace the table with a Computed Text area. We can then use @Formulae to dynamically create the HTML depending on the context that the document is in. Take a look at the form as it appears in the screen-shot below:

Image

Notice that we are creating the original table ourselves* using HTML. We now need a formula that will send the right HTML to the browser at the right time. We are going to use this formula in the computed text area:

REM "This formula dynamically creates input fields";

REM "Make sure you have a multi-value Notes field with the same name as the following variable";
FieldName := "Results";
FieldCount := 26;
FieldLabel := "Result ";
FieldValues := Results;

ZeroToNine := "0" : "1" : "2" : "3" : "4" : "5" : "6" : "7" : "8" : "9";
ZeroToNinetyNine := ZeroToNine *+ ZeroToNine;
ZeroToFieldCount := @Subset( ZeroToNinetyNine; FieldCount);

REM "Create the HTML";
HTML :=
@If(
@IsNewDoc;
FieldLabel + @Text( @TextToNumber(ZeroToFieldCount) + 1 ) + "</td><td><input type=\"text\" name=\"" + FieldName + "\">";

@IsDocBeingEdited;
FieldLabel + @Text( @TextToNumber(ZeroToFieldCount) + 1 ) + "</td><td><input type=\"text\" name=\"" + FieldName + "\" value=\"" + FieldValues + "\">";

FieldLabel + @Text( @TextToNumber(ZeroToFieldCount) + 1 ) + "</td><td>" + FieldValues
);

REM "Send the HTML to the browser";
@Implode( "<tr><td>" + HTML; "</td></tr>" + @NewLine )


Don't worry it's not quite as complicated as it first appears. Let's examine the code piece by piece:

FieldName := "Results";
FieldCount := 26;
FieldLabel := "Result ";
FieldValues := Results;

The first four lines simply set up a few constants. This is the only part you should need to change between uses unless you want to make changes to the HTML attributes of the individual rows and cells.

ZeroToNine := "0" : "1" : "2" : "3" : "4" : "5" : "6" : "7" : "8" : "9";
ZeroToNinetyNine := ZeroToNine *+ ZeroToNine;
ZeroToFieldCount := @Subset( ZeroToNinetyNine; FieldCount);

This is the key part of the formula. The Permuted Addition operator (*+) acts on the two lists, pairing every possible combination of their values. The resulting list has an element for each pairing in the following order: list 1 element 1 paired with each element in list 2, list 1 element 2 paired with each element in list 2, and so on through the last element in list 1. The result is a list of 100 items: 00, 01, 02, 03 through 99. The final variable has the same number of items as the number of fields we require.

HTML :=
@If(
@IsNewDoc;
FieldLabel + @Text( @TextToNumber(ZeroToFieldCount) + 1 ) + "</td><td><input type=\"text\" name=\"" + FieldName + "\">";

Here the form is being newly created and all we need to send to the browser is the empty fields. To do this we assign the HTML variable with a list of rows equal to the ZeroToFieldCount variable. The beauty of the formula language it that we can add HTML to every item in a list simply by permuting each.

@IsDocBeingEdited;
FieldLabel + @Text( @TextToNumber(ZeroToFieldCount) + 1 ) + "</td><td><input type=\"text\" name=\"" + FieldName + "\" value=\"" + FieldValues + "\">";

When the document is in edit mode it is slightly different to a new form. We now need to assign the stored data to the "value" attribute of the inputs. This is why the blank entries were so important earlier on. This way the values are correctly assigned to the right rather than Result1 having Result2's value because Result1 was originally blank.

FieldLabel + @Text( @TextToNumber(ZeroToFieldCount) + 1 ) + "</td><td>" + FieldValues
);

Finally when the form is in read mode we have no need for the input fields and simply send the data to each cell.

REM "Send the HTML to the browser";
@Implode( "<tr><td>" + HTML; "</td></tr>" + @NewLine )

The last step of the formula returns the HTML to the browser. We need to implode each value so that we return a string rather than a list. It is in this implode action that we append the HTML to close the last cell and then the row. The @NewLine is purely to keep the HTML source tidy.

But what about back-end access to this data!?

So we've got a document and it's storing all the values but what about when we need access to this data. This depends on how you want to get access to the values.

If you were using Formula language you can get access to any particular value with the following line:

REM "Get value stored for Result 6";
@Subset( @Subset( Results; 6); -1);

If you were using LotusScipt then you could use code similar to the following:

Dim results as Variant
Dim result6 as String
results = doc.Results
'Note: results is a zero-based array
result6 = results(5)

Fancying yourself as a true developer and using Java you would get the 6th Result using:

Item Results = dc.getFirstItem("Results");
String Result6 = (String)Results.getValues().elementAt(5);

When using JavaScript we need to send an array of values to the browser. We can do this with the following formula in the HTMLHead:

"<script>" + @NewLine +
"<!-- " + @NewLine +
"var Results = new Array();" + @NewLine +
@Implode( "Results[Results.length] = " + Results + ";" ; @NewLine) + @NewLine +
"// -->" + @NewLine +
"</script>"

Then any JavaScript functions can access value 6 using the following line:

alert( Results[5] );

if the document is in edit mode then JavaScript can access the values directly, using this syntax:

document.forms[0].Results[5].value


In conclusion

Yet again I have discussed a technique that, at the end of the day, is probably of no use to man nor beast. Admittedly it may make your life easier and your form tidier but it also has a few things about it that will stop some people from using it. Nevertheless, as with most of articles, I use them to try and get people to read between the lines and pick up on little techniques that, when combined, make Domino an extremely powerful tool....

One possible use of the method is in an Address Form where you have Name, Street, Address Line 1, Address Line 2 etc. Here you could just store it all in one field called Address!




*If you haven't guessed already this approach is only applicable to web-only applications. Sorry but that is all I seem to do nowadays. Can't remember the last time I used the Client ;)