views:

904

answers:

8

What is the best way to automatically set a selected item for a select/option element on post-back? Here's the way we're currently doing it:

<select id="grade" name="grade">
  <option value="A"<cfif form.grade = 'A'> selected="selected"</cfif>>A</option>
  <option value="B"<cfif form.grade = 'B'> selected="selected"</cfif>>B</option> 
  <option value="C"<cfif form.grade = 'C'> selected="selected"</cfif>>C</option> 
  <option value="D"<cfif form.grade = 'D'> selected="selected"</cfif>>D</option> 
  <option value="F"<cfif form.grade = 'F'> selected="selected"</cfif>>F</option>
</select>

Is there a cleaner or easier way to do this with ColdFusion? Thanks in advance!

+1  A: 

Like this:

<cfsavecontent variable="GradeOptions">
A:A
B:B
C:C
D:D
F:F
</cfsavecontent>

<select id="grade" name="grade">
    <cfloop index="CurOpt" list="#trim(GradeOptions)#" delimiters="#Chr(10)#">
     <option value="#ListFirst(CurOpt,':')#"<cfif form.grade EQ ListFirst(CurOpt,':')> selected="selected"</cfif>>#ListRest(CurOpt,':')#</option>
    </cfloop>
</select>


That assumes you always have separate value:label information - if your value and label are always the same, you can do this:

<cfsavecontent variable="GradeOptions">
A
B
C
D
F
</cfsavecontent>

<select id="grade" name="grade">
    <cfloop index="CurOpt" list="#trim(GradeOptions)#" delimiters="#Chr(10)#">
     <option<cfif form.grade EQ CurOpt> selected="selected"</cfif>>#CurOpt#</option>
    </cfloop>
</select>
Peter Boughton
+2  A: 

You could also do this with an array of structs.

<cfparam name="form.grade" default="C">
<cfset mydata = [{grade="A",value="A"},{grade="B",value="B"},{grade="C",value="C"},{grade="D",value="D"},{grade="F",value="F"}]>
<cfoutput>
<select id="grade" name="grade">
    <cfloop array="#mydata#" index="i">
        <option value="#i['value']#"<cfif form.grade EQ i['grade']> selected="selected"</cfif>>#i['value']#</option>
    </cfloop>
</select>
</cfoutput>
rip747
A: 

What about this?

<cfparam name="form.grade" default="A">
<cfoutput>
<select id="grade" name="grade">
<cfloop index="code" from="65" to="90">
    <option value="#Chr(code)#"<cfif form.grade EQ Chr(code)> selected="selected"</cfif>>#Chr(code)#</option>
</cfloop>
</select>
</cfoutput>

A bit tricky, yeah :)

Sergii
I'm not sure why one might want to use character codes, but if you're going to, you should:You should state that it's intentional doing A-Z here.For readability you could do from="#Asc('A')#" to="#Asc('Z')#"
Peter Boughton
Yeah, you're right. This makes code a bit more readable. Just wanted to show the idea.
Sergii
A: 

using cfscript with functions

<cfscript>
Function setSelected(val1, val2){
    if (val1 EQ val2)
    {
     Return 'selected="selected"';
    } 
    else 
    {
     Return '';
    }
}
</cfscript>
<select id="grade" name="grade">
  <option value="A" #setSelected('A', form.grade)#>A</option>
  <option value="B" #setSelected('B', form.grade)#>B</option> 
  <option value="C" #setSelected('C', form.grade)#>C</option> 
  <option value="D" #setSelected('D', form.grade)#>D</option> 
  <option value="F" #setSelected('F', form.grade)#>F</option>
</select>
Jason
Hmm... and what is the _real_ difference between this and original ways? :)
Sergii
Eugh! The difference is that the original is just repetitive, whilst this is both ugly and wrongly named.
Peter Boughton
I think the user was looking for a better way to do the cfif inside the the option tag, not how to put the options in a list and loop through them. If you have better (or just other ways) of doing the cfif then I would also like to hear them.
Jason
What the user (probably) wants is an easier to read and maintain way of doing selects. Both this and the existing code fails on both those counts. Looping over a list/array/query passes on both counts.
Peter Boughton
A: 

You know, it may be unpopular to say so, but the solution that you originally outlined in the question is the best.

It's simple, it's easy to see what is being done, and it's not trying to be tricky just to be tricky.

Sometimes you just need to wear gloves

http://thedailywtf.com/Articles/The_Complicator_0x27_s_Gloves.aspx

Dan Cramer
Alex
A: 

To expand on my other answer, what you should be doing is properly separating your data from your interface, by storing the grades in your database, and using code similar to:

<cfset GradeOptions = Grades.readAvailable() />

<select id="grade" name="grade">
    <cfloop query="GradeOptions">
     <option value="#GradeCode#"
      <cfif Form.Grade EQ GradeCode>selected="selected</cfif>
      >#GradeCode# - #GradeDesc#</option>
    </cfloop>
</select>

(Again, if grades are only to be treated as a single letter, the values can be provided in a simple list/array rather than query.)

The key thing is, you can change your grade structure without having to change your interface, and likewise updating the interface doesn't require you to know what the grades are.

Peter Boughton
While I agree with you in most cases, these are grades. A,B,C,D,F. They haven't changed in generations. Do they really need to be stored in a database? Really? It would be like using a database to store the number of days in a week, in case it changes.
Joel Mueller
@Joel, we don't know how general this question is. Maybe for A-Z range even my answer is shortest and for someone can be better. Peter's (and rip747) answers simply shows most flexible way, when select can contain more dynamic data. Why not?
Sergii
WTF!? Of course grades change!Some places use A,B,C,D,F but not all do. I remember having A,B,C,D,E,F,G - where there was no 'failure', but D,E,F,G were bad (for me).Then, for later exams, my school used the set A,B,C,D,E,N,U (Near-miss and Unmarked being the two 'fail' grades).At uni we had A1,A2,B1,B2,C1,C2,D1,D2,E1,E2 (with descriptions of each).And then, the final BSc Hons grades - 1st,2:1,2:2,3rd,Pass,Fail.And that's just me, I'm sure there are other systems out there.
Peter Boughton
A: 

To be honest I can't see how any of these are better than a corrected version of your initial pass (below)

   <select id="grade" name="grade">
      <option value="A"<cfif form.grade EQ "A"> selected </cfif> >A</option>
      <option value="B"<cfif form.grade EQ "B"> selected </cfif> >B</option> 
      <option value="C"<cfif form.grade EQ "C"> selected </cfif> >C</option> 
      <option value="D"<cfif form.grade EQ "D"> selected </cfif> >D</option> 
      <option value="F"<cfif form.grade EQ "F"> selected </cfif> >F</option>
   </select>

It is simple, clean and understandable.

If you just feel a need to be slicker and are going to be doing a lot of UI manipulation invest some time in jQuery. Study Ray Camden's jQuery and CF Posts and Ben Nadel's Javascript and CF Posts and soon this will be second nature...

<script type="text/javascript">                                         
  jQuery(document).ready(function() {
     $("#grade option[value='<CFOUTPUT>#FORM.Grade#</CFOUTPUT>']")
      .attr('selected', 'selected');
  });
</script>

<select id="grade" name="grade">
  <option value="A">A</option>
  <option value="B">B</option> 
  <option value="C">C</option> 
  <option value="D">D</option> 
  <option value="F">F</option>
</select>

Sure, it's wackier looking than some of the other options here but amazingly powerful at solving problems that CF just isn't good at once you learn it (trust me, it will quickly make sense and you will wonder how you ever did client UI code without it).

Learn any of the popular JavaScript libraries and your ColdFusion client side code will become dramatically more elegant and powerful.

kevink
+3  A: 

In my opinion, one of the best ways to go is to use a CFSelect:

<cfquery name="getGrades" datasource="#application.dsn#">
  select gradeLetter from Grades
</cfquery>

<cfselect
  name="grade"
  query="getGrades"
  display="gradeLetter"
  value="gradeLetter"
  selected="#form.grade#" />
Adam Tuttle
Very clean - I like that you only have to use the "selected" command once in this scenario, no matter what the data. Are there any concerns with the HTML output of cfforms with regards to accessibility and valid code?
Alex