wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

Taming the jumpy categorized Views in XPages


I am not a fan of categorized views in web applications and have suggested alternative uses before. However beautiy is in the eye of the beholder, so categorized views are used. When a category is rendered it sits on its own tablerow (tr) in a tabletag (td) that has a colspan attribute. This effectively disables the column width settings for your view until you expand the category to show a full row of data resulting in some "screen jumping". While that won't sink the Titanic it is a little odd. Short of rolling your own rendering using a repeat control, there is a fix consisting of 2 JavaScript functions (one for the server, one for the client and a little addition in the source code panel. This works in all categorized views. However when you have a column with a column total rendering might be different since XPages doesn't render the colspan attribute then. Lets get started. Open your source pane and locate the view panel and the xp:this.facets. Here you insert a scriptBlock like this:
  1. {xp:this.facets}
  2.     {xp:pager partialRefresh="true" layout="Previous Group Next"
  3.     xp:key="headerPager" id="pager1"}
  4.     {/xp:pager}
  5.       {xp:scriptBlock xp:key="south" id="scriptBlock1"}
  6.     {xp:this.value} {![CDATA[#{javascript:return getClientJSforTableLayout("viewPanel1");} ] ] } {/xp:this.value}
  7.       {/xp:scriptBlock}
  8. {/xp:this.facets}
(Replace { } with < and > except inside the CDATA section). Then you add 2 JavaScript libraries to your XPage: CatViewPatches.js and TableSupportFunctions.jss. They will fix the column width. One caveat: you have to specify the width for all columns otherwise the unspecified columns get 100/{number-of-columns}% width assigned.The ServerSide JS:
  1. function getClientJSforTableLayout (tableID ) {
  2.     /**
  3.       * This code has as result JavaScript source code that is send down to the client]
  4.     */
  5.     var curTable = getComponent ( "viewPanel1" ) ;
  6.     var curID = getClientId ( "viewPanel1" ) ;
  7.     var tablewidth = extractWidthFromStyle (curTable. viewStyle ) ;
  8.     var result = "fixCatTableWidth(\"" ; // This holds the JavaScript we return to the browser
  9.     result += curID + "\",\"" ;
  10.     // Here we compute the sizeString
  11.  
  12.     var kids = curTable. getChildren ( ) ;
  13.     var defaultPercent = Math. floor ( 100 /kids. size ( ) ) + "% " ; // You have to love flexi data types
  14.  
  15.     for ( var x in kids ) {
  16.         var styleCandidate = x. style ;
  17.         if (styleCandidate == null ) {
  18.             result += defaultPercent ;
  19.         } else {
  20.             var candidate2 = extractWidthFromStyle (styleCandidate ) ;
  21.             if (candidate2 == "" ) {
  22.                 result += defaultPercent ;
  23.             } else {
  24.                 result += candidate2 ;
  25.             }
  26.         }  
  27.        
  28.         result += " " ;
  29.        
  30.     }
  31.  
  32.     result += "\",\"" +tablewidth + "\");" ;
  33.     return result ;
  34. }
  35.  
  36. function extractWidthFromStyle (styleCandidate ) {
  37.     var result = "" ;
  38.     var whereisWidth = styleCandidate. indexOf ( "width:" ) ;
  39.     if (whereisWidth < 0 ) {
  40.         result = "" ;
  41.     } else {
  42.         var workString = styleCandidate. substr (whereisWidth + 6 ) ;
  43.         var hasSemiColon = workString. indexOf ( ";" ) ;
  44.             if (hasSemiColon < 0 ) {
  45.                 result = workString ;
  46.             } else {
  47.                 result = workString. substr ( 0 ,hasSemiColon ) ;
  48.             }
  49.     }
  50.     return result ;         
  51. }
The Client Side JS:
  1. function fixCatTableWidth (tableID , sizeString , tableSize ) {        
  2.     // Reset the outer table width
  3.     var outer = dojo. byId (tableID + "_OUTER_TABLE" ) ;
  4.     if (tableSize == null || tableSize == "" ) {
  5.         outer. style. width = "100%" ;
  6.     } else {
  7.         outer. style. width = tableSize ;
  8.     }  
  9.     var newWidth = sizeString. split ( " " ) ;
  10.     var max = newWidth. length ;
  11.     // Get the table and all header elements
  12.     var table = dojo. byId (tableID ) ;
  13.     var allHeaders = table. getElementsByTagName ( "th" ) ;
  14.     var maxHeaders = allHeaders. length ;
  15.     for (i = 0 ;i <maxHeaders ;i ++ ) {
  16.         var curWidth = (i > max ) ? newWidth [max ] : newWidth [i ] ;
  17.         allHeaders [i ]. style. width = curWidth ;
  18.     }
  19. }
As usual: YMMV

Posted by on 29 June 2010 | Comments (6) | categories: XPages

Comments

  1. posted by Atin Palekar on Friday 02 July 2010 AD:
    Hi Stephan,

    Thanks for the workaround.

    For a categorized view not containing totals, I have been able to resolve the jumpy behavior by carefully setting XPages viewpanel & viewcolumn properties as follows
    1. Set viewpanel width to a 100%
    2. Do not specify any width for the view column headers and set the disableTheme property to true
    3. Specify the width for ALL view columns in percent so that the total adds up to 100%
    4. Care needs to be taken so that width does not fall below the minimum needed to display contents (eg. icon/checkbox)
    5. For categorized & view icon columns, set disableTheme to true for allowing width to be reduced as per need
    6. For categorized columns, set white-space:nowrap in the style attributes to prevent wrapping of categories
    The only problem here is that view icons do not display if width is specified for the concerned column. But for view icon columns, if I specify width as required in step 4 and then remove it, things seem to work fine. This method seems to be working in IE7 & 8 with most view display requirements at our end. It can also be adapted for total width other than 100% (or fixed width in pixels, etc) as long as the sum total of individual column widths adds up to the total width of the view panel. Would appreciate your expert opinion on whether there could be any loopholes where this method should fail.

    My main problem was for categorized views with totals, where XPages does not seem to span the category values across columns, thus disturbing column width settings. Though the JS workaround did not work directly, I was able to apply the technique described to check which of the view columns are categorized (in SSJS), loop thru all view data rows, change their colspan as needed and remove interim columns (in CSJS).

    Not sure if these workarounds are ready (in terms of different environments, browsers, etc) to be posted for re-use but in tandem they should provide the final pieces for developers looking to build Notes-client like views in XPages with exactly matching look-n-feel

    Thanks again
  2. posted by Stephan H. Wissel on Monday 05 July 2010 AD:
    Setting the view panel parameters doesn't work for all browsers, so that's what this code is all about.
    Views with Totals are a little more tricky. It can be solved. However you need to be very careful what you wish for (You might just get it). Your guideline for UI should not be " must look like Notes" - we had that in the R5 days. It better be " a modern web UI". There is a very good guideline available from IBM called the IBM OneUI. Looking at it I can't find a categorized view there. Going back to my original suggestions how to handle categorized views I can see the need for breadcrumbs, Tree controls, TableGrids or eventually a Dojo TreeGrid (check the original example). You would need to create a custom control once and then use that instead of the view control. There are also some very interesting things coming in 8.5.2 - but that's a story for another day.

    As usual: YMMV
  3. posted by Wayne Allen on Monday 22 November 2010 AD:
    Stephan,

    Could you post an example of how to append a button column to the end of a view. What I am aiming to achieve is to add a button to the end of every view row that lets users delete the document.

    Cheers,
    Wayne
  4. posted by Sanjeet Uchil on Monday 03 September 2012 AD:
    I have followed the instructions as mentioned. But it doesn't seem to work. What could be the issue? I have two category columns and I am working on 8.5.1
  5. posted by Stephan H. Wissel on Monday 03 September 2012 AD:
    @Sanjeet: what do you expect to happen, what did happen and what happens on a current version? I don't code for older versions
  6. posted by Stephan H. Wissel on Monday 03 September 2012 AD:
    @Sanjeet: what do you expect to happen, what did happen and what happens on a current version? I don't code for older versions