Friday, November 14, 2008

CFSELECT required="true" (Well .. not really)

I was never a big fan of the old html cfforms, and consequently never learned much about them. Though they have probably improved over time. While googling I came across the old question of why the required attribute does not work unless the list supports multiple selections. Having a few minutes to kill I decided to explore it.

The common response was along the lines of "You should not expect it to work. Something is always selected if the list size is one". Technically, that is true. But to me it comes down to the fact that the validation was implemented differently than some people might expect, given how they use select lists. The validation code in /CFIDE/scripts/cfform.js is clearly geared towards multiple selection lists, not single.


// if this form field is a select list
if(_c=="SELECT"){

// verify at least one item was selected
for(i=0;i<_b.length;i++){
if(_b.options[i].selected){
return true;
}
}

return false;
}
...


Now I saw a few examples of tweaking cfform.js to produce the desired results. But they were not exactly what I had in mind. If I were to change the script, I would use "" as the default to represent no-selection. But have an optional attribute called "noSelection". So you could use other values like zero (0), etcetera when needed.


/*
REPLACEMENT JAVASCRIPT
*/
if( _c == "SELECT" ) {
var idx = _b.selectedIndex;
var isValid = false;
var _defaultValue = "";

// use the supplied default value
if ( document.getElementById && _b.getAttribute("noSelection") ) {
_defaultValue = _b.getAttribute("noSelection").toLowerCase();
}

if ( _b.type == "select-one" ) {
// something other than the default value is selected
isValid = ( _b.options[idx].value.toLowerCase() != _defaultValue );
}
else {
// multiple list: at least one item is selected
isValid = ( idx >= 0 );
}
return isValid;
}
...


<!---
Use the default "" to represent no selection
--->
<cfform name="someForm" format="xml" skin="basic">
<cfformgroup type="horizontal">
<cfselect name="Company" label="Company" required="true">
<option value="">-- select ---</option>
<option value="1">Company A</option>
<option value="2">Company B</option>
</cfselect>
<cfinput type="submit" name="submitBtn" value="Submit" >
</cfformgroup>
</cfform>

<!---
Use "0" to represent no selection
--->
<cfform name="someForm" format="xml" skin="basic">
<cfformgroup type="horizontal">
<cfselect name="Company" label="Company" required="true" noSelection="0"
message="Do not pass go. Do not collect $200. Not until you select a Company.">
<option value="0">-- select ---</option>
<option value="1">Company A</option>
<option value="2">Company B</option>
</cfselect>

<cfinput type="submit" name="submitBtn" value="Submit" >
</cfformgroup>
</cfform>

Now I do not really have a need for this. But if I ever do, I now know where to look ;)



11 comments:

Anonymous,  June 23, 2009 at 5:23 PM  

I've been using this code you put up for quite some time on my web server. Makes cfselect's so much easier as I can make them required with a blank initial value without writing my own JS. Thanks!

cfSearching June 28, 2009 at 4:28 PM  

I am glad you found it useful!

Cheers
Leigh

Anonymous,  November 3, 2009 at 2:21 PM  

Just replace the code in cfform with following works like a charm
Orignal
if(_c=="SELECT"){
for(i=0;i<_b.length;i++){
if(_b.options[i].selected){
return true;

New code:
if(_c=="SELECT"){
for(i=0;i<_b.length;i++){
if(_b.options[i].selected && _b.options[i].value != ""){
return true;

cfSearching November 3, 2009 at 3:18 PM  

@Anonymous,

Adding the extra "noSelection" attribute code gives you a lot more flexibility. Then you could use any value to represent no-selection, rather than being limited to just "".

-Leigh

Sushil,  November 4, 2009 at 5:54 AM  

@Leigh

I didn't get what do u mean by adding extra noSelection attribute.

--
Sushil

cfSearching November 4, 2009 at 6:24 AM  

@Sushil,

The javascript above checks the cfselect for an html attribute named "noSelection". If it exists, its value is used to represent nothing selected. So you can use whatever value you want:

Example:
<cfselect required="true" noSelection="" ...>
<cfselect required="true" noSelection="0" ...>
<cfselect required="true" noSelection="ALL" ...>
<cfselect required="true" noSelection="-None-" ...>

-Leigh

sushil,  November 5, 2009 at 6:22 AM  

I got it. Thanks!!!!

--
Sushil

fermin.aguilar May 7, 2010 at 1:35 PM  

My web host wouldn't change the cfform.js nor did the scriptsrc on the cfform tag didn't work. So I used the following on my form page itself:

startscript tag
var _CF_hasValue_old = _CF_hasValue;
_CF_hasValue=function(_b,_c,_d)
{
if (_b.type == 'select-one')
{ var bSelected = false;
for (var i=0; i<_b.options.length; i++)
{
if (_b.options[i].selected == true && _b.options[i].value != '')
{ bSelected = true; break;
}
} return bSelected;
} else
return _CF_hasValue_old(_b,_c,_d);
}
endscript tag

cfSearching May 10, 2010 at 5:21 AM  

@fermin.aguilar,

..nor did the scriptsrc on the cfform tag didn't work

That usually happens when the format selected does not "pass-through" extra html attributes, or at least not for the cfselect element. You can confirm the issue by doing a view source. In those cases, yours is a good work around.

-Leigh

kf,  July 29, 2010 at 9:56 AM  

With cf8, a workaround is binding the selected value to an invisible text input for validation.

<SELECT NAME="dept_select">
<OPTION VALUE=""> -- Select -- </option>
<OPTION VALUE="1">1</option>
<OPTION VALUE="2">2</option>
</SELECT>
<CFINPUT NAME="dept"
TYPE="Text"
STYLE="visibility:hidden"
BIND="{dept_select}"
REQUIRED="Yes"
MESSAGE="Please input Department!">

cfSearching July 29, 2010 at 11:45 AM  

@kf

Very slick idea! I did notice one problem in IE8. CF attempts to focus() the hidden field and of course IE complains:

"Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus."

Sometimes I hate IE ...

-Leigh

  © Blogger templates The Professional Template by Ourblogtemplates.com 2008

Header image adapted from atomicjeep