MWLUG Slides – AD114 Take Your XPages Development to the Next Level

In this session at MWLUG 2015, Paul Calhoun and I dug into a number of features already available in XPages, but not as widely used.

We only had time to cover half of the sections, but the full set of slides is available here

Topics include:

  1. Application Responsiveness (primarily JSON RPC)
  2. SSJS Tips
  3. Modifying Component output
  4. Java Tips
  5. Custom Controls (including dynamic field binding to make reusable fields)
  6. Resources for learning more
  7. Debugging Tips
  8. Event handlers
  9. Dojo

Updating Select2 Styling to Match Bootstrap Fields

Select2 is an awesome jQuery plugin to enhance the functionality of combo boxes. But if you implement it in an application with a Bootstrap UI, you’ll notice that the styling is not consistent. In this post, I’ll show how to make the styling of Select2 fields consistent with other fields on the form.

Bootstrap Default Styling

Here’s an example of a simple Bootstrap form, including two combo box fields. The styling is very consistent across all of the fields.

Select2 A - Default Drop-Down

Select2 Default Styling

Once I implement Select2 and add the code to initialize combo boxes as Select2 fields, they styling changes. The width and internal padding and line spacing are not consistent with other bootstrap fields. (Click image to enlarge in order to see the difference more clearly.)

Select2 B - Default Select2 Style

Consistent Styling

Fortunately, by inspecting a standard bootstrap field either in the bootstrap CSS or in the browser’s developer tools, I can see the styles in use and replicate them for a more consistent look.

Select2 C - Consistent Select2 Style

Here is the CSS required to make it happen:

.select2-container {
  width:100%;	
}

.select2-container a.select2-choice {
  font-size: 14px;
  height: 38px;
  padding: 8px 12px;
  line-height: 1.42857;
}

.select2-container .select2-choice .select2-arrow {
  padding-top:6px;
}

The first rule makes the field width use 100% of the size of its container, as bootstrap fields do.

The second set of rules sets the font and field styling to make them consistent.

The third rule vertically centers the drop-down arrow for the larger field.

Fixing an Issue with Glyphicons with a Bootswatch Theme in XPages

In this post, I’ll describe an issue that I had with glyphicons when using a Bootswatch theme in XPages and how to fix it.

When you use a Bootswatch theme, you do not need to separately include the original
bootstrap.css file. In fact, it would be inefficient to do so, because the styling is provided by the Bootswatch CSS file.

When adding any additional Javascript libraries or CSS to an XPages application, I generally put them in their own folder under WebContent and then include the library or stylesheet via the application theme.

WebContent

However, I noticed an issue when doing this recently. The Bootswatch theme styling loaded fine, but glyphicons on the page were
not displayed properly; the unrecognized font character symbol was displayed instead.

Glyphicons are loaded as a font (much like Font Awesome), so I looked through the stylesheet to see how the font was included. As you can see, the reference is relative to the current directory, assuming a standard Bootstrap directory structure.

@font-face {
  font-family: 'Glyphicons Halflings';
  src: url('../fonts/glyphicons-halflings-regular.eot');
  src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
}

There are two ways to go about fixing this:

  1. You can update the font references to be relative to the actual directory structure.
  2. Much more simply, you can move the bootswatch CSS file into the css directory under the bootstrap directory so the relative font file references work without being modified.

XPages Tip: Displaying View Panel Sort Icons on a Separate Line

If there isn’t enough room to display both the column title and sort icons in all column headers in a view panel, you can end up with an uneven look. In this post, I’ll show how to use CSS to move the sort icons down to a new line.

Column Header Layout

If you’re using a view panel with sortable columns, you will ideally not have too many columns and instead have enough screen real estate that the sort icons don’t make a difference.

View Panel Sort Icons - 1

However, a common scenario is that there are a lot more columns crammed in and some (but not all) sort icons wrap to a new line. This ends up causing an uneven look.

View Panel Sort Icons - 2

In addition, the icons take up space, so you may have columns that have a 1- or 2-letter code, but the sort icon makes the column 2-3 times wider than it needs to be, so you lose precious real estate.

If that happens and you’d like to make them consistent, you can use CSS to always move the sort icons to the next line.

Column Header Source

In order to apply CSS to adjust the layout, we need to view the page source to see what’s being generated.

This snippet contains the tags that start the view panel (line 1), start the column header row (lines 2-3), and display the first column header (lines 4-16).

<table id="view:_id1:viewPanel1" class="xspDataTable">
  <thead>
    <tr>
      <th scope="col">
        <div class="xspPanelViewColumnHeader">
          <span>
            <a id="view:_id1:viewPanel1:viewColumn1:__internal_header_title_id" href="#" class="xspPanelViewColumnHeader">
              <span class="xspPanelViewColumnHeader">Group</span>
            </a>
          </span>
          <span>
            <img id="view:_id1:viewPanel1:viewColumn1:__internal_header_sort_icon" src="/domjava/xsp/theme/common/images/sort_none.gif" 
alt="Sort Toggle" title="Sort Toggle" class="xspImageViewColumnHeaderSort">
          </span>
        </div>
      </th>

This shows that a column header div contains a span with the column title and another span with the sort icon.

Adding a New Line with CSS

This gives us enough information to know how to target the proper element with CSS. There are several ways to go about it. My example will target the table (with class name), div (with class name), and then get the first span tag. It will add the new line after the first span.

table.xspDataTable div.xspPanelViewColumnHeader span:first-of-type:after {
  content: '\A';
  white-space: pre;
}

The '\A' in the content attribute denotes a new line with CSS. It does not work to put a <br> tag there. The white-space attribute is also required or you may not see any difference.

Now, all of the sort icons appear on a separate line.

View Panel Sort Icons - 3

You can tweak it by adjusting the space between the lines or centering, etc, but this gets the ball rolling.

Also, if you want to modify the images themselves, you can target .xspImageViewColumnHeaderSort with CSS.

XPages Tip — Adding Attributes to the HTML Tag

By default, the HTML tag rendered for an XPage only includes an attribute for the page language (<html lang="en">). In this post I’ll show how to add additional attributes to the HTML tag rendered for the page.

Adding an Attribute

You can add an attribute to the HTML tag via the attrs property, which can be found in All Properties > basics > attrs. Click the plus (+) button to add an attribute and then specify the attribute name and value.

HTML Attribute

Here is what’s added to the page, below the tag:

<xp:this.attrs>
  <xp:attr name="myAttribute" value="myValue"></xp:attr>
</xp:this.attrs>

When the page is loaded in the browser, you can see the attribute in the source:

<html lang="en" myAttribute="myValue">

Usage

One handy use for this is the HTML5 manifest attribute, which specifies the location for caching the page for offline and faster access.

Here’s an example:

<xp:this.attrs>
  <xp:attr name="manifest" value="myCache.appcache"></xp:attr>
</xp:this.attrs>

Note: If you want to use an HTML5 feature, you need to change the HTML doctype to HTML5 on the Page Generation tab of the Xsp Properties design element.

Default doctype:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

HTML 5 doctype:

<!DOCTYPE html>

There are other global attributes that can also be applied to the HTML tag, such as id, style, dir (page direction – left to right or right to left), but there are other built-in properties for those on an XPage, so there isn’t a need to set them with a page-level attribute.

Note

In order to be applied to the HTML tag, the attribute must be defined at the XPage level. If added to a custom control, they’ll become attributes of a <div> tag.

Gridx in XPages – 33: Adding a Column Header Menu

Another handy feature that you can add to Gridx is a popup menu from the column headers. In this post, I’ll show how to build a menu and add it to the grid.

Gridx 33 A - Header Menu

Gridx Series

Gridx in XPages — Entire Series

Include the HeaderMenu Module

As always, you have to include the required gridx module (gridx.modules.HeaderMenu) and add it to the grid object’s modules list.

modules: [
  Resizer,
  NestedSort,
  Filter,
  FilterBar,
  QuickFilter,
  VirtualVScroller,
  Persist,
  HeaderMenu
]

Build the Menu

Create a new dijit.Menu object to build the menu. You don’t need to add any resources to the page because, even though dijit.Menu and dijit.MenuItem are not automatically made available in XPages, they are made available by Gridx.

This code creates a menu and adds 3 items to it. Each item has a simple onClick handler that displays an alert.

  var headerContextMenu = new dijit.Menu();
  headerContextMenu.addChild(new dijit.MenuItem({
    id: 'menu1',
    label: 'Option 1',
    onClick: function() {
      alert('Clicked menu item: ' + this.label);
    }	
  }));
  headerContextMenu.addChild(new dijit.MenuItem({
    id: 'menu2',
    label: 'Option 2',
    onClick: function() {
      alert('Clicked menu item: ' + this.label);
    }	
  }));
   headerContextMenu.addChild(new dijit.MenuItem({
    id: 'menu3',
    label: 'Option 3',
    onClick: function() {
      alert('Clicked menu item: ' + this.label);
    }	
  }));

Add Menu to Column Definition

Now that the menu is ready, all you have to do is add it to one or more columns via the menu attribute.

var columns = [
  {
    id: 'first',
    field: 'firstname',
    name: 'First',
    width: '70px',
    menu:headerContextMenu
  }
]

You can add the same menu to all columns or you can create different menus for different columns.

Selecting a Menu Option

When you hover over a column header of a column that has a menu, a solid downward-facing black triangle is visible on the right-hand side of the column header. (Note: The mouse cursor isn’t in the screen shot, but it was hovered over the column header.)

Gridx 33 B - Menu Icon

Here’s the strange part — the interface is not intuitive; left-clicking on the triangle does nothing but select the triangle image.

The menu will be displayed when your right-click on the triangle. (See screen shot near beginning of the post.) Then you can left-click on a menu option to select it.

Gridx 33 C - Alert

Gridx in XPages – 32: Editable Cell Widgets

In the last couple of posts in this series, I showed how to edit data inline and save the changes. But you’re not limited to plain text fields. In this post, I’ll show how to make cells editable with additional input widgets.

Gridx Series

Gridx in XPages — Entire Series

Adding Input Widgets

There are generally 3 steps to the process:

  1. Include the CellWidget module
  2. Include your chosen input widget module (if necessary)
  3. Set the column properties to include and configure the widget

To add any kind of widget for inline data input, you need to first required the CellWidget module and add it to the grid.

To do so, include "gridx/modules/CellWidget" in the require statement (or at the page level) and then also include it in the grid’s modules list.

The next two steps will be specific to the widget that you choose. Below are examples of using a combo box, date picker, and number spinner.

Combo Box

The dijit.form.ComboBox module is automatically included within XPages, so you do not need to require it.

In this example, I’ve added it to the state column. Here’s the updated column definition:

{ id: 'state', field: 'state', name: 'State', width: '80px', 
  editable: true, 
  editor: 'dijit.form.ComboBox',
  editorArgs:{
    props:'store: stateStore'
  } 
},
  • The editable property tells the grid that the column is editable
  • The editor property tells the grid what widget to use when editing
  • The editorArgs property is an object that includes properties required to configure the editor

The ComboBox requires a memory store with the list of choices, so that needed to be set up earlier in the code.

It uses the same MemoryStore module that’s already included for the grid to use. Each option must have a name and unique id.

Note: Do not declare the memory store object as a var. This causes the grid to not be able to find it.

stateStore = new MemoryStore({
  data: [
    {name:"FL", id:"FL"},
    {name:"GA", id:"GA"},
    {name:"IL", id:"IL"},
    {name:"NJ", id:"NJ"},
    {name:"PA", id:"PA"}
  ]
});

Now, when a cell in the State column is put into edit mode, there is a combobox with a list of choices to choose from.

Gridx 32 - ComboBox Widget

Date Picker

If you want to let the user change a date value, it is very handy to include a date picker.

Here are the properties that I added to the date column in order to enable a date picker in my example:

  editable: 'true',
  editor: 'dijit.form.DateTextBox',
  editorArgs: {
    fromEditor: storeDate
  }

I didn’t have to require the dijit.form.DateTextBox module because it’s available already within XPages.

Since it’s a date field, I needed to add a fromEditor function to parse the data before it’s stored. (See this post on formatting date columns for more information on date parsing.)

This function will return a date string that works with the formatting code already on the column:

function storeDate(storeData, gridHandle) {
  if (typeof storeData == 'object') {		
    return storeData.toLocaleDateString();
  } else {
    return '';
  }
}

Gridx 32 - DatePicker Widget

Number Spinner

For the last example, I added a NumberSpinner to a cell. This module is not automatically available in XPages, so it needs to be included.

One of the ways to do that is at the page level with a Dojo Module Resource:

<xp:this.resources>
  <xp:dojoModule name="dijit.form.NumberSpinner"></xp:dojoModule>
</xp:this.resources>

Now that the module is available to the page, it can be used for cell input.

In this example, I have a Rating column that has a number spinner that allows values from 1 to 5.

{ id: 'rating', field: 'Rating', name: 'Rating', dataType: 'number', width: '75px', 
  editable: true, 
  editor: 'dijit.form.NumberSpinner',
  editorArgs:{
    props: 'smallDelta: 1, constraints: {min:1, max:5, places:0}'
  } 
} 

Gridx 32 - NumberSpinner Widget

There are many more options; hopefully, these examples make it much easier to implement any others that you need.

Follow

Get every new post delivered to your Inbox.

Join 75 other followers