Archive | Data View RSS for this section

XPages Tip: Fixing a partial refresh issue with the Pager Add Rows control in a Data View

I came across a strange issue with a Data View using a Pager Add Rows control resetting the number of documents displayed after a partial refresh. In this post, I’ll explain the scenario where I saw the issue and show the simple solution that fixed the problem.

The Problem

I have a Data View that starts with 20 rows and uses a Pager Add Rows control to add 10 more rows at a time.

Within the summary facet, there’s an action that can take place, which runs a minimal script and then performs a partial refresh with partial execution on the summary section.

This *seems* to be fine when performed within the first set of rows, but I noticed that if additional rows are added to the display and then the action is run in any one of the rows, the action runs successfully, but the data view is reset to only display the initial set of 20 rows.

If the action was taken on a row after the initial 20, I can add rows again and see that the action has taken place and the partial refresh has occurred as designed.

The Fix – Changing the ‘State’ Property

I tried making a number of changes to partial refresh and partial execution settings, but none of them fixed this issue.

Ultimately, the issue was fixed by a simple property change. The Pager Add Rows control has a state property (All Properties > basics > state)

When set to true, it stores the view state on the server.

Pager Add Rows - State Property

This did the trick. It allows the row count to be preserved between partial refreshes by storing it on the server.

Advertisements

Getting Awesome Category Icons in Data Views – A jQuery Variation

In a Twitter exchange with Paul Withers regarding his great post on replacing category expand/collapse icons in a Data View with Font Awesome icons, he mentioned that it would be interesting to see a jQuery alternative. Since I’m working on improving my proficiency with jQuery, I thought it would be an interesting challenge. In this post, I’ll show how I implemented it and describe the main challenge that I faced.

This code will use jQuery to replace the expand/collapse icons with related font awesome icons.

As in Paul’s post, this code depends on the category columns having a class of catColumn (although it can be done without it).

The expand and collapse icons are within an <h4> tag and an <a> link. The image source name will be collapse.gif or expand.gif, so this code reads the source to determine which type of image to replace. Since Font Awesome uses <i> tags, it adds the new icon tag and then removes the <img> tag.

This code has been placed in a client-side JavaScript library so I can call it from two different places.

Note: The application I’m testing on has Font Awesome v3, so the icon tags look different than the one in Paul’s post, where he’s using version 4.

// Replace the standard Data View expand/collapse icons with font-awesome icons (v3) using jQuery
function replaceCategoryIcons() {
  $('.catColumn a img').each(function(){
    var src=$(this).attr('src');
    if (src.indexOf('collapse') > -1) {
      $(this).after('<i class="icon-collapse"></i>&nbsp');
    } else {
      $(this).after('<i class="icon-expand"></i>&nbsp');
    }
    $(this).remove();
  });
}

This code was pretty straightforward to create; the real trick is firing it as the data view is refreshed when categories are expanded and collapsed. There’s no pure DOM event that I’m aware of that I can listen to with jQuery in order to re-execute after each partial refresh. jQuery cannot subscribe to a stream that’s published by dojo, so I can’t use code similar to Paul’s post.

I tried to use event delegation (check out Marky’s great explanation here) on the click event of the images (since that’s what triggers the update). This kind of worked, but the problem was that it replaced the icons and then, when the page refreshed, the old icons were right back in place.

So, it all came back to Tommy Valand’s great post about hijacking partial refresh events to solve this problem.

I trimmed down original code and updated the onComplete event to trigger my jQuery function to replace the icons (line 18).

function hijackPartialRefresh() {
  // Hijack the partial refresh
  XSP._inheritedPartialRefresh = XSP._partialRefresh;
  XSP._partialRefresh = function( method, form, refreshId, options ){
    // Publish init
    dojo.publish( 'partialrefresh-init', [ method, form, refreshId, options ]);
    this._inheritedPartialRefresh( method, form, refreshId, options );
  }

  // Publish start, complete and error states
   dojo.subscribe( 'partialrefresh-init', function( method, form, refreshId, options ){
    if( options ){ // Store original event handlers
      var eventOnComplete = options.onComplete;
    }

    options = options || {};
    options.onComplete = function(){
      replaceCategoryIcons();
      if( eventOnComplete ){
        if( typeof eventOnComplete === 'string' ){
          eval( eventOnComplete );
        } else {
          eventOnComplete();
        }
      }
    };
  });
}

Now, to run the code both when the page loads and then again on each partial refresh, I added this code to the onClientLoad event of the page:

// Replace the category icons with font-awesome icons
replaceCategoryIcons();

// Set up ongoing replacement of icons
hijackPartialRefresh();

Now, it replaces the default icons…

DataViewExpandCollapseIcons_Before

… with Font Awesome icons…

DataViewExpandCollapseIcons_After

XPages Data Views – Part 11: Custom Expand/Collapse Links

If you’re not a big fan of the built-in small expand/collapse icon that sits off to the right of each entry (IMHO, it works well on a mobile device but is not a great interface in a full browser), you can create your own. In this post, I’ll show how to create your own links when the details are on the server or on the client. I’ll also point you in the right direction to work around a bug in Notes9.

Data View Series

Example

Here’s a very simple example of how it can look with your own expand/collapse icon:

DataView11_A

Creating Your own Expand/Collapse Link – Details On Server

At a high level, here are the steps:

  1. Add images resources to your application
  2. Create a custom summary section and display the images that will be used to toggle
  3. Add code to toggle the images
  4. Add code to take care of the expand/collapse and trigger a partial refresh as needed
  5. Optionally hide the built-in link

There differences in how to implement it, based on both the Data View’s detailsOnClient property and the version of Notes that you’re using.

Expand/Collapse Images

To start, we’ll set up two images that the user can click to expand or collapse the details.

They’ll need to be displayed in the summary section, so we’ll have to customize the summary as shown in this post. Add two image resources to your database (one for expand and one for collapse). To display them next to the summary link, you’ll have to use CSS or put them in a table.

Since the details will be collapsed by default, we want to display the ‘expand’ image and hide the ‘collapse’ image. We also want their display to toggle as the details section toggles. Since we’re using a server-side refresh to retrieve the details, we’ll use server-side logic to determine whether the images are displayed.

The rendered formula for the ‘expand’ icon is this:

return !viewScope.containsKey('Row_' + getComponent('dataView1').getRowIndex());

The rendered formula for the ‘collapse’ icon is this:

return viewScope.containsKey('Row_' + getComponent('dataView1').getRowIndex());

The scope variable that is referenced will be added and removed in the code shown in the next section. It will be effective because the event code will trigger a partial refresh on the data view.

The next step will be to add the code that actually expands or collapses the section.

Details on Server

The default behavior of the Data View is that the expand/collapse link will make a request to the server to retrieve the details section for that entry. We can recreate this link with either the toggleDetailVisible() or setDetailVisible() methods of the data view.

Paul Withers showed how to use the toggleDetailVisible() method with SSJS in this post on Stack Overflow.

var idex=getComponent("dataView1").getRowIndex();
getComponent("dataView1").toggleDetailVisible(@Text(idex+1));

The same code can be applied to both the expand and collapse images. The server-side event also needs to trigger a partial refresh on the data view so you’ll see the changes.

We just need to add an additional line that also sets or removes the scope variable that is used to determine whether the expand or collapse link is displayed.

Here’s the full source of the expand and collapse images, including the code to toggle the detail section, show or hide the scope variable, and partially refresh the data view:

<xp:image url="/plus.png" id="imgExpand"
  rendered="#{javascript:return !viewScope.containsKey('Row_' + getComponent('dataView1').getRowIndex());}">
  <xp:eventHandler event="onclick"
    submit="true" refreshMode="partial" refreshId="dataView1">
    <xp:this.action><![CDATA[#{javascript:var idex=getComponent("dataView1").getRowIndex();
      viewScope.put('Row_' + idex, '');
      getComponent("dataView1").toggleDetailVisible(@Text(idex+1));}]]>
    </xp:this.action>
  </xp:eventHandler>
</xp:image>
<xp:image url="/minus.png" id="imgCollapse"
  rendered="#{javascript:return viewScope.containsKey('Row_' + getComponent('dataView1').getRowIndex());}">
  <xp:eventHandler event="onclick"
    submit="true" refreshMode="partial" refreshId="dataView1">
    <xp:this.action><![CDATA[#{javascript:var idex=getComponent("dataView1").getRowIndex();
      viewScope.remove('Row_' + idex);
      getComponent("dataView1").toggleDetailVisible(@Text(idex+1));}]]>
    </xp:this.action>
  </xp:eventHandler>
</xp:image>

If you need finer control over the state, you can use the setDetailVisible() method instead of the toggleDetailVisible() method. The difference with this method is that you pass a second parameter (true or false) to the setDetailVisible() method in order to set the visibility state.

Side Note: I was able to figure these things out via two great resources for learning XPages — Stack Overflow and NotesIn9. The toggleDetailVisible method was described by Paul Withers in the Stack Overflow post referenced above, and I used the method described in this video (also by Paul Withers) on NotesIn9 to figure out that there is also a setDetailVisible() method.

Workaround for Bug in Notes 9

Unfortunately, there appears to be a bug in Notes9 that prevents the toggleDetailVisible() method from working.

The workaround is to make the expand/collapse links trigger a click() event on the built-in expand/collapse link.

Henry Newberry worked out the code and posted it on the same Stack Overflow post mentioned above, so you can find it there.

Expand/Collapse with Details On Client

The code shown in both solutions above will work regardless of the detailsOnClient property, but if we’re using the extra overhead to pre-load the details into the client, it’s a waste of resources to hit the server to partially-refresh the view anyway.

When the detailsOnClient property is set to true, a div tag with each detail section is already on the page, but hidden with css.

Here’s an example of the detail section for the first entry in the view:

<div id="view:_id1:dataView1:0_detail" style="display: none">
  <span id="view:_id1:dataView1:0:computedField1" class="xspTextComputedField">
    392 Shinn Street
    <br></br>
    New York, NY  10021
    <br></br>
    US
  </span>
</div>

Since the information is already on the page, it will be much more efficient if we use client-side javascript to toggle the image display and toggle the detail section display. This way, there’s no need to send any requests to the server.

Expand/Collapse Images

To do this, I set the styleClass of the ‘collapse’ image to ‘hidden’ and added this css to a style sheet that’s included on the page:

.hidden {
  display:none;
}

Next, I added this same block of client-side JavaScript code to both images:

idCollapse = '#{id:imgCollapse}'.replace(/:/gi, "\\:");
idExpand = '#{id:imgExpand}'.replace(/:/gi, "\\:");
dojo.query('#' + idCollapse).toggleClass('hidden');
dojo.query('#' + idExpand).toggleClass('hidden');

I wanted to use the dojo toggleClass() method to add/remove the ‘hidden’ class from each image. However, dojo.query() doesn’t like the colons that are prevalent in XPages element ids, so I adapted code from Mark Roden’s jQuery XSnippet to format ids that dojo.query() can handle. The toggleClass will remove the class from the element if it has it and it will add the class to the element if it does not already have it. (This block of code will also work as is if you only have a single image/link/button that will both expand and collapse.)

At this point, we have image to use for expanding and collapsing and they’re set up to toggle based on which option has been clicked.

Expand/Collapse Logic

To toggle the visibility of the detail section, we need to be able to get the handle to it. Fortunately, the names of each section can be derived from the name of the data view and the (0-based) row number, in this format:

[dataViewID]:[rowNum]_detail

In this example, here is the ID of the Data View is view:_id1:dataView1 and the id of the detail section is view:_id1:dataView1:0_detail.

Fortunately, it’s pretty easy to retrieve. When you retrieve the ID of the data view from within the data view, it also includes the row number! To retrieve the ID of the detail section, all you have to do is append ‘_detail’!

Here is the client-side code on the ‘expand’ link that will toggle both the image and detail section display. The code on the ‘collapse’ image is identical, except that the last line sets the display style of the detail section to ‘none’, rather than ‘block’.

idCollapse = '#{id:imgCollapse}'.replace(/:/gi, "\\:");
idExpand = '#{id:imgExpand}'.replace(/:/gi, "\\:");
dojo.query('#' + idCollapse).toggleClass('hidden');
dojo.query('#' + idExpand).toggleClass('hidden');

var dvID = '#{id:dataView1}';
var detailID = dvID + '_detail';
dojo.byId(detailID).style.display='block';

Optionally Hide the Built-In Link

Now that you have your own link to expand and collapse, you may want to hide the built-in link.

You can hide the built in link that the Data View provides to expand/collapse, you can add this css:

a img.lotusIconShow, a img.lotusIconHide {
  display:none;
}

The code looks for all images with the class of lotusIconShow or lotusIconHide within a link and hides them.

XPages Data Views – Part 10: Nested Repeat in the Detail Section

The great benefit of the collapsible detail section in the Data View control is that you can make a lot more information available in the view, but not clutter the screen with it unless the user requests it. I’ve had several cases where I’ve created a repeat control in the detail section and used it to display information related to the main document. In this post, I’ll show how to do it.

Data View Series

Nested Repeat Control

Here’s an example from the NotesIn9 video I did on customizing the Data View control.

DataView10_A

The sample database has a list of companies and a list of stock prices documents related to each company. When the user expands a company in the Data View, they see the list of related stock prices (along with a company description).

Here are the steps:

  1. Set the var property of the data view to provide a handle to each entry
  2. Create a repeat control in the detail section. (Add a panel or div to the Detail section and add the repeat control within that container.)
  3. Compute the value of the repeat control (All properties > data > value) to retrieve the ID of the main document and use it to look up related documents to display in the repeat control

Here is the code that populates the repeat control:

var vwPrices:NotesView = database.getView('Prices');
if (rowHandle.isDocument()) {
  var companyID = rowHandle.getDocument().getItemValueString('CompanyID');
  var vecPrices:NotesViewEntryCollection = vwPrices.getAllEntriesByKey(companyID);
  return vecPrices;
} else {
  return null;
}

The key to making this happen is having the ability to retrieve the UNID of the main document when populating the repeat control. In this example, the var property of the Data View is set to rowHandle and it used in line 3 to retrieve the CompanyID.

In line 4, the list of related stock prices is looked up from a view.

In line 5, the viewEntryCollection of price documents is returned to be the source of the repeat control.

At this point, the repeat control has the data and you just need to add computed fields to display it like you would with any repeat control.

XPages Data Views – Part 9: Multi-Column Layout

Another great feature of the Data View control is its ability to easily provide a multiple-column layout. In this post, I’ll show you can use that property to let the user dynamically determine the number of columns to display.

Data View Series

Multiple Column Layout

If your data lends itself to a multiple-column layout (e.g. a business card-style display), you can easily achieve this by setting the multiColumnCount property of the Data View. (All Properties > format > multiColumnCount)

DataView9_A

You can still expand/collapse detail sections when multiple columns are used.

Giving the user control

Taking this a step further, you can compute the property and give the user the ability to adjust the display as desired.

The steps are virtually the same as shown in the last post on sorting and filtering.

1) Put a combobox on the page with options for the number of columns to display. (You can event put it in one of the pager top facets of the data view to tie it in.)
2) Bind the combobox to a sessionScope variable
3) Set the onchange event of the combobox to trigger a partial refresh on the data view
4) Compute the Sort column property to return that sessionScope variable

XPages Data Views – Part 8: Sorting and Filtering

There are several properties available for searching and filtering a data view. In this post, I’ll show how to use them.

The Data View properties make a few of these features prominently available.

DataView8_A

Data View Series

Sort column

The Sort column property allows you to choose a column on which the data view should be sorted. The combobox will give you a list of all columns in the underlying view.

However, you need to be aware that the underlying view column must have that sort option available (“click oncolumn header to sort”) or it won’t work.

The data view will be sorted the way the underlying view is sorted by default. You can choose an existing column, but you can also compute the value.

To add even more flexibility to your view, you can provide links or a combobox to let the user choose the sort option.

Here’s how you can make it happen:

1) Put a combobox on the page with a list of columns that are viable sort options for the view. (You can event put it in one of the pager top facets of the data view to tie it in.)
2) Bind the combobox to a sessionScope variable
3) Set the onchange event of the combobox to trigger a partial refresh on the data view
4) Compute the Sort column property to return that sessionScope variable

Filter by column value

The Filter by column value property sets the keys property of the data source (which is also available on other view data sources).

You can enter or compute a value here that will set the ‘keys’ property and filter out the data based on the sorted column in the view, which is the default sorted column or the column defined in the Sort column property.

Just like with a search field, you can provide the user a field to type and filter the data. You can provide a highly flexible experience for the user if you allow them to choose both the sort column and the filter to apply to that column.

Search in view results

Searching and category filtering work the same as view data sources in several other data display controls. For an example of how to implement searching, take a look at this post I wrote about searching with the View Panel control.

XPages Data Views – Part 7: Customizing the Summary Section

The ability to customize the summary section allows you take the interface of the Data View to another level. You’re not limited to just a link based on the summary column — you can take full control over the layout and add more information.

Data View Series

Custom Summary Examples

For the sake of comparison, here’s a data view with just a summaryColumn defined:

DataView7_1

Let’s take a look at a few examples of Data Views with customized summary sections.

Here’s an example from the second NotesIn9 video I did on the Data View control. It’s nothing fancy, but you can see the concept of adding more information to the summary. Additional information can still be included in the collapsible detail section.

DataView7_2

Here’s an example from IBM Domino Developer wiki. This summary includes graphics and 3 lines’ worth of information.

DataView7_3

Here’s an example from an application that I recently worked on with Kathy Brown, master of icons. The images are used to convey information without requiring labels that would repeat on every row.

DataView7_RAIN_Sample

The possibilities are endless.

Customizing the Summary

All you have to do to customize the summary section is to drag and drop a container control (panel or div) into the summary facet of the data view, then put whatever you want inside that container.

You’ll need to use the Data View variable (var property) to access information in the view entry.

Creating Your Own Link

One thing to keep in mind is that you will have to manually add the link to open the document if you create a custom summary.

In order to pick up the same styling as the default summary column data view link, use a Computed Field control, set it’s Content type to HTML and output the link as an <a> tag within an <h4> tag.
You’ll then need to build link to open the document.

Here’s an example, assuming the var property of the data view is set to rowHandle.

return "<h4><a href='PageName.xsp?openDocument&documentId=" + rowHandle.getUniversalID() + "'>" + rowHandle.getDocument().getItemValueString('FieldName') + "</a></h4>";