Earlier this week a user asked me to look into something odd with CF901's new multirowselect feature for cfgrid. If you haven't played with this yet, it is a way to enable multiple row selections in a grid. Unfortunately it doesn't quite work as advertised, but in this blog entry I'll tell you how to make it work.
First, let's start with a simple example so you can see what the attribute does when enabled.
<cfform name="mytest" method="post">
<cfgrid name="SelectStuff" query="q" format="html" width="400" height="250" multirowselect="true"></cfgrid>
<cfinput type="submit" name="submit" value="Submit">
</cfform> <cfdump var="#form#">
<cfset q = queryNew("id,name")>
<cfloop index="x" from="1" to="10">
<cfset queryAddRow(q)>
<cfset querySetCell(q, "id", x)>
<cfset querySetCell(q, "name", "Name #x#")>
</cfloop>
My code begins with a quick fake query on top. Next I've got a cfform with my grid inside. The only thing really interesting there is the multirowselect. I also added a quick dump of the form scope to the bottom. Let's take a look at how the grid is changed. I'll first show a picture with the option turned off, this is the default:
Now let's turn the option back on, as in the code above.
As you can see, there is a new column now with checkboxes. There is also a checkbox on top. Clicking that works as a Select All/Deselect All feature. So in theory, that should be it, right? Unfortunately, it completely doesn't work as shown above. If I click a few checkboxes and hit submit, I get this in the form scope.
Nothing. Ugh. So I pinged Adobe on this. Turns out - the real expectation for this feature was within Ajax-based applications. You can get the value just fine via JavaScript, but if you don't do this, nothing will be sent to the server. I've already filed a bug report on this.
So how can you make this work? The simplest solution is to use the getSelectedRows API:
obj = ColdFusion.Grid.getSelectedRows('SelectStuff');
This returns a struct of objects. How do you send that to the server? One option would be to turn into JSON:
jsonbj = ColdFusion.JSON.encode(obj);
However, this will give you a JSON representation of the entire row. You probably only want the ID values, right? Here is the code I came up:
var selected = "";
for(var i=0; i<obj.length; i++) {
if(selected == "") selected = obj[i].ID;
else selected += "," + obj[i].ID;
}
document.getElementById('selected').value = selected;
Basically - create a list of IDs from the object and assign it to a new form field, in this case, a hidden one. You can try this yourself via the demo link below, and I've pasted the entire completed template below.
<cfset q = queryNew("id,name")>
<cfloop index="x" from="1" to="10">
<cfset queryAddRow(q)>
<cfset querySetCell(q, "id", x)>
<cfset querySetCell(q, "name", "Name #x#")>
</cfloop> <script>
function fixMe() {
obj = ColdFusion.Grid.getSelectedRows('SelectStuff');
var selected = "";
for(var i=0; i<obj.length; i++) {
if(selected == "") selected = obj[i].ID;
else selected += "," + obj[i].ID;
}
document.getElementById('selected').value = selected;
return true;
}
</script> <cfform name="mytest" method="post" onSubmit="return fixMe()">
<cfgrid name="SelectStuff" query="q" format="html" width="400" height="250" multirowselect="true"></cfgrid>
<input type="hidden" name="selected" id="selected">
<cfinput type="submit" name="submit" value="Submit">
</cfform> <cfdump var="#form#">
Archived Comments
I have a form page with the kind of code you suggest and a post page. The form page sends the field "selected" and it send the correct number of values according to the multirowselect number of checked rows, but the values are all "undefined" whereas they should be ID integers. I include the script, grid code, and debugging info:
<!-- http://www.raymondcamden.co... -->
<script>
function fixMe() {
obj = ColdFusion.Grid.getSelectedRows('chron_grid_master');
var selected = "";
for(var i=0; i<obj.length; i++) {
if(selected == "") selected = obj[i].HistoryMasterID;
else selected += "," + obj[i].HistoryMasterID;
}
document.getElementById('selected').value = selected;
return true;
}
</script>
<table class="tableelements" width="100%" align="center" cellpadding="5" border="0">
<tr>
<td align="center">
<cfform action="chron_action_finishgrid.cfm" method="post" onSubmit="return fixMe()">
<cfgrid name="chron_grid_master" query="McCVersion" rowheight="20" width="720" colheaderalign="center" collapsible="true" groupfield="ColumnName" fontsize="14" format="html" title="Grouping of Historical Events (collapsible)" multirowselect="Yes">
<cfgridcolumn name="HistoryMasterID" display="no">
<cfgridcolumn name="EventYear" header="year" width="40">
<cfgridcolumn name="MonthText" header="month" width="45">
<cfgridcolumn name="ColumnName" header="focus" bold="yes" width="50">
<cfgridcolumn name="CategoryShortName" header="group" width="45">
<cfgridcolumn name="ChronEvent" header="Albany event" headerbold="yes" width="500">
</cfgrid>
</td>
</tr>
<tr>
<td align="center">
<cfinput type="hidden" name="selected" id="selected">
<input name="submit" type="submit" value="Submit">
</cfform>
</td>
</tr>
</table>
Form Fields:
CHRON_GRID_MASTER=
FIELDNAMES=SELECTED,SUBMIT,__CFGRID__CFFORM_2__CHRON_GRID_MASTER
SELECTED=undefined,undefined,undefined
SUBMIT=Submit
__CFGRID__CFFORM_2__CHRON_GRID_MASTER=
It looks right to me. Try this.
After the "for" statement, add:
console.dir(obj[i])
and look in your JavaScript console. Tell me what you see.
If I place console.dir(obj[i]) within the script, I lose the submission of undefined, undefined, etc. in "selected" If I put it outside the script, I get a page error "console undefined." So I added a piece of script I found on Stackoverflow. In all cases (because Java workings are new to me) I cannot get the Java Console to stay open or to stick in the system tray to report on fixMe(). Here is the script at present:
<script type="text/javascript">
function fixMe() {
obj = ColdFusion.Grid.getSelectedRows('chron_grid_master');
var selected = "";
for (var i=0; i<obj.length; i++) {
if(selected == "") selected = obj[i].HistoryMasterID;
else selected += "," + obj[i].HistoryMasterID;
}
document.getElementById('selected').value = selected;
return true;
}
// http://stackoverflow.com/qu...
// entry by AndyE
if (typeof console == "undefined") {
window.console = {
log: function () {}
};
}
console.log("fixMe");
</script>
The console undefined message means you are using a browser that does not support the JavaScript console. You should switch, at least temporarily, to one that does. Any modern Firefox, Chrome, Safari, or IE9 and higher will be fine. You also want to add a return false right after. That stops the form from submitting and allowing you to see what was printed to the console.
Switched from IE 8 to Firefox 16.0.1
No longer getting page error "console not defined"
Submission string for selected still "undefined, undefined, etc.
Java Console stays open. Logging set to true.
Nothing recorded in console upon page submission. Script is now:
<script>
function fixMe() {
obj = ColdFusion.Grid.getSelectedRows('chron_grid_master');
var selected = "";
for(var i=0; i<obj.length; i++) {
if(selected == "") selected = obj[i].HistoryMasterID;
else selected += "," + obj[i].HistoryMasterID;
}
document.getElementById('selected').value = selected;
console.dir(obj[i])
return true;
}
</script>
Change the last return true to false. This will temporarily stop the form from submitting, but you will see something in the JavaScript console.
Actually, return true or return false, nothing is written to the Java console, whether it is open or closed. Perhaps there is a problem in the environment (XP) or CF settings that I don't see or understand. We can let it drop unless you would like to see the whole page or have another diagnostic tool I could try.
To be clear, are you using the JavaScript Console? That is not the same as the "Java" Console.
No, I was using Java console. I opened Javascript Console, got the key, inserted the key into the page, edited the function return to false, updated the page, called it, pressed submit, got confirmation of connection, and here is the log:
remote console.loglink
undefined
JOhn, I have no idea what you are talking about in terms of 'key'. These posts may help:
http://www.raymondcamden.co...
http://www.developria.com/2...
referring to your line 23:
<input type="hidden" name="selected" id="selected">
Is "selected" defined in your CSS?
It's a hidden form field simply used to store data.
No, I actually was wondering to what the id="selected" was referring within the line: <input type="hidden" name="selected" id="selected">
BUT, using Firebug [thank you for that tip], I have pinpointed the problem for me within the function fixMe()
1) getSelectedRows works fine
2) obj[i] shows correctly in this line:
if(selected == "") selected = obj[i].HistoryMasterID;
AS the column names and data of the selected rows and shows IN THE ROWS the two HistoryMasterIDs with their exact values in those selected rows.
3) however, the second part of obj[i].HistoryMasterID in that same line, namely the HistoryMasterID, shows "undefined"
4) so, for some reason, the function gets the rows and shows the correct column names and values, but does not pluck the value from obj[i].HistoryMasterID
Issue solved. In Lines
if(selected == "") selected = obj[i].HistoryMasterID;
else selected += "," + obj[i].HistoryMasterID;
HISTORYMASTERID must be in all caps
"selected" then has the primary key values I wanted and they are sucessfully posted to the action page.
Many many thanks for this fixMe() script
In terms of IDs, it is a way to uniquely identify a part of the DOM. It doesn't relate to anything else. Consider this HTML:
<div id="region1"></div>
<div id="region2"></div>
The ID values must be (should be) unique and not repeated. The ID then gives me an easy way to "address" it via JavaScript.