Flexible View Control for XPages – Adding Subcategories To Categorized Views

With the last post in the Flexible View Control Series introducing categorized Views, this post is picking right up where that left off and demonstrating how easy it can be to add multiple levels of categorization to a View.

The View Definition

Jumping right in, I’ve created a copy of the ferraris-categorized View Definition used in the previous demo and named the copy ferraris-subcategorized. Since this View Definition is already categorized by MODEL, I want to subcategorize the YEAR column. All I need to do is make a couple of small changes:

  1. Sort the subcategory column
  2. Tell the YEAR column to categorize in addition to the MODEL category
  3. Hide the YEAR column for the detail rows

Displaying the View

For displaying the View I can reuse my viewCategories XPage created for the category demo in the last blog post since it’s configured to dynamically read the View Definition and query from the url QueryString.

Keep in mind … I haven’t altered the back-end View at all. The power is in the View Definition, allowing me to use the same data source to create multiple Views.

See the demo for yourself


Adding a subcategory to a categorized View

Expanding, collapsing, and filtering all still work as you would expect.

How About More than Two Levels of Categorization?

The Flexible View Control will support as many levels of categorization as you need. The caveat, of course, is all of the categorized columns need to be sorted in the order of categorization.

Demo: Three levels of categorization

View Definitions currently support four levels of client-side sorting. Alternatively, if the data being sent from the server is already sorted, then theoretically the FVC will support as many levels of categorization as needed.

Note: It’s important to understand the performance implications of using multiple levels of categorization. Every additional level adds an nth degree of speed reduction in rendering the View since categorization is accomplished by traversing the dataset. The more data in the View, the more impact on performance.

Note: For any categorized View, whether the data is sorted sever-side or client side, the first category column needs to be listed in the first client-side sort field of the View Definition. This is an unintended byproduct of the categorization functionality and I will look to remove this requirement in the future

To demonstrate the server-sort, I created a View in the used car database of all Ferraris and sorted the first four columns, By MODEL, By STATE, By YEAR, and By CITY.

Click to open the View Definition
Click to open this View

Note: The category CSS for the Flexible View Control that handles category indentation only accommodates three levels of categories but its easy enough to add custom CSS for four levels and beyond:

/* level-3 refers to the 4th level since it's
 zero based indexing */
tr.group-level-3 td:nth-child(1) {
    padding-left: 75px;

tr[data-category-level="3"] td:nth-child(1) {


Again, not to beat a dead horse … but flexibility, reusability, and low-code-ability (I think I just made that word up) are qualities that make the Flexible View Control a game-changer for XPage development. The next post will talk about “Category Renderers”, a killer feature to enhance the UI and UX of categorized views.

Flexible View Control for XPages – Categorized Views

HCL Digital Week made very clear the emphasis in the application development space is on “low-code”. That got me thinking about the Flexible View Control and how, at the end of the day, it really is a low-code tool to add View data to XPage applications. Just like with Domino Volt, many aspects of configuring a View with the Flexible View Control are as simple as drag, drop, and configure.

Creating a categorized View is no different. Let’s take a look at just how easy it is by creating a View of all the Ferraris listed in the used car database. Naturally, we start by creating the View Definition:

The View Definition

The View Definition being used for the demos shown in this post can be found here.

I want to categorize this View by the MODEL column. Just like we do for a Notes View, I make that the first column. Next, check off the “category” checkbox in the last column of the MODEL column row.

A couple of important notes in the above sceenshot:

  1. The column to be categorized MUST be sorted.
  2. In the image above the MODEL column is being hidden. That might seem counter-intuitive since this is the column we are categorizing, but it will make sense in a moment.

The View XPage

Link to the demo used in this post.

BEFORE adding categorization

The image above shows what the Ferraris View looks like before adding the categorization.

The image below shows what the View looks like after adding the categorization and hiding the MODEL column.

AFTER adding categorization and hiding the MODEL column

How does it work?

To create the categorization the Flexible View Control traverses the DataTable during the drawCallback phase and when a row contains a different MODEL value, a new tr node is inserted before that row to represent the category. The reason why we can hide the MODEL column is the traversal is done on the DataTable dataset, not the actual HTML of the table. After inserting the tr node a click event is applied to provide expand/collapse functionality for the category.

Let’s break down the tr node that gets added for each category:

category tr element insert for each category
  • The data-category attribute is the unique identifier for this category. This value also gets applied to all of the child rows for this category.
  • “group” classes are added to aid in referencing the category rows.
  • The data-hide attribute is used to handle expansion/collapse of the category.
  • The table cell created inside the tr node is automatically given a colspan equal to the number of cells in a non-category row.

Doing the Twist(ie)

You probably noticed in the image above that there is a “twistie” node in the tr HTML. By default this is simply a “-” to denote expansion and a “+” to denote a collapsed category. This is handled with some simple CSS:

.twistie {margin-right:5px}
.twistie-collapse::before {

Adding a little more “flair” to the twistie is easy by simply overriding the CSS. For example, I’m a big fan of Font Awesome and use that for my twistie icons:

Using Font Awesome icons for twisities
	font-family: FontAwesome;
	content: "\f056";
	font-size: inherit;
.twistie-collapse::before {
	font-family: FontAwesome;
	content: "\f055";
	font-size: inherit;

Expand/Collapse All

Just as in a Notes View that contains categorization, the Flexible View Control provides the ability to expand or collapse the entire View. In our demo there are two buttons at the top that are wired with this functionality:

When the control is rendered functions are created dynamically that call functions contained in the csjsCCRestView.js script library:

collapseAll : function() {
expandAll : function() {

The code behind the buttons can then refer directly to a specific View, which we’ve named ViewBasic in the thisView property of the control for this demo:



One of the great features of DataTables is being able to enter a query in the search box and having the contents of the DataTable reduce to the records that match that query. This functionality still works when the View is categorized!

The next post will expand on the categorization functionality by introducing subcategories and a feature called “category renderers”.

Flexible View Control for XPages Updates

On the heels of the OpenNTF Webinar where I presented the Flexible View Control for XPages, I was able to make some long-overdue updates that improve the functionality and performance of the control.

Get the code from GitHub:

Release v2020-11-17

Updates in this release:

  • Improved the setup and configuration process to make it more intuitive, including adding an option to create the default (view-definitions) View Definition automatically.
  • Fixed and improved category/sub-category creation and expansion/collapse.
  • Cleaned up the View Definition UI.
  • Added additional client sorting fields in the View Definition. There is now a total of 4 levels of client-side sorting available.
  • Fixed totals options including totaling up for each category/subcategory and showing totals in the footer.
  • Added ability to total # of rows or total the values for a column in each row.
  • Added a category renderer option to the View Definition.
  • Added a footer callback option.
  • Reduced expression language evaluations on the ccRestView custom control.
  • Added a stats component to the View object to track the performance of different aspects of the control.

The demo site has been updated with the most recent version of the control. Blog posts expanding on/demonstrating the items listed above to follow soon!

Join me for an OpenNTF Webinar on Thu 10/22

On Thursday, October 22nd I will be presenting a webinar for OpenNTF about the Flexible View Control for XPages. Thank you to OpenNTF for the opportunity to demonstrate this content.

For more information and to register for the webinar, visit the OpenNTF website.

For a primer on the Flexible View Control, see the series here:


I look forward to (virtually) seeing you there!

This one trick helps you analyze your Notes data!

Cheesy click-bait headline aside, I did recently have a need to analyze millions of records in a database and get a feel for what type of non-alphanumeric characters existed in one field in particular.

What I came up with was an incredibly simple view that gives me a count of how often every combination of English non-alphanumeric characters appears in this one field.

column1: Categorized with the following formula:

str := "abcdefghijklmnopqrstuvwxyz1234567890 ";

column2: Totals column.

The results are… interesting, to say the least.

The “Not Categorized” category contains all the documents that do NOT have non-alphanumeric characters in field_name.

This type of analysis can be handy if you don’t have control over the input – for example, if your database contains a large amount of data from external sources or if users do a lot of copy and paste from external emails, Word docs, PDFs, and other files.

A Flexible View Control for XPages Part 10 – Multiple Views Using Tabs

If you’ve been a Domino Developer long enough, then you remember when embedded views were added to Lotus Notes (version 5?) and how it turned your world upside down with the information you could easily make available to users on one form.

Combining The Flexible View Control with Bootstrap tabs makes adding this type of functionality to your XPages application incredibly easy. The demo below uses the used car database to display a few different types of large SUVs on one XPage in different tabs. Each tab is sourced from the same view that contains 2.2 million documents but is categorized creatively to easily drill down to the desired data.

Now, you don’t have to use Bootstrap tabs (or any tabs for that matter) – I do since I’m using Bootstrap as my layout framework and this page has the standard out-of-the-box tab setup with a tab strip navigator and tab. The entire page layout looks like this:

<div class="level0-flex-container">
		<div class="level0-flex-item">
		<div class="level0-flex-view">
			<div class="level0-flex-item" style="padding-top:10px;background:#ddd">
				<ul class="nav nav-tabs" role="tablist">
					<li role="presentation" class="active" style="margin-left:25px;">
						<a href="#tahoe" aria-controls="tahoe" role="tab" data-toggle="tab"
							Chevrolet Tahoe
					<li role="presentation">
						<a href="#yukon" aria-controls="profile" role="tab" data-toggle="tab"
							GMC Yukon
					<li role="presentation">
						<a href="#escalade" aria-controls="messages" role="tab"
							data-toggle="tab" class="escalade">Escalade</a>
					<li role="presentation">
						<a href="#expedition" aria-controls="settings" role="tab"
							data-toggle="tab" class="expedition">Expedition</a>
			<div class="tab-content"
				<div role="tabpanel" class="tab-pane active flex-tab" id="tahoe"
					<Flexible View Control>
				<div role="tabpanel" class="tab-pane flex-tab" id="yukon"
					<Flexible View Control>
				<div role="tabpanel" class="tab-pane flex-tab" id="escalade"
					<Flexible View Control>
				<div role="tabpanel" class="tab-pane flex-tab" id="expedition"
					<Flexible View Control>

Flexbox, again

If you read Part 8 of this series, then you probably remember how important of a role Flexbox plays in the layout of the Flexible View Control. Getting the FVC to size properly in a Bootstrap tab panel is no different. We add a little extra CSS to make those tab panels flex so the view control fills up the available space in each tab:

.tab-content {	
.tab-pane.flex-tab {
.tab-pane.flex-tab {
	display:none !important;
.tab-pane.flex-tab.active {
	display:flex !important;

Now that the page is laid out we can add our view controls to each tab panel. The image below shows the FVC for the first tab, although the other three tabs are essentially configured the same.

Custom Control properties for the ‘Tahoe’ tab FVC

Remember: thisView must be unique! This is what allows multiple views to be added to one XPage.

An important takeaway from the highlighted areas in the screenshot above: loadOnInit: false. Setting loadOnInit directly in the control will override the value defined in the View Definition. We set it to false here because we want to build the table manually, and even though the page has four views, we only want to fetch the data for the ‘active’ tab when the page loads:

$( document ).ready(function() {
				params: 'category=make:Chevrolet:model:Tahoe&view=xspAll'
Remember: The thisView property of the FVC is used to construct a client-side JavaScript object at run-time.
  • When loadOnInit: true the control will automatically execute the .build() function.
  • When loadOnInit: false the DataTable can be built manually by calling [thisView].build().
  • Refresh a view at anytime by calling [thisView].build()

So how do we load the data for the other tabs? In Part 7 – Modals & Picklists, we talked about needing to wait until a modal was ‘shown’ before building the view in a Picklist to make sure that the DataTable sized correctly. Similarly here, to load a view in a tab we need to wait until that tab is ‘shown’. To do that, again, we tap into a Bootstrap event:

$('.tahoe').on('shown.bs.tab', function (e) {
	// Even though this view is built on page load
	// the shown property can be set so the view refreshes
	// when this tab is made active again

$('.yukon').on('shown.bs.tab', function (e) {
$('.escalade').on('shown.bs.tab', function (e) {
$('.expedition').on('shown.bs.tab', function (e) {

What about those .build() parameters?

As you probably noticed in the snippets above, we didn’t define the keys/category for the views within the control. We could have, but the FVC makes it possible to pass options to the .build() command. This is a huge part of what makes the Flexible View Control so ‘flexible’. More in this in the next blog post!

A Flexible View Control for XPages Part 9 – Using Scroller for Large Views

Up to this point, the examples I’ve created to demonstrate the capabilities of the FVC have used fairly small sets of data. In a real-world production scenario there is going to be a need to display datasets of various sizes, including thousands of records. So how does the FVC scale for larger sets of data?

Understanding DataTables

Comprehending what is going on behind the scenes of DataTables is vital to developing a strategy to handle large sets of data efficiently. Similar to XPages, DataTables has a “lifecycle” where a series of events occur in the initialization of the table. Previously, I created a bunch of “pure” DataTables demos, one of which highlights the various phases (callbacks) of the lifecycle (turn on your developer console):


DataTables Lifecycle

The image above shows browser consoles statements to demonstrate what the lifecycle looks like for a DataTable with 25 records. Take a close look and you’ll see that drawCallback executes multiple times. This means DataTables has drawn the table twice as part of the initialization. A good comparison to this in the XPages lifecycle is the renderResponse phase.

Now, let’s focus on the rowCallback and createdRow callbacks. In this table with 25 rows each of these is called every time DataTables processes a row of data. Normally, you are likely to only use one or the other – both are included here for demonstration purposes. Regardless of which callback you use, the code that resides there will be executed for each row. If your DataTable is replicating the functionality of a Notes View, then you’re likely to have click and double-click events at a minimum.

Now imagine you have a view with a large amount of data – thousands of records, for example. Your rowCallback is now executing THOUSANDS of times, resulting in slower rendering of your DataTable and negatively impacting the user experience. YIKES!

Large Datasets – Notes Views vs. Web Views

Anyone that has used Lotus Notes and Domino for years has been spoiled by having views with thousands (or millions) of records, nicely indexed, and extremely performant as you click on columns to re-sort or search the view based on a key. This is one thing that Notes does really, really well, but sending thousands to millions of rows to a view in a web application can seem like a fool’s errand.

While I certainly continue maintaining large Notes views on the back-end, I use categories, keys, and server-side searching to make it easy for users to target the information they need so only a small subset of information is sent to the client-side web view.

I feel like my magic number is about 5000 records or less. Any more than that and users are waiting too long. Obviously, there are going to be use cases where that is not possible and there are other factors to consider, such as server processing power and bandwidth between the client and server.


Paging is one way to deal with large amounts of data and can be turned on with a simple paging: true as part of the initialization options within DataTables. But let’s say you have 4000 records and 50 records show up on each page. This results in 80 pages. Do you want to page through 80 pages of data? Do you want your users to page through 80 pages of data? NO!

Enter Scroller

I’ve said this before in previous posts and presentations but it bears repeating again – the more I use DataTables and the more I have challenging business requirements that at first glance seems like I might have to hack a solution together, ultimately DataTables has a fairly simple way to achieve the desired result. Displaying large amounts of data in a performant way is no different.

DataTables has a plugin called Scroller that does a great job of managing large amounts of data. It does so by only “drawing” the rows that are visible within the scroll body of the table plus extra rows so when the user starts scrolling the table is able to respond quickly. As the user scrolls, the scroller plugin draws more rows. All of the features of DataTables still work as you expect them to – filtering, sorting, etc. Technically, DataTables is using paging functionality behind the scenes to manage the data,

Enabling Scroller in the FVC

The Flexible View Control has the ability to utilize Scroller. Do the following to turn it on:

  1. Make sure you are loading the Scroller plugin resources. I’m loading the plugin via a CDN in my theme:

2. You must have showFixedHeader: true on your FVC. Scroller will not work properly without fixed headers being turned on.

3. Set scroller: true in the FVC custom properties.

showFixedHeader must be ‘true’ for scroller to function properly


This page contains multiple demos that illustrate the dramatic effect scroller has on loading large amounts of data.

Focusing on the first line of the page above, these two sets of console statements highlight the significant difference in loading time between two tables that have the same ~5000 records, one without scroller, one with scroller.

table builds in ~11 seconds WITHOUT scroller enabled
table builds in ~1 second WITH scroller enabled
  • The first thing we notice is the data is retrieved from the server in the same amount of time in both scenarios.
  • In the first console image where scroller is not enabled, every row is being processed by DataTables at initialization, which drastically slows down the table build vs. the second console image, where scroller is enabled.
  • Finally, we see a dramatic difference in the build times between the two tables. 11 seconds is a lifetime compared to 1 second and as tables get bigger the 10-second difference will grow in magnitude.

Some Minor Side-Effects

Using scroller does come with a couple of side-effects, but they are fairly minor and shouldn’t have much of an impact.

Since the scroller plugin relies on precise calculations to build the table rows, it forces each row to be the same height. This is really only a problem if you have one column that has one or more records that have extraordinarily long data. Scroller will stretch that column out so all of the data can fit within that row height.

Luckily, the FVC View Definition makes it easy to overcome this. All you need to do is explicitly define a max width for your column and check the “Hide Overflow” box:

The result is your column tries to honor the width you’ve assigned and any text that exceeds that width is truncated with an ellipsis:

The other noticeable side-effect is simply cosmetic and can easily be overridden with CSS. When a table does not have enough data to fill the table body, scroller fills the white space with a background image:

If your users don’t like it, you can use the following CSS to override the background to be whatever you want:

div.dts div.dataTables_scrollBody {


The numbers above are pretty clear – using scroller will greatly improve the performance of the Flexible View Control when your table has a large amount of data and a difference can be seen with as few as 2-300 records. Every view in my application now has scroller enabled.

A Flexible View Control for XPages Part 8 – Putting the 'Flex' in Flexible

Thus far, all of my demonstrations of the Flexible View Control have been centered around starting from scratch and integrating the view control into a mostly blank page, aside from a basic Bootstrap layout. Even the basic Bootstrap layout I’ve been using contains some very important elements that are integral to the control sizing properly, whether you are starting with a blank page or trying to integrate into an existing application.

Enter Flexbox

For years I read about Flexbox and how it can simplify the layout of pages and I always managed to push learning it to the back burner. Once I did all I could think is WHY DIDN’T I LEARN THIS SOONER?!?!? Prior to that, I probably did an embarrassing amount of dynamic element sizing with JavaScript. I can’t stress this enough … Flexbox has fundamentally changed how I approach application layout.

So what exactly is Flexbox?

The Flexbox Layout (Flexible Box) module (a W3C Candidate Recommendation as of October 2017) aims at providing a more efficient way to lay out, align and distribute space among items in a container, even when their size is unknown and/or dynamic (thus the word “flex”).

from css-tricks.com

An important concept to understand is flexible containers are not an “all-in” or “all-out” proposition. You can mix flexed elements with non-flexed elements on the same page, depending on what you’re trying to accomplish.

DataTables and Fixed Headers

Before showing how the FVC integrates Flexbox, a little background. When displaying view data in an application, if the amount of data in the view requires the user to scroll, having fixed headers naturally creates a better user experience. This can be achieved in out-of-the-box DataTables in two ways:

  • By adding the parameter ‘scrollY’ to the configuration options with a px value > 0. For example, “scrollY”: “200px”
  • By utilizing the DataTables Fixed Header plugin

The pros and cons of each based on my experience are below.

scrollYBuilt into DTRequires a hard-coded
height value.
Fixed Header pluginAlso can create a
fixed footer
Requires loading an
additional 30k js file, does not handle responsiveness well

Before using Flexbox for dynamic sizing, the Flexible View Control used the scrollY method to define an initial height and then after the table was rendered, the height of the scroll body would be recomputed so that the table would fit into the parent container. This involved a lot of taking measurements of various elements such as the table offset, header height, filter height, info height, etc. and creating a formula to calculate the correct height needed for the scroll body. It worked great but I really wanted a “cleaner” way to layout the table elements… and I found that with Flexbox.

The Flexible View Control and Fixed Header/Footer

The FVC has a parameter showFixedHeader which is set to true by default. When fixed headers are enabled, the control creates the scrollY option parameter for DataTables at runtime. The value is irrelevant .. the purpose is for DataTables to trigger the dom changes needed to make the table body scrollable:

The key elements here are dataTables_scrollHead, dataTables_scrollBody, and ffDefault. The first two are created by DataTables at initialization. The third, ffDefault is created by the FVC.

Now that we know what the elements are, we need to get them to “flex” by overriding their default styling. The snippet below displays the CSS from the FVC that allows us to accomplish that.

 DataTables overrides 
.dataTables_wrapper {
.dataTables_scroll {
.dataTables_scrollBody {
.dataTables_scrollHead {
	flex-basis: auto;
	flex-grow: 0;
.dataTables_filter input {
    display: inline;
    width: 250px;
    margin-left: 5px;
div.dts {
	display: flex !important;
.ffDefault {
	height:35px; /* Not required */

With the CSS above we have the DataTables dom elements configured to flex. But we need to do some more flexin’ to get the layout to flow the way we want. Continuing to work our way up the dom hierarchy, we flex the wrapper that the FVC places around the DataTables dom:

.panelRestViewContainer {

Remember the ultimate goal

We want our view to fit in the space being provided by the flexed layout without having to hardcode dimensions or compute dimensions dynamically. This requires the top-most flexed container defining its height either through pixels or through the viewport height (vh). The demos being used throughout this series (such as this) use the following dom hierarchy (simplified for illustration):

	<div class="level0-flex-container">
		<div class="level0-flex-item">
		<!-- Flexible View Control -->
		<div class="panelRestViewContainer">

body, form {
	/* very important! */
.level0-flex-container {
	height:100vh; /* will fill the window viewport */
.level0-flex-item {
	flex-basis: auto;
	flex-grow: 0; /* this element will size to its content */

What this all means:

  • Body and form elements will not scroll
  • Which allows for the flex container to fill the viewport
  • The navbar, with flex-grow: 0, will only get as tall as its content
  • The Flexible View Control, with flex-grow: 1 (through the shorthand flex:1 property) will expand to fill the remaining empty space in the container.

The end result is our view filling the screen with scrollable content, just as we wanted.

More Layout Demos

The demos below illustrate how easy it is to create several different flexible layouts and to add the FVC to XPage layout controls:

  1. Starting with a flexible container
  2. Adding a header
  3. Adding the Flexible View Control
  4. Add an action bar
  5. Add a footer
  6. Add a left-side navigator
  7. Add a footer
  8. Add a right sidebar
  9. FVC with NO fixed header in a flexible layout
  10. Adding the Flexible View Control to an Editable Area
  11. Adding the Flexible View Control to an Application Layout control

A Flexible View Control for XPages Part 7 – Modals & Picklists

While I intended to get to this blog post eventually, the urgency of doing so was increased due to the following post on StackOverflow:


The OP also reached out to me directly looking for an answer so why not crank out a blog post with the answer?

By combining the Flexible View Control with a Bootstrap modal, you can easily mimic the Lotus Notes Picklist functionality. Getting the control working in a Bootstrap modal requires only a few simple steps, illustrated in this working demo.

Step 1 – Add the modal source to your page

I start with something like this:

<!-- Modal -->
		<div class="modal fade picklist" id="myModal" tabindex="-1" role="dialog"
		<div class="modal-dialog" role="document">
			<div class="modal-content">
				<div class="modal-header">
					<button type="button" class="close" data-dismiss="modal"
						<span aria-hidden="true">x</span>
					<h4 class="modal-title" id="myModalLabel">Choose a record</h4>
				<div class="modal-body" style="display:flex;">
					<!-- place the Flexible View Control here -->
				<div class="modal-footer">
					<button type="button" class="btn btn-default" data-dismiss="modal">
					<button type="button" class="btn btn-primary">OK</button>

For the most part, the code above is straight off the Bootstrap site with a few important additions:

  • I added the class ‘picklist’ to the ‘modal’ div.
  • I add an inline style to ‘modal-body’ of ‘display:flex;’. This is very important since the control uses Flexbox (more on that in a future post).

Step 2 – Add the control to your page

Drop the control in the space bookmarked in the source above and configure the control as you do for a normal view page. Make sure you keep the modal-body intact!

Step 3 – Check your View Definition

It is highly recommended to have the Load on Init property unchecked. You will see why in a moment.

Step 4 – Resize the modal

By default, the Bootstrap modal is pretty small. Of course, the dimensions can be changed with css, but how do you deal with different screen sizes? By adding an event handler to the ‘show’ event of the Bootstrap modal, you can resize the modal to a percentage of available screen space:


			<xp:this.value><![CDATA[$(".picklist").on('show.bs.modal', function () {

	var mbHeight = $(window).height() * 0.8;
	$('.modal-content').css('overflow-y', 'auto'); 
	$('.modal-body').css('height', mbHeight-50);
	$('.modal-body').css('overflow', "auto");
	$('.modal-dialog').css('width', $(window).width() * 0.9);


In the code above, my modal-body will be 80% of the window height and 90% of the window width.

Step 5 – Load the view after the modal is opened

This is an important step due to the way DataTables draws the table and dynamically sizes the table and its cells. Initializing the table too soon results in a table that is not formatted properly:

Badly formatted DataTable when view is built before the modal has been completely rendered.

In this example above, the view is built during the ‘show’ event, resulting in the table header dimensions not matching up to the actual data rows.

Lucky for us, Bootstrap has another event that we can tap into. You may have noticed it in the screenshot above. The ‘shown’ event fires AFTER the modal has been rendered to the user:

By adding 3 lines of code to the scriptBlock above and tapping into the ‘shown’ event, our table will be built with the proper layout dimensions.

$(".picklist").on('shown.bs.modal', function () {

In the snippet above, viewBasic refers to the ‘thisView’ property of the control. Since the ‘Load on Init’ property is unchecked on the View Definition, the view needs to be triggered by its ‘build’ function.

Step 6 – Patch the csjsCCRestView.js file

After completing the steps above, the modal will work fine and the table will load and be fully functional. However, in creating this demo I realized there is an adjustment that needs to be made to the source code. This is due to legacy code that was used when many aspects of the control were being sized dynamically with javascript prior to implementing Flexbox.

The issue is the footer is being calculated based on the window’s dimensions and not the modal’s, resulting in the width of the interior of the modal being wider than the modal container. This will be updated on the github repo, but in the short term you can fix it in your database by updating the buildFooter function in the ccRestView.js file with the code below. The fixes are labeled ‘//MODAL FIX’. A few lines are commented out and a few are altered.

buildFooter : function(o) {
		if (o==null) { return; }
		if (o==undefined) { return; }
		console.log("=== start build footer for " + o.thisView + " ===")
		if ($(".ffDefault",$(".panel"+o.thisView)).length > 0) {
			// check to see if the info is in the footer.
			// if so, set it aside and pick it up later
		// MODAL FIX	$(".ffDefault",$(".panel"+o.thisView)).css("max-width",$(".dataTables_scrollBody table",$(".panel"+o.thisView)).outerWidth()+"px")
		// MODAL FIX	$(".ffDefault",$(".panel"+o.thisView)).css("min-width",$(".dataTables_scrollBody table",$(".panel"+o.thisView)).outerWidth()+"px")
		var cellCount = $("th",$(".panel"+o.thisView + " " + o.dataTableClass + " thead")[0]);
		footerAttachTo = o.viewport == "" ? "body" : o.viewport;
		var tableWidth = "100%"; //$(".panel"+o.thisView + " tbody").outerWidth();
		var tdCells = [];
		var rowOne = $(".panel"+o.thisView + " tbody tr");
		//console.log("rowOne length=" + $(rowOne).html())
		// create footer cells to match header
		for (x=0;x<cellCount.length;x++) {	

			cell = $("td",rowOne).length == 1 ? $("td",rowOne) : $("td",rowOne)[x]

			var paddingLR = ($(cell).outerWidth() - $(cell).width())/2 + "px";  // css("width") //$("td",rowOne).css("padding");   // ($(o.cellCount[x]).outerWidth() - $(o.cellCount[x]).width())/2 + "px";
			var border = ($(cell).outerWidth())
			tdCells.push("<td data-column='" + o.itemIndexes[x] + "' class='" + o.itemNames[x] + "Foot' style='width:" + $(cell).width() + "px;padding:" + paddingLR + "'> </td>");  //padding-left:" + paddingLR + ";padding-right:" + paddingLR + "

		var pos = $(footerAttachTo).position();
		// get the width of the fixed header so we can apply it to
		// the footer
		width = $(footerAttachTo).css("width");
		footerAtachTo = footerAttachTo != "" ? footerAttachTo : "body";
		//console.log("attaching footer to " + footerAttachTo);
		var footerHtml = "<div class='ffDefault " + o.ffClass + "' style='width:" + width + "'><table style='width:" + tableWidth + "px' class='fixedFooterTable cell-border'><tfoot><tr>" + tdCells.join("") + "</tr></tfoot></table></div>";
		if (o.showFixedHeader) {
			width="auto"; // MODAL FIX
			var newFooter = $(".dataTables_scroll",$(".panel"+o.thisView)).after("<div class='ffDefault " + o.ffClass + "' style='width:" + width + "'><table style='width:" + tableWidth + "' class='fixedFooterTable cell-border'><tfoot><tr>" + tdCells.join("") + "</tr></tfoot></table></div>");
		} else {
			width="auto"; // MODAL FIX
			var newFooter = $(".panelRestView",$(".panel"+o.thisView)).append("<div class='ffDefault " + o.ffClass + "' style='width:" + width + "'><table style='width:" + tableWidth + "px' class='fixedFooterTable cell-border'><tfoot><tr>" + tdCells.join("") + "</tr></tfoot></table></div>");
		if (footerAttachTo != "body") {
		  if (pos) {
			left = $(footerAttachTo).css("position") == "relative" ? 0 : pos.left;
	// MODAL FIX	$(".ffDefault",$(".panel"+o.thisView)).css("max-width",$(".dataTables_scrollBody table",$(".panel"+o.thisView)).outerWidth()+"px");
	// MODAL FIX	$(".ffDefault",$(".panel"+o.thisView)).css("min-width",$(".dataTables_scrollBody table",$(".panel"+o.thisView)).outerWidth()+"px");
		console.log("=== end build footer for " + o.thisView + " ===")

In conclusion

Integrating the Flexible View Control with a Bootstrap modal is easy and provides all of the functionality of DataTables you’d expect – filtering, sorting, etc.


For applications where multiple Picklists are needed with different data, you can create a modal custom control with custom properties that can be fed into the view control dynamically. I have one application that has at least 20-25 different Picklists and this method works great.

A Flexible View Control for XPages Part 6 – Sorting

When I started building the Flexible View Control, one of the driving forces was to significantly reduce the amount of back-end Domino views that would collect in my applications. I was just as guilty as anyone of creating a new view with the same selection formula and columns as an existing view with the only difference being the column arrangement or how the new view was sorted/categorized.

Build a Client-Side Sorting Demo

If you’ve used DataTables then you know that by default DataTables makes sorting your data by any column very easy. As a result, The Flexible View Control, being a mashup of Domino XPages and DataTables, has advanced sorting capabilities baked in.

Create a new XPage

To demonstrate this, I start by making a copy of my viewBasic XPage and call it viewBasicSorting.

Since I want to re-use this new XPage for multiple demos, I’m making my viewKey dynamic by having it look for a querystring parameter to get its value:

Using a url parameter to populate the viewKey

This means anytime I load this XPage I need to supply the name of the desired View Definition in the url:


Create the View Definition

Now I create my new View Definition. Note that we are using an existing view and existing rest service to fetch the data.

View Definition with default sorting

In the screenshot above, there is no value in the Client Sort (1) field, meaning when the DataTable is built client-side it will display in the order the data was loaded from the rest service. If your back-end data is sorted by the 1st column, then that is how the data will display in the constructed DataTable.


By default the id column is sorted

Adding a client-side sort

But what if your users insist on another view that sorts by Price with the Price column being first? In Notes/Domino, you begrudgingly copy your view, move the Price column to the front and make sure it’s sorted. UGH!

This is where the “Flexible” part of the Flexible View Control comes in. All I have to do is create a new View Definition, point it to the same data, and simply drag the Price column to the top and designate that as the sort column in the Client Sort (1) field. In my case I want to sort it descending.

View Definition sorted by price
Important: The value entered in the Client Sort must match the itemName of the column you want to sort.

The results

Now, I can load my view viewBasicSorting XPage and simply change the viewdef url parameter to show a different “virtual view”, in this case By Price:


Same data from same view now sorted by price


Using one back-end view, and one XPage equipped with the Flexible View Control, we can display countless views to the front-end user by simply making a View Definition for each front-end view we need to display. Cool!

Advanced Sorting

When specifying the column to sort in a View Definition we use the Client Sort (1) field. But you may have noticed a field below that named Client Sort (2) and perhaps you wondered what purpose this serves.

DataTables has incredible advanced sorting capabilities, as the gif below illustrates.

Advanced DataTables sort capabilities

To get the multiple column sorting capability in the Flexible View Control, we use the Client Sort (2) field. To demonstrate I created a new View Definition that points to the same Domino view we’ve been using. This time, I make the Client Sort (1) by Year (descending) and add Price as a secondary sort, also descending.

When I look at the By Year view I can see that it has a primary sort (by year) and secondary sort (by price).

Sorting Server-side

My preference is to do all sorting client-side and have my back-end views act as fairly static tables of data that are indexed well and can respond quickly to requests. The View Definition does have a “Server Sort” field but it currently is not operable.

However, if you do need to sort server side before returning data view a rest service or xAgent you can certainly do so.

Important: to resort a back-end view server-side you must set the column sort setting to “both” for any column you want to be able to resort by.

To demonstrate sorting the price column I start by creating a new View Definition with pretty much all the defaults except I move the Price column to the first position (Note: the column you want to be the first sort does NOT have to be in the first position).

Next, I update the rest service that has been used repeatedly to look for the sortColumn and sortOrder query string parameters:

<xe:restService id="restService1" pathInfo="used-cars-phil-chevy-tahoe-basic"
			<xe:viewJsonService systemColumns="2"
				viewName="xspPhilPaChevTahoe" defaultColumns="true" count="1000">
				<xe:this.sortColumn><![CDATA[#{javascript:context.getUrlParameter("sortColumn")!="" ? context.getUrlParameter("sortColumn") : ""}]]></xe:this.sortColumn>
				<xe:this.sortOrder><![CDATA[#{javascript:context.getUrlParameter("sortOrder")!="" ? context.getUrlParameter("sortOrder") : ""}]]></xe:this.sortOrder>

Finally, the Flexible View Control has a queryString parameter that can be utilized to pass url parameters to the rest service being called to retrieve data.

context.getUrlParameter("viewdef") == "used-cars-sorting-server-byprice" ? "&sortColumn=PRICE&sortOrder=descending" : ""

The Results


Price column sorted server-side

While we get the desired result, notice that there is no indication in the column header that Price is the sorted column. Only by examining the data can you determine that.