Friday, October 22, 2010

XPages: Make categorized views behave

Update 22.05.14: Updated with improved code.

The current implementation of categorized views gives a table column to each column. In most cases, you probably don't want this. The code below makes the categories look more like nested sections.

The code is requires Dojo 1.4, which is bundled with Domino 8.5.2. To make it compatible with previous versions, you'd have to exchange categoryButtons.closest( 'tr' ) with code that walks up the DOM tree until it finds a tr-node.

function transformCategorizedViews(){
 // Needed for dojoj.NodeList.closest()
 dojo.require("dojo.NodeList-traverse");
  
 var dataTables = dojo.query( '.xspDataTable' );
 dataTables.forEach( function( dataTable ){
  var numColumnsTotal = dojo.query( 'thead th', dataTable ).length;
   
  var categoryButtons = dojo.query( '.xspDataTable button[title~=collapsed], .xspDataTable button[title~=expanded]' );
  
  // Get parent row/find all the empty cells before expand/collapse button
  var categoryButtonRows = categoryButtons.closest( 'tr' );
  categoryButtonRows.forEach( function( categoryButtonRow ){
   var numCellsWithContent = 0;
   var numEmptyCellsBeforeButton = 0;
   var cells = categoryButtonRow.cells;
   if( cells.length > 1 ){
    var categoryButtonCell = null;
    for( var i = 0, numCells = cells.length; i < numCells; i++ ){
     var cell = cells[i];
     var cellIsEmpty = ( cell.innerHTML === '' );
     
     // Hide empty cells/set colspan to the category equal to hidden cells
     if( !categoryButtonCell ){
      if( cellIsEmpty ){
       numEmptyCellsBeforeButton += 1;        
      } else {
       categoryButtonCell = cell;
      }
     }
     
     if( cellIsEmpty ){
      cell.style.display = 'none';       
     } else {
      numCellsWithContent += 1;
     }
    }
     
    // colspan = column with expand/collapse + column count - num cells with content 
    categoryButtonCell.setAttribute( 'colspan', 1 + numColumnsTotal - numCellsWithContent );
    categoryButtonCell.style.paddingLeft = (30 * numEmptyCellsBeforeButton ) + 'px';     
   }
  });
 });
}

Since categorized views use partial refresh, you'll need something like my partial refresh hijacker.

Here's how to use the above code with the hijacker:
dojo.addOnLoad(function(){
 // Run on load
 transformCategorizedViews();
 // Run on partial refreshes
 dojo.subscribe( 'partialrefresh-complete', transformCategorizedViews );
});
Share and enjoy!

5 comments:

Rajeev said...

This "dojo.NodeList-traverse" seems to be a very useful library. Unfortunately it is only available with dojo v1.4 (XPages 8.5.2). Anyways, thanks Tommy. I was completely unaware of this library.

Tommy Valand said...

No problem :)

Thanks for making me aware of the requirement. I've added a notice about this in the blogpost.

booyaa said...

Hi Tommy - sorry fro being dim, but does dojo.addOnLoad go? couldn't see a way to add it to the viewpanel control or perhaps it goes in the viewcolumn item?

booyaa said...

To answer my own question: Add the "dojo.addOnLoad" code in the onClientLoad section. This is reachable through the Outline perspective in XPage node > All Propertise > events

Steve Smillie said...

Thank You!

Too bad the view control doesn't handle this correctly, but your fix worked well to improve the indentation on my multiple category view.