Quantcast
Channel: XPages – Xcellerant
Viewing all 216 articles
Browse latest View live

Creating a Dojo Filtering Select Control in XPages

$
0
0

If you’d like to provide users the ability to type directly into an XPages combo box (like Dialog List field in the Notes client), you can easily convert combo boxes to dojo filtering select controls in XPages.

Combo Box

Let’s start with this straightforward combo box:

FilteringSelect_2

<xp:comboBox id="comboBox1">
  <xp:selectItem itemLabel="Acai"></xp:selectItem>
  <xp:selectItem itemLabel="Apple"></xp:selectItem>
  <xp:selectItem itemLabel="Apricot"></xp:selectItem>
  <xp:selectItem itemLabel="Artichoke"></xp:selectItem>
  <xp:selectItem itemLabel="Asparagus"></xp:selectItem>
  <xp:selectItem itemLabel="Avocado"></xp:selectItem>
</xp:comboBox>

Dojo Filtering Select

If you have the extension library, 8.5.3 with Upgrade Pack 1, or Notes 9, the Dojo Filtering Select control is available to you in the Dojo Form section of the Controls palette. When you drag it onto a page, it looks the same as the standard Combo Box on the Design tab.

FilteringSelect_1

The primary difference between the two is that you can type directly into the filtering select in order to narrow down the choices.

Easily Creating a Dojo Filtering Select

There’s also a major difference in Domino Designer when creating a filtering select — it doesn’t have the ‘Values’ subtab in its properties, so there isn’t a similar method of building the options for the list.

There are two simple ways to convert this to a dojo filtering select.

1) Manually change the xp:comboBox tag to xe:dojoFilteringSelect (along with the corresponding closing tag)
2) Drag a Dojo Filtering Select control onto the page and copy all of the xp:selectItem tags into it (then remove the combo box)

In fact, this is the best way that I know of to create a dojo filtering select easily. They can be enhanced to work with a different data store, but if your list of options is hard-coded or computed with SSJS, this is an easy way to go.

<xe:djFilteringSelect id="djFilteringSelect1">
  <xp:selectItem itemLabel="Acai"></xp:selectItem>
  <xp:selectItem itemLabel="Apple"></xp:selectItem>
  <xp:selectItem itemLabel="Apricot"></xp:selectItem>
  <xp:selectItem itemLabel="Artichoke"></xp:selectItem>
  <xp:selectItem itemLabel="Asparagus"></xp:selectItem>
  <xp:selectItem itemLabel="Avocado"></xp:selectItem>
</xe:djFilteringSelect>

UI Features

Even though it’s a combo box at its core, the filtering select control functions quite differently than the standard combo box.

1) The UI is a little different, as the filtering select picks up dojo styling. This is definitely something to keep in mind; you may need to add CSS to restyle it to keep it consistent with the rest of the page.

FilteringSelect_3

2) As you start typing, it will filter the list to only include the values that start with the characters that have been typed.

FilteringSelect_4

3) If the user types a value that is not valid in the list, an icon displays in the field and an error message pops up next to the field.
FilteringSelect_5

When this happens, no server-side code will execute, because it’s failing client-side validation that’s built into the control.



Dojo Data Grid – Part 28: Overriding Grid CSS

$
0
0

In the last post, I showed how to modify the look and feel of a Dojo Data Grid in XPages by selecting a different dojo theme. In this post, I’ll show how to further customize the UI by adding your own CSS.

Dojo Data Grid Series

Dojo Grids in XPages — All Blog Posts

Default Style

Here’s what the grid looks like by default:

Grid 28 - A

Grid Classes to Target

In order to figure out how to override the styling, we need to understand more about the structure of the grid on the page.

There are a lot of divs and tables that get generated when a grid is rendered. There’s a parent grid div, header and view divs, scrollbox and content divs, and then each grid row is a div and table.

Here’s a list of elements that it uses from the start of the grid down to the first data cell:


div.dojoxGrid > div.dojoxGridMasterView > div.dojoxGridView > div.dojoxGridScrollbox > div.dojoxGridContent > div[role="presentation"] > div.dojoxGridRow > table.dojoxGridRowTable > tbody > tr > td.dojoxGridCell

Every other div.dojoxGridRow also gets the dojoxGridRowOdd class as well.

Font and Row Color

Fortunately, we don’t need to go through all of that to find the classes that we need to customize.

You can change the row color and font by targeting .dojoxGridRow tr

/* Grid Row - Default Styles */
.dojoxGridRow tr {
  background-color: #2F4F4F;
  color: #FFFFFF;
  font-size: 14px;
}

Fixing Header/Cell Alignment with Column Widths

Once you start changing default fonts (or padding), it throws off the alignment of the column headers to the column data.

Grid 28 - B

The simplest way to fix this is to define the width of each grid column explicitly in the grid column properties. This ensures that the header and cell line up.

Grid 28 - C

Alternate Row Color

You can also change the alternate row color and font by targeting .dojoxGridRowOdd tr

/* Grid Row -- Alternate Row Background Color*/
.dojoxGridRowOdd tr {
  background-color: #778899;
}

Row Hover

It’s a little trickier, but you can also override the row hover styles. In this case, I’m changing the background color, text color, and font size.

/* Grid Row - Hover Styles */
.dojoxGridRow tr:hover td {
  background-color: white !important;
  color: #2F4F4F !important;
  font-size: 18px;
}

Grid 28 - D

Header

You can also modify the style of the column header cells. You have to target both the header cell and the div contained within the header cell in order to fully change the background color, which uses an image by default.

/* Grid Header - Styles */
.dojoxGridHeader .dojoxGridCell {
  background-image: none !important;
  background-color: #777777 !important;
}

.dojoxGridHeader .dojoxGridCell div {
  color: white;
  font-size: 12px;
}

Using Dynamic Value Binding to Create a Reusable Field

$
0
0

In XPages, you can compute the value binding of an input control. In this post, I’ll show how to use the powerful capability in order to streamline your design by creating custom control that allows you to reuse a field as needed for consistency, flexibility, and ease of maintenance.

A Simple Use Case

A simple use case for this functionality would be a survey application where forms could consist of many questions with similar sets of options for a response. Rather than creating the same combobox or radio button group over and over, you could have a single field designed as needed and reused for all questions that require the same options.

This would be especially useful if you develop dynamic form-building capability that allows users to specify any number of questions and build the form on the fly.

Advantages of this Design Pattern

There are several advantages to modularizing the design in this way, rather than creating several instances of a field with the same design over and over.

  • Easy to reuse
  • Easy to maintain list values, styling, etc
  • Easy to maintain more complex functionality (event handling code, validation/error handling)
  • Ability to bind the field to a document data source or some other data source

Creating the Reusable Field Custom Control

1) Create a custom control and add custom properties

Create a property for the field name / object property name. This will define where the data will be stored. This property is a string.

Create a property for the data source. This step isn’t required if you just want to assume the data source name (e.g. if all fields will be stored on document1), but it allows you to make the custom control much more flexible.

Set the property type to be a data source. Click the folder icon next to the Type field, expand Data Sources, and select dataInterface.

DynamicValueBinding_2_DataSourceProperty

You can set the Editor to be a Data Source Picker, but it doesn’t quite work the way you’d expect, so it’s not necessary. (Explanation below.)

DynamicValueBinding_1_CustomProperty

2) Add the reusable field and set its value binding. In this case, I have a combobox with 5 standard rating values that will be reused throughout the page.

<xp:comboBox id="comboBox1"	
  value="#{compositeData.dataSource[compositeData.Rating_FieldName]}">
  <xp:selectItem itemLabel="Strongly Agree"></xp:selectItem>
  <xp:selectItem itemLabel="Mildly Agree"></xp:selectItem>
  <xp:selectItem itemLabel="Neutral"></xp:selectItem>
  <xp:selectItem itemLabel="Mildly Disagree"></xp:selectItem>
  <xp:selectItem itemLabel="Strongly Disagree"></xp:selectItem>
</xp:comboBox>

The Power of EL

The computed value binding for the field in line 2 above is what makes this flexible design pattern possible. It combines the flexibility of XPages (allowing dynamic value bindings) with the power of Expression Language in order to define a value binding that is flexible enough to work with multiple types of data sources.

The EL syntax of dataSource[propertyName] will evaluate for an object property as well as a document field name!

compositeData is the object that provides the handle to all property values passed into a custom control, so compositeData.dataSource evaluates to the data source that is passed in. compositeData.Rating_FieldName is the property for the name of the field to which the value will be bound.

Using the Custom Control

Here’s an example of the source of an XPage that will use 3 instances of the field. In each case, it passes in the same data source but a different field name.

Question 1:
<xc:ccDynamicallyBoundField 
  dataSource="#{javascript:return document1;}"
  Rating_FieldName="Field1">
</xc:ccDynamicallyBoundField>
<xp:br></xp:br>

Question 2:
<xc:ccDynamicallyBoundField
  dataSource="#{javascript:return document1;}"
  Rating_FieldName="Field2">
</xc:ccDynamicallyBoundField>
<xp:br></xp:br>

Question 3:
<xc:ccDynamicallyBoundField 
  dataSource="#{javascript:return document1;}"
  Rating_FieldName="Field3">
</xc:ccDynamicallyBoundField>

Passing the Data Source

I mentioned earlier that you can set up the data source custom property of the custom control to be a data source picker (screen shot above shows it), but that it doesn’t work as expected.

It appears to work in that it lets you choose from a list of data sources on the page, but it does not actually work to pass the data source! It essentially passes a string with the name of the data source. The property looks like this:

dataSource="document1"

Instead, you need to compute the property that’s being passed to the custom control to return the data source:

return document1;

This sets the property passed to the custom control as shown in the code snippet that uses the custom control earlier in this post.

I’ve tested this with a document data source as well as a JSON object data source.

Up Next

In the next post, I’ll describe another use case and show how to extend this functionality a bit.


More on Using Dynamic Value Binding to Create a Reusable Field

$
0
0

In my last post, I showed how to use dynamic value binding to create a custom control to reuse a field. In this post, I’ll describe another use case, dig into the functionality a little further, and show one way to enhance it.

Another Use Case

Another use case for this functionality — the case where I first used this functionality — is a scheduling application.

I have a schedule form set up to display the schedule for a single day, but it has to work for any day of the given week.

Rather than create 7 sets of fields to bind them to the appropriate document fields or leave all fields unbound and write code to read values and write them the correct fields, this design pattern allows me to dynamically bind the fields and pass in an index for the specific day being displayed.

In addition, there’s jQuery code activating a type ahead feature, code that runs on a field event, validation, etc. All of this would be more tedious and complicated to manage if repeated over and over again dozens times on the same form.

Using Dynamic Field Binding

As I showed in the previous post, here’s an example of a combobox that’s on a reusable custom control and set up for dynamic value binding:

<xp:comboBox id="comboBox1"	
  value="#{compositeData.dataSource[compositeData.MyFieldName]}">
  <xp:selectItem itemLabel="Strongly Agree"></xp:selectItem>
  <xp:selectItem itemLabel="Mildly Agree"></xp:selectItem>
  <xp:selectItem itemLabel="Neutral"></xp:selectItem>
  <xp:selectItem itemLabel="Mildly Disagree"></xp:selectItem>
  <xp:selectItem itemLabel="Strongly Disagree"></xp:selectItem>
</xp:comboBox>

This code works as is to bind to the data source and field name passed into the custom control when it’s added to the XPage.

It is important to understand, though, that there is a limit to how dynamic the field binding can be; it cannot execute a computation within the value binding.

Let me explain.

In my scheduling application, I have a set of fields that is reused for any day of the week. Say that the field name on the underlying document is Date_1 for Sunday, Date_2 for Monday, and so on, and I’m storing the current day of the week in a sessionScope variable called DayNumber.

It does not work if the field binding is set up like this:

value="#{compositeData.dataSource[compositeData.MyFieldName + sessionScope.DayNumber]}"

However, that doesn’t mean it’s not possible — it can be done with the original version of the value binding shown in the first code snippet. The solution is to execute the computation before passing the field name into the custom control.

<xc:ccDynamicallyBoundField
  dataSource="#{javascript:return document1;}"
  Rating_FieldName="#{javascript:return 'Date_' + sessionScope.scheduleDay;}">
</xc:ccDynamicallyBoundField>

Accessing the Value of a Dynamically-Bound Field from the Data Source

As an enhancement, I wanted to have a computed text control that displays the value in read mode, because comboboxes don’t display nicely in read mode.

While trying to use this with two different data sources (one a document and the other a JSON object), I noticed that it’s a little more complicated to compute within SSJS because it’s not as powerful as EL in its resolution.

I can bind the data source with this format dataSource[FieldName], but, although that syntax works with a JSON object, SSJS cannot use that same syntax to retrieve a field from a document.

In order to work with both types of data sources, I had to enhance the computed text control to check the data source type and treat it differently if it’s a document.

This syntax does the job:

if (typeof compositeData.dataSource == 'NotesXspDocument') {
  // Data Source is Notes Document
  return compositeData.dataSource.getItemValueString(compositeData.MyFieldName);
} else {
  // Data Source is JSON
  return compositeData.dataSource[compositeData.MyFieldName];
}

The difference between two methods of adding client-side JavaScript event-handling code to an XPages input control

$
0
0

There are two ways to add event handling client-side JavaScript code to an XPages input control. This post shows the difference in what is generated and a use case for when the less-obvious method can be useful.

The Scenario

I recently worked on an application designed for a touch screen interface where there are a lot of data entry fields.

In order to make it easier to use, I want it to automatically select the contents of a field when the field receives focus. This way, the user can easily change the value of a field without having to click into a field, select the contents, then delete the existing value and re-enter a new value.

If the contents can be automatically selected when the user sets the focus on a field, then the user can just start typing and it will replace the value.

It may not sound like much, but this is a significant improvement in usability on a touch screen, without using a mouse.

The JavaScript

This is a simple problem to solve on a standard web page. You can put this code on the onfocus event of a field to automatically select the contents:

this.select();

JavaScript Keyword: this

In JavaScript, the keyword this can be used to refer to the object receiving the event that has triggered the handler code.

This is very handy for getting the value of the current field or taking some other action on the field with a very compact syntax. In my case, I was trying to get a handle on the current field in order to select its contents.

Adding JavaScript via the Events View

The standard way to add client-side JavaScript event code is through the Events view. I went there and added the code to the client-side onfocus event.

JSEventCode_1

Here’s what the XPage source looks like:

<xp:inputText id="inputText1" value="#{document1.Field1}" defaultValue="--">
  <xp:eventHandler event="onfocus" submit="false">
    <xp:this.script><![CDATA[this.select();]]></xp:this.script>
  </xp:eventHandler>
</xp:inputText>

This is the HTML and JavaScript generated when the page is rendered:

...
<input type="text" value="--" id="view:_id1:inputText1" name="view:_id1:inputText1" class="xspInputFieldEditBox">
...
<script type="text/javascript">

function view__id1__id4_clientSide_onfocus(thisEvent) {
alert('Value: ' + this.value);
this.select();
}

XSP.addOnLoad(function() {
XSP.attachEvent("view:_id1:_id4", "view:_id1:inputText1", "onfocus", view__id1__id4_clientSide_onfocus, false, 2);
}); 

</script>

This is how client-side JavaScript is added on the XPage. It creates a function and runs code on page load to attach it to the event. In this case ‘this’ is not attached directly to the field.

When I click on the field (thus setting the focus and triggering the onfocus event), I see an error in firebug.

JSEventCode_1b_FirebugError

Workaround

There are ways to work around this limitation. If you add a unique class to the field or compute the client-side ID with pass-through EL syntax (“#{id:inputText1}”), you can get a handle to the field. But this is a little more effort that shouldn’t be necessary.

Adding JavaScript via the Properties View.

I noticed that there’s another place to add onfocus event code. If you click on the field and open the Properties view rather than the events view, you can go to All Properties > events > onfocus. Click on the icon in that field and you can enter JavaScript code.

JSEventCode_2

Code added this way is clearly managed differently than the standard event code. I noticed that code entered via All Properties doesn’t show up in the Events view and vice versa.

There’s a big difference in the output as well. Rather than the JavaScript function and code to attach it to the event handler, code entered this way is passed directly through to the generated input tag.

<input type="text" value="--" id="view:_id1:inputText2" name="view:_id1:inputText2" class="xspInputFieldEditBox" onfocus="this.select();">

This is exactly what I wanted to begin with!

And it works as expected.

Caveat

Since code entered this way is passed directly through, you cannot set it to execute a partial refresh (or trigger any other server side update directly). However, if you need a quick snippet of code that only needs to work with the client-side DOM, this is an option.

You can, however, still include pass-thru EL statements. You just have to realize that, since it’s putting the code directly into the client-side onfocus event, it will already be surrounded by double-quotes by default, so use single quotes instead and account for that.


Dojo Data Grid – Part 29: Displaying the Row Count

$
0
0

In this post, I’ll show how display a row count for the grid and update it after executing a search.

Dojo Data Grid Series

Dojo Grids in XPages — All Blog Posts

All About the Timing

The grid has a rowCount property which will generally return the number of rows in the grid.

It will work fine if you check it by clicking a button after the page loads. However, if you run this immediately as the page loads (even if it’s in the onClientLoad event), it will return 0, because the grid hasn’t been drawn out yet).

At this point, my solution is to introduce a delay to give it time — then it can return the correct amount.

Displaying the Count

One simple way to display the count is to put a simple span tag on the page, add a class to it so it can be programmatically updated.

I added this to my page:

Row Count: <span class="displayRowCount"></span>

Updating the Count

Another consideration is that you don’t just want to check the count when the page loads, you’ll want it to be updated any time the grid is refreshed.

In the post where I showed how to add search to a grid I mentioned that executing a search must refresh the rest service and the grid, so you should put them both in a panel and target that panel with a partial refresh.

To finish this solution, add code that will check the grid’s row count and update the display (span) tag on the onClientLoad event of the panel, so it re-runs every time the panel is refreshed.

var t=setTimeout(function(){
    var myGrid = dijit.byId('#{id:djxDataGrid1}');
    dojo.query('.displayRowCount')[0].innerHTML = myGrid.rowCount;
  }
,500);

This code waits half of a second (500 milliseconds) and then checks the grid’s row count (line 2) and updates the span tag to display it (line 3).

These screen shots show the row count below the grid after initial load and then after a search:
Grid 29 - 1

Grid 29 - 2

REST Service Note

I tested this with a JSON rest service as the source for the grid. Interestingly, the service’s count property is set to 20, so it shouldn’t be retrieving the full set of rows, but it returns the full count of over 1,300 anyway.


XPages Tip: Dynamically Loading a Style Sheet for IE Compatibility Mode

$
0
0

I’ve wrestled with IE’s compatibility mode (which makes higher versions of IE act like IE7) a few times recently. Some workarounds I’ve come across were documented previously. In this post, I’ll show how to conditionally load a style sheet that has styles specifically for compatibility mode.

Themes to the Rescue

Fortunately, you can determine the browser and version in XPages and you can use a theme to conditionally load a style sheet based on the browser and version.

The snippet below will load one style sheet if the browser is IE7 (or below) and load another style sheet if the browser is anything else.

The .isIE(0,7) method returns true or false based on whether the current browser is any version of IE from 0 to 7.

<resource rendered="#{javascript:context.getUserAgent().isIE(0,7)}">
  <content-type>text/css</content-type>
  <href>IE7StyleSheet.css</href>
</resource>
	
<resource rendered="#{javascript:!context.getUserAgent().isIE(0,7)}">
  <content-type>text/css</content-type>
  <href>NonIE7StyleSheet.css</href>
</resource>

It’s a massive headache to deal with compatibility mode, but this flexibility makes it a little easier to adjust styles as needed.


CSS Tip: Dealing with Table Spacing in IE Compatibility Mode

$
0
0

One of the most frustrating things about dealing with IE compatibility mode is how it handles table layouts. In my recent experience, it seems to automatically stretch them out to take up more room than you’d expect. It seems to generally disregard padding, spacing, and column width CSS settings (unless the column width is greater than the amount of space the browser wants to provide for a column).

Even worse, it doesn’t always space the columns evenly. In a recent application, cells with consistent content were sized differently in compatibility mode, so the content of some cells would wrap and others wouldn’t — even though there would have been plenty of space for all of the data if the columns had been spaced evenly.

Since the pattern seems to be that it will stretch tables out to the full available width, the quick workaround to improve table layouts in compatibility mode is to put a container around the table (such as a div) and use CSS to fix the width of that container. This doesn’t cause it to space the columns perfectly, but it can be very handy in limiting the size of the table as needed.



Dojo Data Grid – Part 30: The onStyleRow Event

$
0
0

In this post, I’ll dig into the onStyleRow event of the XPages Dojo Data Grid in order to show how it can be used to modify the display of grid rows. This will lay the foundation for the next few posts.

Dojo Data Grid Series

Dojo Grids in XPages — All Blog Posts

The onStyleRow Event

The onStyleRow event runs on each row in the grid as it is rendered. The event handler automatically receives one argument that has a number of properties that can be used to conditionally customize the display of the row.

By default, the grid will provide alternate row coloring and different styles when you select or hover over a row. This image shows all three of those styles in effect.
Grid 30A

However, as soon as you add any code into the event handler, you lose the hover, selected, and alternate row styles. (In this case, I added a single comment to the event handler and it was still enough to cause other styles to be dropped.)
Grid 30B

onStyleRow Argument Properties

The onStyleRow event automatically receives an object with information about the current row. It’s not a named argument, so we refer to it by arguments[0]. The properties of the row object are as follows:

  • index (row index)
  • node (DOM node)
  • odd (boolean — whether the row is an odd-numbered row)
  • selected (boolean — whether the row is currently selected)
  • over (boolean — whether the row is currently hovered over)
  • customStyles (used to add style information to the row)
  • customClasses (used to add classes to the row)

The index property is the 0-based number of the row in the grid.

odd, selected, and over are boolean properties that are handy for changing styles for alternate rows and whether the row is selected or hovered over.

The customStyles property is used to add custom style information to the row. The customClasses property is used to add additional classes to the grid row.

The node provides a handle to the DOM node of the grid row.

Here’s an example of what it returns:

</pre>
<table class="dojoxGridRowTable" role="presentation" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td class="dojoxGridCell " style="width: 6em;" tabindex="-1" role="gridcell" colspan="1">...</td>
<td class="dojoxGridCell " style="width: 6em;" tabindex="-1" role="gridcell" colspan="1">...</td>
<td class="dojoxGridCell " style="width: 6em;" tabindex="-1" role="gridcell" colspan="1">...</td>
<td class="dojoxGridCell " style="width: 6em;" tabindex="-1" role="gridcell" colspan="1">...</td>
<td class="dojoxGridCell " style="width: 6em;" tabindex="-1" role="gridcell" colspan="1">...</td>
<td class="dojoxGridCell " style="width: 6em;" tabindex="-1" role="gridcell" colspan="1">...</td>
</tr>
</tbody>
</table>
<pre>

There are two interesting things to note. First of all, each row in the grid is actually its own table. Secondly, each cell in the row is initially populated with ‘…’. It then fills in the data from the REST service on a second pass.

This is important to understand — the onStyleRow event will generally run more than once on each row. Essentially, whatever settings are applied in the last pass are the ones that stick.

In my testing, it appears to run between 1 and 3 times on each row. It seems to most commonly run twice — once when it sets up the row and pre-populates every cell with ‘…’ and then a second time when it actually fills in the data. (I tested this by reviewing the full contents of the row node.)

It seems to run 3 times on the first chunk of rows displayed when the grid is first loaded. It only runs once one when the data has already been loaded or if the row is read-protected and thus cannot display any data.

Adding event code

I enter code for the onStyleRow event via the All Properties tab and not directly in the event view. Take a look at this post to see why this works differently.

Grid 30 - A

Up Next

Now that we’ve explored what happens in the event and what’s available to it, the next couple of posts will show how to use the event to provide additional row styling.


Getting Awesome Category Icons in Data Views – A jQuery Variation

$
0
0

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


Dojo Data Grid – Part 31: Setting Row Styles on Hover, Selection, and Alternate Rows

$
0
0

In this post, I’ll show how change the row color in an XPages Dojo Data Grid based on whether the row is hovered over, selected, or is an odd-numbered row.

Dojo Data Grid Series

Dojo Grids in XPages — All Blog Posts

Row Styling

By default, the grid will provide alternate row coloring and different styles when you select or hover over a row. This image shows all three of those styles in effect.
Grid 30A

However, as soon as you add any code into the event handler, you lose the hover, selected, and alternate row styles.

The client-side onStyleRow allows us to re-apply this styling or change it. The event executes as each row is drawn in the grid, which is the timing we need in order to dynamically set the row styling. See my previous post on the onStyleRow event for more information.

Restoring the Default Styles

To restore the default styles, you can use the same class names that the grid uses to apply the styles.

  • Alternate Rows – dojoxGridRowOdd
  • Row Hover – dojoxGridRowOver
  • Row Selection – dojoxGridRowSelected
// This event automatically receives an object with information about the current row. It's not named, so we refer to it by arguments[0]
var row = arguments[0];

if (row.over) {
  row.customClasses = 'dojoxGridRowOver';
} else if (row.selected) {
  row.customClasses = 'dojoxGridRowSelected';
} else if (row.odd) {
  row.customClasses = 'dojoxGridRowOdd';
}

Customizing the Styles

Now that you understand how this works, it is just as easy to change the styles completely in these three cases.

Just define classes in CSS and include the style sheet on the page and you use the same technique to append classes to rows as needed.

In this hideous example, alternate rows are set to blue, selected rows are set to yellow, and the hovered row is set to red.

Grid 31

var row = arguments[0];

if (row.over) {
  row.customClasses = 'redRow';
} else if (row.selected) {
  row.customClasses = 'yellowRow';
} else if (row.odd) {
  row.customClasses = 'blueRow';
}

Dojo Data Grid – Part 32: Setting Row Color Based on Row Data

$
0
0

In this post, I’ll show how dynamically change the row color in an XPages Dojo Data Grid based on data in the row.

Dojo Data Grid Series

Dojo Grids in XPages — All Blog Posts

Conditional Row Styling

A common use case for setting the row color is to draw attention to certain rows based on the status. If an item is being shipped and it’s late, you might want to highlight it in red to make sure that it’s obvious that there’s a problem.

The client-side onStyleRow event of the grid is the key to this technique. It executes as each row is drawn in the grid, which is the timing we need in order to dynamically set the row styling. See my previous post on the onStyleRow event for more information.

CSS

In the method I’ll demonstrate, it appends a class to each row that is styled. For this to work, the class names have to correspond to CSS classes that define the row style.

Here is the CSS used in my sample:

.redRow {
  background-color:red !important;
}
.yellowRow {
  background-color:yellow !important;
}
.greenRow {
  background-color:green !important;
}
.blueRow {
  background-color:blue !important;
}
.grayRow {
  background-color:gray !important;
}

The Code

Below is the source code for a working example. Since the data I have in my test database is about people, I set it up to display different row colors if the first name starts with A, B, C, or D.

var columnNumber = 0;  // 0-based column number of the grid data to read

// This event automatically receives an object with information about the current row. It's not named, so we refer to it by arguments[0]
var row = arguments[0];

// Read the data from the specified cell in the row
var firstName = dojo.query('TD', row.node)[columnNumber].innerHTML;
if (firstName == '...') {
  return;
}

// Assign the style class based on the first letter of the first name
var rowClass = 'grayRow';
switch(firstName.substr(0,1).toUpperCase()) {
  case 'A':
    rowClass = 'redRow';
    break;
  case 'B':
    rowClass = 'yellowRow';
    break;
  case 'C':
    rowClass = 'greenRow';
    break;
  case 'D':
    rowClass = 'blueRow';
    break;
}

row.customClasses += ' ' + rowClass;

Understanding the Code

The main thing that the code needs to do is to read the row data and set the class accordingly. Now that we understand what information is available in the onStyleRow even, the code is pretty simple.

Line 1 defines the column number from which data should be read. It’s 0-based (because I’ll end up getting an array of table cells to read from and that will be 0-based). In this example, I want to read the firstname column, so it’s set to column #0.

Line 4 gets a handle to the object that is automatically passed into the onStyleRow event. It’s not named, so we refer to it by arguments[0].

Line 7 does the heavy lifting. It uses dojo.query to get all of the cells (TD) in the table for the current row. It gets the nth cell based on the column number defined in line 1; this determines which column it’s reading. It then returns the innerHTML of that cell in order to return the cell content.

Lines 8-10 account for the grid behavior that defaults each cell to ‘…’. The onStyleRow event will run twice as rows are loaded. The first time, the data will be ‘…’ and thereafter, it will be populated with the real data. So, if this is the initial pass, just exit the function for the time being and it will re-execute as the data is filled in.

The rest of the code reads the first character of the first name and determines which style class to add to the row.

The last line uses the customClasses property of the row in order to add the style class that sets the row color.

The Result

That’s it — we now have code that dynamically styles the row colors based on the data within the row!

Grid 30 - B


XPages Tip: Setting a Date Field with @Now

$
0
0

I came across an interesting quirk recently when using @Now() to set date fields in an XPages workflow application — it works on the front-end (XSP) document but not on a back-end NotesDocument.

Assuming I have a document data source named document1, this code will work in SSJS to set a date field to @Now() via the XSP document:

document1.replaceItemValue('DateField', @Now());

However, if I get the back end document and try to do the same thing, it throws an error. (The same is true if working with the back-end document even without getting a handle to it through the front-end document.)

document1.getDocument().replaceItemValue('DateField', @Now());

AtNowError

Looking through the stack trace, this is the real error: “NotesException: Unknown or unsupported object type in Vector”

So it appears that the NotesDocument version of replaceItemValue() doesn’t handle that type of date.

Fortunately, there’s an easy workaround. You can use session.createDateTime() to get a date in a format that replaceItemValue can handle.

This line of code works:

document1.getDocument().replaceItemValue('DateField', session.createDateTime(@Now()));

Set the Bootstrap class for all text fields in XPages with a Theme

$
0
0

In order to enable your fields to pick up Bootstrap styling, you must specify the class name form-control on each of them. In this post, I’ll show how to make this much easier by setting it for all text fields at one time via the theme.

In an application that is set up to work with Bootstrap 3, I created a div with the class well and added three labels and fields. This is what it looks like by default:

BootstrapFields_Before

My application is already using a custom theme in order to make Bootstrap and other resources available globally, so I added this code to automatically add the form-control class to all input text controls.

<!-- Set all inputs to bootstrap styling -->
<control>
  <name>InputField.EditBox</name>
  <property mode="concat">
    <name>styleClass</name>
    <value>form-control</value>
  </property>
</control>

This instructs the server to look for all text fields and add form-control to the styleClass attribute. The property mode is set to concat so it will add the class to any other classes you define on the control rather than overwrite it. Now, all text fields will automatically pick up the correct styling!

BootstrapFields_After

That’s more like it.

Note

This assumes you already have bootstrap enabled in your application. The specific layout in these screen shots was created with a well and other divs with standard Bootstrap scaffolding classes.


Set the Bootstrap class for all multiline text fields and labels in XPages with a Theme

$
0
0

In my last post, I showed how to automatically set the class name to pick up Bootstrap styling on all text fields in an XPages application via the theme. In this post, I’ll show how to set the class name on multiline fields and field labels as well.

If I update the form from the last post to include a multiline edit box, it will look this (if I already have the code from the last post included in the theme):

BootstrapTextArea_Labels_Before

The multiline edit box doesn’t match and the field labels also aren’t picking up Bootstrap styling.

This code in the theme will update the multiline edit box controls to pick up the required form-control class:

<!-- Set all multiline inputs to Bootstrap styling -->
<control>
  <name>InputField.TextArea</name>
  <property mode="concat">
    <name>styleClass</name>
    <value>form-control</value>
  </property>
</control>

This code will automatically provide the class to all field labels via the theme:

<!-- Set all labels to Bootstrap styling -->
<control>
  <name>Text.Label</name>
  <property mode="concat">
    <name>styleClass</name>
    <value>control-label</value>
  </property>
</control>

Now, everything on the form is picking up the correct styling:

BootstrapTextArea_Labels_After



Adding Bootstrap Error Styling to a Select2

$
0
0

In this excellent post, Mark Leusink shows how to create a reusable control that makes it easier to create Bootstrap-styled fields with labels and field validation. While using a similar concept — computing the class of the div around a field based on whether there’s an error in the field — I found that Select2 fields need a little bit of extra work to display with the error styling.

Boostrap looks for an error class in the div surrounding a field and label and it styles them accordingly. See Mark’s post for a more detailed explanation.

This works well in general, but Select2 fields are different, because they are generated on the front end as the page is loaded. The same thing happens with dojo-enabled fields (date and time pickers, etc) — the lose some styling that you try to define when they’re rendered on the page.

Adding the error class to the div around a Select2 will not cause it to pick up error styling. However, with a little CSS, you can easily update a Select2 field to be highlighted in red if the surrounding div contains the error class.

/* Styling for Select2 with error */
div.has-error ul.select2-choices {
  border-color: rgb(185, 74, 72) !important;
}

This looks for a div with the error class (which is has-error in Bootstrap 3) and then locates a ul with the class select2-choices within that div. I just found this by looking at the generated html and trying classes on different elements until it worked.

The CSS sets the border color to the same color that it sets other error fields. I found that by flagging a field as an error and checking the styles that it picked up.

The good news is that these 3 lines of CSS will do the trick for any instance of a Select2 that’s contained in a div that’s flagged as an error.

Note

I’m using Bootstrap 3, so the class names are a little different than on Mark’s page, but the concept remains the same.


Triggering a Partial Refresh when a Select2 Value is Changed in XPages

$
0
0

In my last post, I showed how to add Bootstrap error styling to a Select2. As I’ve continued to work with Select2, I came across another challenge — triggering a partial refresh when a Select2 value is changed — because standard component events aren’t executed on a Select2.

This issue is because a component is converted to a Select2 after the page loads. It takes your combobox or listbox and converts it to a div surrounding the entire field, a div for the search, and an unordered list of elements to display in the drop-down. Any event handler code that you entered on the combobox or listbox will no longer be associated with the field because it’s been replaced with the Select2 UI elements.

It is common to update a part of a page based on a value selected in any variation of a list field, so, fortunately, this issues can be worked around by using event delegation to trigger a client-side JavaScript function when the Select2 is changed.

Take this example of converting a field (with a class name of “mySelect2″) to a Select2:

$(".mySelect2").select2({
  placeholder: "Select the value..."
});

To trigger a partial refresh when the value is changed, add this code on the client load or document ready event after the previous code to enable the Select2:

	
$(".mySelect2").on("change", function(e) { 
  XSP.partialRefreshPost("#{id:myPanel}");
})

The code will watch for any change on the Select2 and it will trigger a partial refresh via client-side JavaScript. (See Mark Roden’s post on jQuery event delegation for a good explanation of jQuery event delegation.)

It must trigger a partialRefreshPost (rather than a partialRefreshGet) because it has to send the new value to the server. For the same reason, the field must be within the partial refresh target area.

You can only write client-side JavaScript code here, but if you have a larger piece of server-side JavaScript or Java code that needs to be executed, you could put that code on a hidden button (it must be rendered, but could be hidden from the screen with CSS) and trigger a click() event on that button with client-side JavaScript.

_


Published in The VIEW: Article on Reusable Message Display Control for XPages

$
0
0

The first article of a two-part series by myself and the estimable Troy Reimer has been published in The VIEW. (Subscription Required)

Reusable and Consistent Message Displays in XPages Part 1: Inline Messages

The goal is to provide an easy way to display consistent messages to the user with a single line of code, but to style them and fit inline them within the application in a way that looks much better than, say, a client-side JavaScript popup. This article shows how you can easily display all 4 types of OneUI-styled messages (Confirmation, Error, Warning, Info) at any time and from any page in the application.

Part 2 will demonstrate reusable message controls that function as dialogs — one with OneUI styling and one with a Twitter Bootstrap modal.

Abstract

The separation of client-side and server-side logic in XPages (read: web) applications increases the complexity of communicating consistently with users. In this series, we demonstrate several implementations of reusable message controls that save development time by being easy to implement and help improve the user experience by providing consistently formatted inline messages. Options include OneUI and Twitter Bootstrap styling as well as client-side and/or server-side triggers, so you can choose what works best for your application.


Conditionally Render an Event Handler

$
0
0

One of the great features of XPages is that so many properties can be computed dynamically. One such property that you may not have realized is that you can even dynamically determine whether to render event handlers. In this post, I’ll show you how.

I first used this feature when using a repeat control to generate a link for every letter in the alphabet in order to jump to a specific place in a view. However, I wanted to disable the letters where there was no matching data, so I didn’t want the click to send a server request in that case.

To show it more clearly here, I’ll demonstrate a simpler example.

This code shows a repeat control that will display a link for the letters A through J. A click on any link will run client-side JavaScript to pop up an alert to display that letter.

<xp:repeat id="repeat1" rows="30" indexVar="rptIndex" var="rptVar">
  <xp:this.value>
    <![CDATA[#{javascript:return ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];}]]>
  </xp:this.value>
  <xp:link escape="true" text="#{javascript:return rptVar;}" id="link1">
    <xp:eventHandler event="onclick" submit="false">
      <xp:this.script>
        <![CDATA[alert('#{javascript:rptVar}');]]>
      </xp:this.script>
    </xp:eventHandler>
  </xp:link>
     
</xp:repeat>

If you switch to the page source and click on the tag, you’ll find a rendered property. Just add logic there to conditionally determine whether to render the event handler.

In this example, I’m checking the repeat index and only rendering event handlers for even-numbered entries in the repeat control (0, 2, 4, 6, 8, etc.)

Blog_ConditionalEventHandler

This code shows the repeat control with the added rendered condition on the event handler.

<xp:repeat id="repeat1" rows="30" indexVar="rptIndex" var="rptVar">
  <xp:this.value>
    <![CDATA[#{javascript:return ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];}]]>
  </xp:this.value>
  <xp:link escape="true" text="#{javascript:return rptVar;}" id="link1">
    <xp:eventHandler event="onclick" submit="false">
      <xp:this.script>
        <![CDATA[alert('#{javascript:rptVar}');]]>
      </xp:this.script>
      <xp:this.rendered>
        <![CDATA[#{javascript:return rptIndex % 2 == 0;}]]>
      </xp:this.rendered>
    </xp:eventHandler>
  </xp:link>
     
</xp:repeat>

Now, the odd-numbered links will not do anything when clicked, because their event handler is not rendered.


Resizing a Bootstrap 3 Modal

$
0
0

The Twitter Bootstrap modal is a very slick dialog that you can easily use within XPages. In this post, I’ll show how to increase the size of modals globally. I’ll also show how to re-center the modal if you resize the body with CSS.

Resizing a Modal

The bootstrap modal uses the class modal-dialog. By default, it is 600px wide.

To widen it, use CSS to increase the width.

.modal-dialog {
  width: 900px;
}

This updates the modal size and allows it to be centered properly when launched.

However, if you increase the size on the modal content area or body, it will not re-center properly. (This may happen if you don’t want to globally resize all modals or if you have a reusable control for modals, but your content changes and you want to re-size some instances.)

.modal-content {
  width: 900px;
}

If you display this modal, you’ll notice that it’s no longer centered.

Modal_NotCentered

Centering a Modal with the Body Resized

In order to center it, you can add another line of CSS to set a negative value for the left margin.

Since the modal is automatically centered based on a width of 600px, you want to move it back to the left by half of the additional width.

Left Margin = (600px – [New Width]) / 2

In this example, the new width is 900px, so the left margin is (600-900)/2 = -150px

Here’s the updated CSS:

.modal-content {
  width: 900px;
  margin-left: -150px;
}

Now the modal will be centered.

Modal_Centered


Viewing all 216 articles
Browse latest View live