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.

Advertisements

One response to “XPages Data Views – Part 11: Custom Expand/Collapse Links”

  1. Baxter Jimuk says :

    Hi, this is unrelated to above post but since you have many posts working with views, may I know why does the default Pager Expand / Collapse control (the one in the extension library) only follow its default design while in a Data View’s facet? If I put it outside the Data View’s facet or in a normal ViewPanel’s facet (the default XPages viewPanel), it’ll become a bullet list (instead of Expand All | Collpase All).

    Most of the time I’ll just custom build my own expand/collapse control but it’s just bad that something useful like the Pager Expand/Collapse provided in the extension library appears differently if put in different controls.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: