By Peter Bell

Implementing Value Lists

Whenever I build an application, one of the requirements is to support the concept of "value lists" for populating drop downs, lists of radio buttons and the like. I created an approach to handling this a few years back with my procedural generator which has worked out really well for me and I wanted to document that as well as updating it to work with service layers and DAOs (the old system had queries built into the value list scripts). Any feedback appreciated . . .

[More]

Comments
Just a thought... as this would be part of a form field, perhaps there would be some sort of form validation. Let's say someone selected a billing address state that was not valid (for instance, maybe the country-state combo does not validate). In that case, the form has to refresh.

Now, the tricky part - on the first form load, the value User.get("BillAddressState") would be totally fine. However, on the second form load, this value would not be appropriate as it would not reflect the previous form selection (but would instead reset to the default form value). Does that make sense?

How do you handle the "selected" form value changing form form load to form load if the "Value" in question is not persisted in an object (ie. you wouldn't re-init the User object with the current form values - User.set("billaddres...", form.billaddres...) - as the user's billing value might not be the property in question such as in an order checkout page).
# Posted By Ben Nadel | 3/22/07 3:17 PM
Hi Ben,

My flow on "returning" pages (where a form is being submitted is to load the form values into the appropriate object and run validations in it, so the controller looks something like:

User = UserService.new();
User.loadStructure(form);
Valid = User.validate();
If Valid
xxx
Else
yyy

And because I pass User back to the form, all of the form fields are automatically prepopulated with the appropriate values.

Make sense?
# Posted By Peter Bell | 3/22/07 3:56 PM
It does and it doesn't. I am sure the doesn't stems from not really having my head wrapped around OOP yet. I can understand the User load and validate when you are doing a form that CRUDs a User... but what about when you have a form that has secondary user info but is not really *about* the user.

Take a silly example: a shipping service web site where you have to select your country / state before you enter the site (so that they can adjust shipping weights and info for your locale). Now, let's say that country / state combo needs to be validated. This form really has nothing to do with a user object (arguably). So, for that kind of a scenario, where is the state / country kept? would you still create some sort of "Address" object, load the form into that, and then validate it:

Address.Load( form ).
Address.validate().
# Posted By Ben Nadel | 3/22/07 4:04 PM
On second thought... don't bother answering that... this has gone astray... it is now more about my understand OOP and NOT about value lists. My bad.
# Posted By Ben Nadel | 3/22/07 4:05 PM
Hi Ben, Yeah, an address object or any other kind of object. The value list just exists. Any object can have a property (perhaps called "State") that has a data type of ValueList:USState, and whether it is the User object or the Address object or whatever other object, that will provide the default values and the validations and will handle the passing of the selected values back to the form. I use a base controller to do all of the heavy lifting so you don't even need to write the code for each object - you just need to let the object know which of its properties it should look for from the form and the base code does all of the actual work.
# Posted By Peter Bell | 3/22/07 4:24 PM
I hope I will understand eventually :) Still cultivating the OOP areas of my brain.
# Posted By Ben Nadel | 3/22/07 5:28 PM
Hey Peter, care to share the actual CFC? I'd like to use it.
# Posted By Doug Boude | 3/23/07 9:59 AM
One question came to mind regarding this CFC...did you make provision for creating the list with some values already selected?
# Posted By Doug Boude | 3/23/07 10:07 AM
Hi Doug,

Sorry - this one is kind of mired in the middle of my system so it is pretty dependent on a set of base classes and the like so I can't really pull it out. Sorry about that!

Yes - it allows for a comma delimited list of selected values. In an "add" situation, it'd take the default values from the bean. In an edit situation, it'd use the existing values.
# Posted By Peter Bell | 3/23/07 11:25 AM
Hi Peter,

I want your job! I am always thinking about ways to abstract everything to the nth degree in the way that you do, but I am too bound up in needing to get things done...

One idea I had was to represent a value list using a struct of arrays, where the name of the struct is the name of your list eg "products", each key is a field name (productname, productid etc.), and the value of each key is an array with two elements: the first is the field value, and the second is "isSelected." This struct would be able to take care of most of your dsl as described in your post. I haven't gotten around to trying it yet but in theory it should work.

Keep rockin' your blog, I learn a lot here.
# Posted By Josh Nathanson | 3/27/07 12:06 AM
Hi Josh, Thanks for the comment! I also have to get things done sometimes - it's an ongoing battle to balance product with projects . . .

Interesting approach - will have to think about that more, and glad you're enjoying the blog!
# Posted By Peter Bell | 3/27/07 1:03 AM
Only thing I'd consider is that if you wanted to put value list in struct, consider just putting name: value pairs into the struct rather than an array of tuples and have a separate SelectedValueList comma delimited list. I think the code would be easier. Only other thing to remember is that the values might not always be valid variable names so you'd be looking at a struct.["#ValueName#"] syntax rather than struct.#valuename#. I actually put my value lists in an Iterating Business Object which works well for me, but it could be argued to be more overhead than required for what they do . . .
# Posted By Peter Bell | 3/27/07 10:31 AM
Hi Peter,

It was a little late last night, so I thought I'd try to clarify my approach using your states example.

Suppose you already had your query with your name/value columns, and your selectedlist. To load up your "valueStruct" you'd do something like:
<cfset var s = structNew() />
<cfloop query="q">
<cfset s[name] = arrayNew(1) />
<cfset s[name][1] = value>
<cfset s[name][2] = iif(ListFindNoCase(arguments.selectedlist,name),'true','false')>
</cfloop>
<cfreturn s />
Your valueStruct is all loaded up and ready to go.

Then to output, for example a dropdown:
<cfset s = ValueStruct.get(params to your method) />
<select name="states">
<cfloop collection="s" item="statename">
<option value="#s[statename][1]#" selected="#s[statename][2]#">#s[statename]#</option>
</cfloop>
</select>

The nice thing is that since everything is associated in the struct you don't have to worry about name/value mapping. Your point about valid variable names is certainly valid. I think you could do something like replacing whitespaces with a delimiter of some sort on the way into the struct, and then remove them on the way out to overcome that limitation.

The other possible downside would be the ordering of the struct keys, not exactly sure how that would play out. You generally don't want your states coming out any other way than alphabetically. I haven't messed with ordering a struct so I'm not sure how that would be handled.
# Posted By Josh Nathanson | 3/27/07 11:34 AM
Actually, the ordering issue would kill the approach for me. I use value lists for both static and dynamic value lists including lists of categories, indented lists of pages, etc. Very seldom are they in alphabetical order. I know you could work around it, but . . .

still, interesting approach!
# Posted By Peter Bell | 3/27/07 12:25 PM
OK, I thought of another way...I'm a tenacious bugger!

How about a 2-dimensional array - the first dimension would have the same number of elements as the value list, the second dimension would have 3 elements. Something like

states[1][1] = "Arkansas"
states[1][2] = "AR"
states[1][3] = false

states[2][1] = "California"
states[2][2] = "CA"
states[2][3] = true

etc.

This handles both the variable naming issue, since you are not using the name as a struct key or array index, and the ordering issue, since the array would maintain whatever order you set. The code to load up and output the array would be similar to the struct idea, just looping over an array instead of a struct.
# Posted By Josh Nathanson | 3/27/07 12:58 PM
It is an approach! Personally I drop the value list into an object which hides the implementation details from the rest of my code so if I chose to vary how I stored the value lists, they would present a consistent interface, but the array would work!

Always good to look at different solutions :->
# Posted By Peter Bell | 3/27/07 2:34 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.005.