Dynamic Column Output (Part Two)

Creating Dynamic Columns part 2 (Read Part One here)

Earlier I showed you how to output your dynamic content across specified columns within a row.

It was a fairly straightforward example that showed the basics behind the trick.

However, being basic, it left a few things to be desired. Specifically, if you were outputting across 5 columns, and the final row only contained two records, the rest of the row was not populated with the 3 remaining
<td></td>?s that it should have contained. 

If you were outputting this data in a bordered table, you?d see that there were no table cells in the bottom right corner?just a solid block.

The following example is more of a ?full tutorial? on a more proper way to specify a number of columns, and still maintain a ?well-formed? HTML document.

(you can see this code live at http://charlie.griefer.com/dyn_cols.cfm)

**********************************
<cfquery name="getData" datasource="xxx">
    SELECT myValue
    FROM dynamicCols
</cfquery>

<cfscript>

    if (NOT structKeyExists(form, 'cols')) form.cols = 4;
</cfscript>

<cfif getData.recordCount MOD form.cols>
    <cfset variables.rows = int(getData.recordCount/form.cols) + 1>
    <cfset variables.pads = form.cols - (getData.recordCount MOD form.cols)>

    <cfloop from=
"1" to="#variables.pads#" index="i">
        <cfset temp = queryAddRow(getData)>
        <cfset temp = querySetCell(getData,
'myValue', '&nbsp;')>
    </cfloop>
<cfelse>
    <cfset variables.rows = getData.recordCount/form.cols>
</cfif>

<cfset variables.thisRow = 1>

<cfset variables.newrow = false>

<form action="4cols.cfm" method="post">
Columns per row: 
<select name="cols" onchange="this.form.submit();" style="font-family:verdana; font-size:11px;">
    <cfloop from="1" to="10" index="i">
        <cfoutput><option value="#i#"<cfif i EQ form.cols> selected</cfif>>#i#</option></cfoutput>
    </cfloop>
</select>
</form>

<table style="border:0px; background-color:#000000;" cellspacing="1">
    <tr>
       
<cfoutput query="getData">
            <cfif variables.newrow IS true><tr></cfif>
           
<td style="background-color:##ffffff; text-align:right;">#myValue#</td>
           
<cfif (getData.currentRow MOD form.cols EQ 0) AND (variables.thisROW LT variables.rows)>
               
<tr>
               <cfset variables.newrow = true>

              
<cfset variables.currentRow = variables.currentRow + 1>
            <cfelse>
               <cfset variables.newrow = false>

           
</cfif>
        </cfoutput>

    </tr>
</table>

*********************************

Now to break it down:

<cfquery name="getData" datasource="xxx">
    SELECT myValue
    FROM dynamicCols
</cfquery>

This is our query. Previous example used a list, and looped over the list content (for the sake of simplicity). For this example, I wanted to show it as you?d be using it. This query will return 22 records. The values stored in the column myValue are the numbers 1 to 22 (inclusive).


<cfscript>
    if (NOT structKeyExists(form, 'cols')) form.cols = 4;
</cfscript>

This is the equivelant of
<cfparam name=?form.cols? default=4>. I believe the syntax above executes a little bit faster, and as a general rule, I try to use <cfscript> over tags wherever I can (within reason).


<cfif getData.recordCount MOD form.cols>
    <cfset variables.rows = int(getData.recordCount/form.cols) + 1>
    <cfset variables.pads = form.cols - (getData.recordCount MOD form.cols)>

    <cfloop from=
"1" to="#variables.pads#" index="i">
        <cfset temp = queryAddRow(getData)>
        <cfset temp = querySetCell(getData,
'myValue', '&nbsp;')>
    </cfloop>
<cfelse>
    <cfset variables.rows = getData.recordCount/form.cols>
</cfif>


Here, we?re checking to see if the number of records returned evenly divisible by the number of columns that we want to display. If it is, then there isn?t much more we have to do. If it?s not, we need to determine how many ?pad? cells we?re going to need to add to the end of the final row.

variables.rows will be the number of rows that get output.. We determine this by dividing the recordCount of our query by the number of columns to display. We want a whole number, so we use CF?s int() function. We then need to add 1, which will be the ?incomplete? row to which we will add the extra
<td>s.

variables.pads takes the MOD (remainder) of the recordcount divided by the specified number of columns. We then subtract that value from the specified number of columns, and the result tells us how many
<td>?s we need to add.

Once we know how many ?pad? cells we need, we use the built-in queryAddRow() function to append the appropriate number of rows to the query (we want to end up with a recordcount that is evenly divisible by the column count). Once the additional records are added, we assign them a value of &nbsp; (non-breaking space).

The
<cfelse> condition runs if the number of records in the recordcount is evenly divisible by the number of rows specified. e.g. if 20 records are returned, and 4 columns are specified, we will have 5 rows with 4 columns, and no need to pad (or add 1). So assuming this is the case, we set variables.rows to be the number of records divided by the columns value.


<cfset variables.thisRow = 1>
<cfset variables.newrow = false>

We need to keep track of which row we?re on (to know when we?ve hit the final row). So outside of any output, we set variables.thisRow to equal 1. We?ll increment it with each iteration of our output.

We also need to keep track of whether or not we closed a row (see the MOD condition towards the bottom of the table).  If we have, we will set variables.newrow to be true.  However, by default, and for every iteration of the output that does NOT create a new row, variables.newrow will evaluate to false.


The form is self-explanatory, and more or less extraneous, as your application likely won?t include one, so let?s just jump right to:

<table style="border:0px; background-color:#000000;" cellspacing="1">
    <tr>

        <cfoutput query="getData">
       <cfif variables.newrow IS true><tr></cfif>

        <td style="background-color:##ffffff; text-align:right;">#myValue#</td>
            <cfif (getData.currentRow MOD form.cols EQ 0) AND (variables.thisROW LT variables.rows)>
               <tr>
               <cfset variables.newrow = true>

               <cfset variables.currentRow = variables.currentRow + 1>
            <cfelse>
               <cfset variables.newrow = false>

            </cfif>
        </cfoutput>

    </tr>
</table>

Of course, we create our <table> and our first <tr> tag. We then initiate a query-driven <cfoutput> to start outputting the query data within <td>s.

At the end of the output, we insert a conditional to check to see if we?ve hit the specified number of columns yet?AND to see if the current row (stored in variables.thisRow) is less than the total number of rows. If both of those conditions are met, we close our current row, set the value of variables.newrow to be true (indicating that the next iteration of the output should open a new <tr>, and increment the value of variables.thisRow by 1.  Otherwise, we make sure that the value of variables.newrow is false (as we did not end a row).

That?s all there is to it. You now have complete control over your layout, even with dynamic content?and your HTML will still pass validation (as well as render properly in older browsers such as Netscape 4.x).


All ColdFusion Tutorials By Author: Charlie Griefer (CJ)
  • arrays and structures - part 1
    part one of a three-part tutorial designed to gently introduce you to the world of complex variables.
    Author: Charlie Griefer (CJ)
    Views: 24,357
    Posted Date: Monday, August 11, 2003
  • arrays and structures - part 2
    part two of a three-part tutorial designed to gently introduce you to the world of complex variables.
    Author: Charlie Griefer (CJ)
    Views: 17,295
    Posted Date: Monday, August 11, 2003
  • arrays and structures - part 3
    part three of a three-part tutorial designed to gently introduce you to the world of complex variables.
    Author: Charlie Griefer (CJ)
    Views: 21,107
    Posted Date: Monday, August 11, 2003
  • CF 'Best Practices'
    Some tips and techniques that I've picked up over the years. I don't maintain that these are 'official' or 'absolute'...they are simply my preference and things that have worked for me. I would like to share them here, and leave you to make the decision as to whether or not they fit in your 'code arsenal' :)
    Author: Charlie Griefer (CJ)
    Views: 23,372
    Posted Date: Friday, August 15, 2003
  • CFSCRIPT Intro
    An introductory look at CFSCRIPT. Rules, some basic syntax, and a couple of examples of loops and conditional processing.
    Author: Charlie Griefer (CJ)
    Views: 29,707
    Posted Date: Saturday, January 18, 2003
  • ColdFusion Mad Libs - Part I
    A silly but fun time-waster that you can easily include on your Web site. You might be surprised at how addicting it can become :)
    Author: Charlie Griefer (CJ)
    Views: 18,876
    Posted Date: Thursday, May 29, 2003
  • ColdFusion Mad Libs - Part II
    You've finished the first Mad Libs tutorial, but you feel like there's something missing. Of course there is! You want to be able to save the final output to a database to let your visitors browse through other user's stories. Includes a bad-words filter for the more conservative among us :)
    Author: Charlie Griefer (CJ)
    Views: 15,403
    Posted Date: Thursday, May 29, 2003
  • Dynamic Column Output (Part One)
    Have you ever wanted to display your content in rows of 3 columns? If you ever wanted to specify the number of columns per row within your content, here's the tutorial for you.
    Author: Charlie Griefer (CJ)
    Views: 21,902
    Posted Date: Thursday, May 29, 2003
  • Dynamic Column Output (Part Two)
    This tutorial picks up where the Dynamic Columns tutorial left off, showing you how to not only output your data in a specified number of columns, but how to do it while still publishing well formed HTML.
    Author: Charlie Griefer (CJ)
    Views: 17,475
    Posted Date: Saturday, May 31, 2003
  • Grouping Output in CF
    How to group cfquery output in order to effectively display relational database data. Includes an overview of how to output nested groups as well.
    Author: Charlie Griefer (CJ)
    Views: 20,014
    Posted Date: Tuesday, June 17, 2003
  • Helping users obtain their passwords
    Your site requires your visitors to log in. of course, some of your visitors are going to forget their passwords (ok, most will forget their passwords). You don't want them to have to send you an e-mail, and then wait for a response. They need immediate access.

    This tutorial shows two methods by which you can accomodate them.
    Author: Charlie Griefer (CJ)
    Views: 18,346
    Posted Date: Thursday, August 28, 2003
  • JavaScript Form Validation
    Yes, I know we're a ColdFusion site...but ColdFusion does not live in a vacuum. We have to know SQL, HTML, CSS...and sometimes...JavaScript! This tutorial focuses on using JavaScript (in lieu of cfform) to create client side form validation (and explains why writing your own is better than using ).
    Author: Charlie Griefer (CJ)
    Views: 36,957
    Posted Date: Thursday, August 14, 2003
  • Remote File Management
    Manage text-based files on your server from any Web browser. Create a new file, edit a file, or delete a file. Can be a life saver if you're on the road, and find an error in some of your code that needs a quick fix.
    Author: Charlie Griefer (CJ)
    Views: 19,175
    Posted Date: Tuesday, June 3, 2003
  • Save your visitor's clickstreams
    A nifty little custom tag that will allow you to save a visitor's clickstream through your site, as well as display it back to them (with links). Did I really just say 'nifty'?
    Author: Charlie Griefer (CJ)
    Views: 16,735
    Posted Date: Monday, June 16, 2003
  • to cfqueryparam or not to cfqueryparam
    It's been out there since ColdFusion 4.5...most of us have heard of it...few of us use it. Here are some compelling reasons why you should get into the habit of using the tag.
    Author: Charlie Griefer (CJ)
    Views: 22,604
    Posted Date: Thursday, May 29, 2003