Wednesday, May 11, 2011

Expand / Collapse Javascript

Very recently I implemented a requirement to provide expand / collapse feature in long-spanning reports. The JavaScript for this is available on Cognos Support Site. Wish Cognos provides objects for this rather than using javascripts that can prove to be unreliable during upgrades.

What I wanted to focus on through this article is to provide an overview of what this JS does, because it is important to understand the code that you include/integrate into your reports so it is easier to debug issues.

The code is fairly self-explanatory. I have explained the code at a high level:
  • Identify the selected row (the row for which an expand/collapse has been initiated).
  • Switch the icon for the selected row from an expand to collapse or vice-versa.
  • Process all rows below the selected row.
  • For each row processed, called as current row, identify if the initiated function was an expand/collapse.
  • If collapse, then set style property for current row to "none" which will hide current row. If the current row contains an image and if the image is a collapse icon, then re-set the icon to expand icon and hide the row by setting the style property to "none". All rows below the row clicked get hidden and any collapse icons are re-set so when the higher level is expanded, the clickable rows below display an expand icon and not a collapse icon.
  • If expand, and if the current row is at level lower than immediate lower level, then set style property for current row to "none" to hide the row. If the current row is at an immediate lower level then set style to "" to display this row. If the current row contains an image, and if the current row is a sibling level for the selected level then do not process the row.
  • The StartHidden function is called on load of the page. This function gets the table element and hides all rows where padding has been set by setting the style property to "none". Thus all rows except the 1st level rows get hidden on load of the report.

[User comments: RecoveringAdmin]
I implemented this as well when I saw that article in the KB. Well, more specifically, when my users saw that article. The downsides I found with this solution are that it's HTML ONLY. if you want to export a specific state to xls or pdf (say, a few rows expanded, some contracted) you would have to screen scrape and repass those selections to cognos as part of some parameter set. Just rerunning the report to another format will disregard the javascript. (pdf and xls engine rebuilds the result set and disregards the html blocks). It's a neat trick, but not super useful as a report output.

<script>
// These settings you can change to modify the report processing
var UOM="px"; // Set to unit of measure for padding
var INDENT_SIZE=20; // Set to indent padding step size. Setting to 20 means the padding steps are 20,40,60,80. These have to match the padding applied to the report objects
var UOM_SIZE = UOM.length;
function stripTrailing(string,num) {
if ( string == "") {return parseInt(0);} else {return parseInt(string.substring(0,string.length-num));}
}
function ExpandCollapse( el )
{
// Grab the ROW that was clicked and the TABLE that contains it
var tr = el.parentElement.parentElement;
var tbl = tr.parentElement.parentElement;
// Set the alternating display values for hiding/showing the row
var sDisplay = ( el.src.indexOf( "minus" ) == -1 ) ? "" : "none";
var sDisplayReverse = ( el.src.indexOf( "minus" ) == -1 ) ? "none" : "";
//Switch the icon for the clicked row
el.src = "../pat/images/PropertyGroup_" + ( el.src.indexOf( "minus" ) == -1 ? "minus" : "plus" ) + ".gif";
// Starting with the row below the clicked row, start checking each row
for ( var i = tr.rowIndex + 1; i < tbl.rows.length; i++ )
{
// Set the Current row indicator nad the left padding value
var trCurrent = tbl.rows( i );
var trCurrentLeft = trCurrent.cells(0).style.paddingLeft;
// if the current row contains an IMG in it, it's a clickable level and we either have to stop processing,
// or reset the icons to a + as it's being collapsed
if ( trCurrent.cells( 0 ).firstChild && trCurrent.cells( 0 ).getElementsByTagName( "IMG" ).length )
{
// If the current row is at the same level or above in the tree, then stop processing,
// else reset all the signs below it, essentially collapsing all branches underneath the one that is beig collapsed.

if ( stripTrailing(trCurrentLeft , UOM_SIZE) <= stripTrailing(tr.cells(0).style.paddingLeft , UOM_SIZE) )
{ break; }
else
{
if (el.src.indexOf( "minus" ) == -1 )
{
trCurrent.cells(0).getElementsByTagName("IMG").item(0).src = "../pat/images/PropertyGroup_plus.gif";
}
}
}
// Now, we determine if the row should be hidden or shown.
if ( eval(stripTrailing(tr.cells(0).style.paddingLeft, UOM_SIZE) + INDENT_SIZE) < stripTrailing(trCurrentLeft, UOM_SIZE) && el.src.indexOf( "minus" ) > 0 )
{
trCurrent.style.display = sDisplayReverse;
} else
{
trCurrent.style.display = sDisplay;
}
}
}
function StartHidden(el)
{
var tbl=el.parentElement.parentElement.parentElement.parentElement;
for (var i = 0; i < tbl.rows.length; i++)
{
var trCurrent = tbl.rows(i);
if (trCurrent.cells(0).style.paddingLeft.indexOf(UOM) > -1)
{
trCurrent.style.display = "none";
}
}
}
</script>

Note: This technique uses JavaScript against underlying report objects in a IBM Cognos 8 BI report. For this reason, there is no guarantee that reports created using this technique will migrate or upgrade successfully to future versions without requiring modifications. Any such modifications are the responsibility of the report designer.
 

4 comments:

RecoveringBIAdmin said...

I implemented this as well when I saw that article in the KB. Well, more specifically, when my users saw that article. The downsides I found with this solution are that it's HTML ONLY. if you want to export a specific state to xls or pdf (say, a few rows expanded, some contracted) you would have to screen scrape and repass those selections to cognos as part of some parameter set. Just rerunning the report to another format will disregard the javascript. (pdf and xls engine rebuilds the result set and disregards the html blocks). It's a neat trick, but not super useful as a report output.

Zephyr said...

Agree with you RecoveringAdmin. Wish there were properties or settings that could help us achieve often required features like these.

Thanks for bringing this to my notice. I will update the article to include these concerns.

Shruthi said...

1.How to implement the same functionality for crosstab columns instead of crosstab rows expand/collapse?
2.Can we do for specific number of crosstab columns instead of all the columns?

Could you please help me in doing this.

Thanks!

spyhop said...

Hi, In an 11.2.1 Cognos report I have a simple 'master' list that has a 'child' list embedded in a column of the 'master' list.

I would like to know how to implement this technique to show/hide the 'child' list.

Can you help?