Tuesday, 18 February 2014

Splitting an Apex report into several columns

The other day I was asked to create a page where a large number of properties should be connected to an object. I created a report consisting of the rows with the property name and a check box. This resulted in a very long and narrow report. All of the data could not be seen without scrolling. So I decided the report should be split into a number of columns. And I wanted to avoid special constructs in Apex and SQL (I have done that before and it was very compex). So I decided to use JavaScript to move the rows. By using JavaScript the Apex transaction mechanism keeps functioning, Apex does not know or notice that anything in the layout of the page has been changed.

I want to achieve the right side situation starting with the left report ( that by the way continues a long way below where the picture ends)

With JavaScript a number of extra table elements are created on the same level as the table element containing the report. The thead element of the original table element will be copied to the other table nodes to provide the same heading and a tbody element will be created to house the rows. After that the rows can be distributed among the tables. With CSS the table elements are positioned next to each other. The only limit to the number of columns is the available horizontal space.
You should call the JavaScript function below at startup or in the refresh event when using partial page rendering.

function reportToColumns ( tabSelector, numCols )
{ var numRows = 0;
  // bereken aantal rijen per kolom
  var rowsPerCol = Math.ceil( ( $(tabSelector+' tr').length-1 )/numCols);
  var baseName = 'reportColumn'; 

  // maak kolommen aan
  for ( i = 2; i <= numCols; i++)
  { $(tabSelector).parent().append('
'); $(tabSelector+' thead').clone().appendTo( $('#'+baseName+i) ); $('#'+baseName+i).append(''); } // verdeel de rijen over de kolommen var id = 2; var dest = ''; $(tabSelector+' tr').each( function(index) { if (index > rowsPerCol) { dest = '#'+baseName+id+' tbody'; $(dest).append( $(this) ); numRows = numRows + 1; // wissel de kolom als rijen per kolom is bereikt if ( numRows >= rowsPerCol) { id = id + 1; numRows = 0; } } } ); }

I used classes for the layout of a specific report. You might need to change that according to your needs.
To make sure the div’s are placed next to each other a little bit of CSS is needed:
.reportColumn {
    display: block;
    float: left;
    margin-right: 30px;
    position: relative;

You can see this in action right here.
Note 03-10-2015: The software is dependent of the template used. The link above is suited for Theme 25. You will find a version for the Universal Theme here.

This method can be used on either Apex Reports or Apex Tabular Forms. In fact it is not a specific Apex solution and can be used on any HTML table. Happy Apexing


Tony said...

Notice that you get some unwanted results when you click the button a second or third time.

When I did this the first column of the recently split columns continue to shrink. I eventually found the missing rows in another column, not in the correct order.

Could probably be solved by simply disabling the button upon clicking.

Pretty cool idea. As you said, the best part is that the apex functionality isn't compromised.

Dick Dral said...

Tony, you are right. Off course this is an example, in practice I would not supply such a button ;-).
I will follow your advice to hide the button after being pressed.

Dave said...

Hi Dick,

Would you be able to provide access to the page or an export?

I'm new to JavaScript and would like to understand how and where to call the function.


Dick Dral said...

Hi Dave,

the easiest would be to send you the application export. For that I need your e-mail. Please mail me your request and i will send you the application export.