Archive | Theme RSS for this section

XPages Tip: Adding a Bootstrap Class to All Labels via the Theme (Redux)

In this post, I showed how to set all labels in an application to include a class to pick up Bootstrap styling so it could be applied application-wide and not require touching every label control individually. However, there’s a quirk with label rendering that may make it not apply universally as desired. In this post, I’ll explain the issue and how to use another theme setting to easily fix it.

Updating all Labels via the Theme

This code in the theme will apply the control-label class to all label components in the application. This allows me to apply a Bootstrap style class to all labels without having to update each label control individually on every XPage and custom control.

<control>
  <name>Text.Label</name>
  <property mode="concat">
    <name>styleClass</name>
    <value>control-label</value>
  </property>
</control>

The Bootstrap styling will be applied to label tags that have the control-label class.

The Problem with Label Rendering

However, there’s an issue with the way labels are rendered that may not make it work consistently.

Any labels that do not have a for attribute specified will actually be rendered as span tags and the styling will not be picked up. (Labels that have the for attribute specified will be rendered as label tags and work as expected.)

In another post, I described the effects of different modes on theme property application. In this post, we’ll see a practical example of the effects.

Theme to the Rescue (Again)

Fortunately, there’s an easy way to add another property setting to the theme to handle this.

There isn’t a tagName attribute on a label, so you can’t directly modify the output tag this way, but for is a property of the control, so you can use the theme to add it to all labels.

1. Overriding the Property

One option is to override the for property on all labels. Lines 7-10 were added below to do so.

<control>
  <name>Text.Label</name>
  <property mode="concat">
    <name>styleClass</name>
    <value>control-label</value>
  </property>
  <property mode="override">
    <name>for</name>
    <value>dummyTarget</value>
  </property>	
</control>

This puts the for property in place and assumes it needs to figure out the client-side ID of the element that’s been specified. This is a dummy value, but it does the job.

<label id="view:_id1:_id3:_id51:office_Label1" class="control-label" for="view:_id1:_id3:_id51:dummyTarget">Office</label>

However, it does the job too ambitiously. It removes any existing for property, so it would break the association with the specified input control, which may cause other issues with the application and most certainly would cause issues with a screen reader.

2. Concatenating the Value

Changing line 7 to concatenate instead of overriding the value gives us a bit better behavior.

<property mode="concat">

This mode will append the property value to any value that currently exists for the attribute. It will also add the attribute and value if it doesn’t exist.

<label id="view:_id1:_id3:_id51:label17" class="control-label" for="view:_id1:_id3:_id51:dummyTarget">Payment #</label>

This also does the job, but it causes two values to be in the for property if one already existed (although it doesn’t try to generate a client-side ID when it’s appended).

<label id="view:_id1:_id3:_id51:office_Label1" class="control-label" for="view:_id1:_id3:_id51:office1 dummyTarget">Office</label>

This is better than the override method, but still may cause problems.

3. Mode not specified

You can also just not specify the mode.

<property>

In this case it only adds the property and value if the property doesn’t already exist, so it’s the cleanest solution.

This is an example of a label that already had the for attribute specified. It does not get the new dummy value.

<label id="view:_id1:_id3:_id51:office_Label1" class="control-label" for="view:_id1:_id3:_id51:office1">Office</label>

This is an example of a label that did not have the for attribute specified:

<label id="view:_id1:_id3:_id51:requisitionLabel" class="control-label" for="view:_id1:_id3:_id51:dummyTarget">Requisition #</label>
Advertisements

XPages Tip: Overriding, Concatenating, and Conditionally Adding Control Properties via a Theme

Themes are commonly-used in XPages applications to include resources needed throughout an application, but they can also be a powerful tool for consistently applying application-wide control settings. In this post, I’ll explain the difference between different modes for applying a control property and link to a corresponding post with a practical example.

1. Overriding a Control Property

In this post, I showed how to use the theme to add a class to all labels in an application to pick up Bootstrap styling without having to set the class on each individual label.

Here’s an example of the theme code to make it happen:

<control>
  <name>Text.Label</name>
  <property mode="override">
    <name>styleClass</name>
    <value>control-label</value>
  </property>
</control>

When a page loads, the theme is applied to the page and all label controls are updated as the page is rendered.

  • Line 2 is directing it to apply to all Label controls throughout the application. (This is using a theme ID that’s predefined for the label control, as noted in the Mastering XPages book.)
  • Line 4 specifies that it’s updating the styleClass property of the controls.
  • Line 5 specifies the control-label class to add to each label. (This is the class name that Bootstrap styling looks for.)
  • Line 3 is the variable for this post. This example starts with the setting the override mode. This mode will set the class to the specified value on all label controls, overriding anything that may have been set when the control was added on an XPage or custom control.

2. Concatenating a Control Property

Instead of overriding the property on all controls, another option is to concatenate the specified value to any existing value specified on each instance of the control.

<property mode="concat">

In this case, if there was a label control that already had a class specified, the control-label class would be added to it (after a space) so the label would have both classes.

If the label control did not have a class, then the specified class would be added.

3. Conditionally Applying a Control Property

The last option is not to specify a mode at all.

<property>

In this case, it will not do anything to a label control that already has a class specified, but it will add the specified class to any label control that does not have one.

Take a look at for a practical example of each of these methods and how the distinction between these methods matters.

XPages Tip: Displaying Bootstrap Applications Properly on Mobile Devices

Do you have a Bootstrap navigation menu in XPages that collapses properly in a full browser but not on a mobile device? You may need to set a meta tag to force it to display properly. In this post, I’ll show the effects with and without the tag on a mobile device.

Navbar Collapsing

One of the great features of Bootstrap is its built-in responsive UI that scales well to work on screens of varying sizes. The navigation bars collapse nicely into the hamburger menus to make better use of available space on smaller devices.

However, in recent testing of an XPages application with a Bootstrap UI, it was noted that the navigation menus did not collapse properly when viewed on a phone. It was a little confusing because it collapsed as expected on a full browser.

When the navigation bar is set up properly, it should collapse properly on its own when the screen width is below 768 px. I verified that there are no CSS rules overruling that cutoff and that 768px is, in fact, the point where the menu collapses on the full browser.

Bootstrap Navbar Test Page

To test it out, I tried out the Bootstrap Navbar test page

On my laptop with a full browser, it showed the navigation normally.

Nav Collapse 1A - Full Browser Over 768px

When I shrunk the screen to be less than 768px wide, it collapsed the navigation properly.

Nav Collapse 1B - Full Browser Under 768px

When I checked it on my phone, it was collapsed properly.

Nav Collapse 1C - Phone - Collapsed

Test Page in XPages

I copied the div that contains the navigation from that test page and pasted into an XPage in my application to see how it worked.

It collapsed as expected in a full browser but not on the phone.

Nav Collapse 2A - Phone - NOT Collapsed in XPages

It is also apparent that the page has been scaled so that it fits fully on the phone.

To verify this, I put a button on the page to tell me the screen width. As expected, it showed a width > 768 px, which is why it did not collapse the menus. It scaled the entire page to fit on the screen, so it did not fall below the threshold of the responsive design media queries.

Nav Collapse 2B - Phone - Screen Width

(This is on a Samsung Galaxy. An iPhone showed it to be roughly 900px.)

And this is just a simple page. Imagine how that looks with a much bigger XPage!

The Difference

Ultimately, the main difference is that the Bootstrap test page contains a meta tag to make sure the device doesn’t shrink to fit the entire page on the screen.

<meta name="viewport" content="width=device-width, initial-scale=1">

In XPages, this can be set at the page level, but it’s easiest to do it application-wide via a theme, within a resources tag.

<resources>
  <metaData>
    <name>viewport</name>
    <content>width=device-width, initial-scale=1.0</content>
  </metaData> 
</resources>

Now, when I reload the Bootstrap test page, it is displayed properly on the phone.

Nav Collapse 3 - Phone - Collapsed in XPages

The button showing the screen with on the phone now shows it to be 360px wide.

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.

The Differences Between The resource and resources Tags in an XPages Theme

If you’ve modified a theme, you’ve almost certainly used resource tags to include stylesheets and client-side JavaScript libraries. Each resource tag specifies a single resource to include. However, you can also use the resources tag to include multiple resources. In this post, I’ll explain the differences and a caveat.

resource Tags

Most themes only make use of resource tags because they’re what’s demonstrated by the examples. When you create a new theme, these comments are included by default:

<theme extends="webstandard" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="platform:/plugin/com.ibm.designer.domino.stylekits/schema/stylekit.xsd" >
  <!-- 
    Use this pattern to include resources (such as style sheets
    and JavaScript files that are used by this theme.
  -->
  <!--
    <resource>
      <content-type>text/css</content-type>
      <href>mystylesheet.css</href>
    </resource>
  -->

If I have a stylesheet named myStylesheet.css, it’s very easy to tweak that example to include it in my theme.

<resource>
  <content-type>text/css</content-type>
  <href>myStylesheet.css</href>
</resource>

I can also include a client-side JavaScript library like this:

<resource>
  <content-type>application/x-javascript</content-type>
  <href>csjsMyClientJSLibrary.js</href>
</resource>

Each resource tag defines a single resource. It has child tags to define the content type and the path to the resource.

The resources Tag

The resource tags work well, but you can also use a single resources tag instead and there are a few reasons that this is advantageous.

If you’ve looked in the source of an XPage or custom control where you included one or more resources, you’ve noticed that there’s a this.resources tag that includes a line for each individual resource.

For example, in a test database, I created an SSJS script library and a stylesheet and included them as resources on an XPage. This is how they are included in the page source within an xp:this.resources tag:

<xp:this.resources>
  <xp:script src="/ssjsMyLibrary.jss" clientSide="false"></xp:script>
  <xp:styleSheet href="/myStylesheet.css"></xp:styleSheet>
</xp:this.resources>

With the resources tag, you can use a similar syntax in the theme.

There are a few benefits:

1) Succinct syntax

Especially as the theme grows to include a lot of resources, it is nicer to have a less verbose syntax so you can review the contents of the theme more easily.

All of the same properties are available as with the resource tag, they’re just specified as attributes rather than as child tags.

2) Ability to include server-side resources

The resource tag can only include client-side resource (client-side JavaScript libraries, stylesheets, dojo modules,etc), but the resources tag can include SSJS libraries and resource bundles as well.

This makes it much easier to deploy those resources application-wide.

Tip for Specifying Resources in the Theme

There isn’t a simple way to add resources to the theme, but here’s how to streamline the process:

  1. Add the resources to an XPage or custom control
  2. View the source of the page and copy the block to the theme (and remove it from the page)
  3. Remove xp: and xp:this. from the tags
<resources>
  <script src="/ssjsMyLibrary.jss" clientSide="false"></script>
  <styleSheet href="/myStylesheet.css"></styleSheet>
</resources>

This works, but it’s a good idea to specify the type attribute (for example type=”text/javascript”) as well.

Caveat – Avialability of SSJS Library on Page Events

There is an important caveat to loading SSJS libraries this way. The theme is loaded during the Render Response phase of the JSF lifecycle, so SSJS libraries loaded by the theme will not be available to page events that happen before that phase. Therefore, if you need to execute library functions on page-level events that happen earlier (beforePageLoad and afterPageLoad), you will need to include them on the page and not in the theme. SSJS libraries loaded by the theme are available to the beforeRenderResponse and afterRenderResponse events.

XPages Tip – Disable Autocomplete on All Fields via the Theme

In my last post, I showed how to disable autocomplete on a single field. In this post, I’ll show how to disable it application-wide via the theme.

If you want to prevent autocomplete on all fields in your application, you can do it in one fell swoop by adding this code to your theme:

<!-- Set all edit box controls to disable autocomplete -->
<control>
  <name>InputField.EditBox</name>
  <property mode="override">
    <name>autocomplete</name>
    <value>off</value>
  </property>
</control>

This will automatically insert the autocomplete="off" attribute into all Edit Box controls in the application.

Here’s an example of what a field now looks like in the browser page source:

<input type="text" id="view:_id1:AutoComplete" name="view:_id1:AutoComplete" autocomplete="off" class="xspInputFieldEditBox"><br>

XSPUserAgent Methods Do Not Detect IE 11

I’ve found that the methods of the XSPUserAgent class, which are commonly used for browser detection in XPages applications, do not properly detect IE 11. In this post, I’ll show what happens and how to work around it.

Browser Detection Methods

It is standard practice to use methods like these on rendered formulas for resources (in a theme or directly on a page) that you want to conditionally load, based on the browser type:

<xp:styleSheet rendered="#{javascript:context.getUserAgent().isIE()}" href="/ie_stylesheet.css"/>
<xp:styleSheet rendered="#{javascript:context.getUserAgent().isFirefox()}" href="/firefox_stylesheet.css"/>
<xp:styleSheet rendered="#{javascript:context.getUserAgent().isChrome()}" href="/chrome_stylesheet.css"/>

Unfortunately, the isIE() check fails for IE 11.

XSPUserAgent Method Values for IE 11

Method Value Returned
context.getUserAgent().isIE() false
context.getUserAgent().getBrowser() ‘Unknown’
context.getUserAgent().getBrowserVersion() (blank)
context.getUserAgent().getBrowserVersionNumber() 0

I tested and found these return values to be the case on both 8.5.2 (which doesn’t surprise me) and Notes 9 (which does).

Checking the Full Browser String

Fortunately, this method will return the full browser string:

context.getUserAgent().getUserAgent()

In IE 11, the string is as follows:

Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko

Apparently, Microsoft made some changes to their standard pattern for the browser string (including removing the easily-detectable ‘MSIE’).

Fortunately, two things stand out about this string:

  1. Trident/7.0 >> IE10 included Trident/6.0 and IE9 included Trident/5.0
  2. rv:11.0 >> Indicates version 11 of Internet Explorer

I don’t feel like it’s a future-proof solution, but for now, I’m checking the browser string for those values. Just in case point release numbers change, I’m checking for ‘Trident/7’ and ‘rv:11’.

Here’s a simple function that you can use to check for IE 11:

function isIE11() {
  var isBrowserIE11 = false;
	
  if (context.getUserAgent().getBrowser() == 'Unknown') {
    var userAgent = context.getUserAgent().getUserAgent();
    if (userAgent.indexOf('Trident/7') > -1 && userAgent.indexOf('rv:11') > -1) {
      isBrowserIE11 = true;
    }
  }	
 
  return isBrowserIE11;
}