Questions and Answers from deploying a Teamroom application on Bluemix

Introduction

write once, run everywhere

Thise WORE slogan is used often in cross-platform solutions, also in IBM Domino. But what if you move your XPages application from an on premise Domino installation to the cloud, more specific IBM Bluemix?

Teamroom

To gain some experience in this area I decided to take a well known application, IBM’s Teamroom and deploy it on Bluemix. I will spare you the details of creating a Bluemix account, setting up Domino Designer, deploying the Design application and binding it to a XPages NoSQL Database service. I will focus on adapting/preparing the Design of the XPages application and some areas I have not found an answer for (yet).

First consideration – Choose your data-binding

bluemixContext

Bluemix requires the separation of design and data so if you haven’t use this principle your applications yet, you must apply it now. Second, Bluemix will allow you also to run your XPages application in a mixed environment (part cloud / part on-premise) so if your application will do so the bluemixContext object will let you identify where your application is running and point to the corresponding data NSF.

DAO Bean

In my case the Teamroom application would only run in Bluemix so instead of applying bluemixContext.getDataService().findDatabaseName() I prefer to use Oliver Busse’s daobean which I have been using in other applications as well. This allows me to use the shorter dao.getDbpath() for xp:dominoDocument and xp:dominoView data-binding.

Remember: in the Teamroom design you need to set this property for every Document and View binding!

<xp:this.databaseName><![CDATA[#{javascript://bluemixContext.getDataService().findDatabaseName()
dao.getDbpath()}]]></xp:this.databaseName>

Use the search function in DDE to locate where these bindings are used.

Valuepickers

Value pickers are used throughout the Teamroom application and for the dataProvider property you need to calculate the location of the database.

Search after dataProvider and you will get the list of design elements where they are used. Look for the once who use the xe:dominoViewValuePicker since they are using a View as data-source. Again make the database location computed.

Addressbook

Some dataProviders use xe:dominoNABNamePicker and the location of the server is set via the Teamroom Setup function, which asks for the web server address. Default the web server address where the design resides is filled in. I tried to fill in the ip address of the server where the data resides (gathered via dao.getServer()) but this still did not allow me to open an NAB.

@dbcolumn @dblookup

What is an XPages application without the @dbcolumn and @dblookup functions? Use DDE to allocate in which design elements there are used and exchange the @DbName() function with bluemixContext.isRunningOnBluemix()? bluemixContext.getDataService().atDbName():@DbName or dao.getServer()+”!!”+dao.getDatabase()

database object

With the database object is you can make a quick reference to the NotesDatabse class, and so it is used throughout the Teamroom application. Mind a database.isFTIndexed() to check if the search bar will be displayed or not? Again do your search in DDE.

You can apply method chaining e.g. dao.getDatabase().isFTIndexed()

Run form validation

I noticed that I had to disable the option to validate the saving of documents using form validation (on document save option). Otherwise the operation failed silently.

Changing form name

Adding a team member via the browser resulted in a document created with the form reference of Team Member Profile   –   ParticipantProfile. If you add a member via the Notes client the form reference will be ParticipantProfile. This explains why newly created members did not appear in the list at first.

So I modified the form name as:
<xp:dominoDocument formName=”ParticipantProfile” …

I also set this alias as first in the Notes form. Not sure if that is necessary.

Wrap-up

So far so good. So how “Bluemix ready” is my Teamroom and what are my first thoughts?

Except the lookup to the name and address book I pressume my Teamroom is working as normal. I guess you can call this is major issue.

The tag cloud is not working, but that is due to the fact that the  xp:tagCoud control does not provide a database property (yet). Again I would call this a major issue.

Looking at the design of the Teamroom and counting the number of design elements I would think the development team has made the application design too complicated. I think the power of custom controls and property definitions is that you can re-use them in complete other ways and so reduce the amount of design elements.

I also notified almost no Java code in the design. Perhaps adding some classes for the objects can also reduce the amount of design elements dramatically. Also the application might get less spaghetti coded with logic stored in a central place.

Going through the code resulted in some new “discoveries” and was therefor fun to do. I will write another post about the things I haven’t used before.

Happy development and looking forward to next week’s Engage =)

 

 

Resource optimization for Anonymous users

The ‘Use runtime optimized … resources’ is a great XSP Properties property to downsize the amount of calls to the Domino server for downloading stylesheet and javascript resources.

In order to reduce the size of the download(s) the rendered property in a Theme design element can be handy.

For example resources that are used in Xpages that are not accessible by Anonymous users you can initially restrict via:

Screen Shot 2016-03-08 at 12.15.01

When they later logon and access XPages that use the resource, it will be available.

Just a simple tip. Happy development =)

Styling the Pager control for Bootstrap UI

Many of us are acquainted with  Bootstrap in our XPages application. However if you place a Pager control on your XPage it does not render according bootstrap style.

So what can you do?

If you have the option to install the Extension Library with Bootstrap in it: DO IT.

If you don’t have that option you can place the Bootstrap resources in your NSF or use a CDN.

If you are using these last two options you will notice the Pager control is still non-Bootstrap styled. You can either:

  • Apply CSS yourself (link, link)
  • Apply your renderer (link)

I noticed in the first approach the … display for page numbering will not be proper styled:

Screen Shot 2016-03-01 at 10.46.13.png

If you are a CSS-wizard you probably can fix this (I can’t).

The second approach renders the Pager perfectly with Bootstrap style applied and hopefully the strange behaviours Frank van der Linden refers to do not appear.

Happy development =)

 

 

Bootstrap Pills navigation for Dynamic Content control

Introduction

Besides parameters you can include a hash (#) property in the URL in your XPages application. This “fragment identifier” is a short string of characters that refers to a resource that is subordinate to another, primary resource.

So it is mainly used to set a location anchor to a part in a page.

In this post I will show how to get value of Hash property in XPages and make a nice Bootstrap Pills navigation control for the Dynamic Content Control

XPages

In XPages you can use it within the xp:link control but the dynamic content control also makes use of it.

XSnippet Dynamic Content Control

The following XSnippet demonstrates how to set up a dynamic content control:

<?xml version="1.0" encoding="UTF-8"?>
  <xp:link escape="true" text="Load Dynamic Content Server side" id="link1">
    <xp:eventHandler event="onclick" submit="true" refreshMode="partial">
      <xp:this.action>
        <![CDATA[#{javascript:getComponent("dynC").show("key1")}]]>
      </xp:this.action>
    </xp:eventHandler>
  </xp:link>
  <xp:link escape="true" text="Load Dynamic Content Client side" id="link2">
    <xp:eventHandler event="onclick" submit="false">
      <xp:this.script><![CDATA[XSP.showContent("#{id:dynC}","key2")]]></xp:this.script>
    </xp:eventHandler>
  </xp:link>
  <xe:dynamicContent id="dynC" useHash="true">
    <xp:this.facets>
      <xp:panel xp:key="key1">Content1</xp:panel>
      <xp:panel xp:key="key2">Content2</xp:panel>
    </xp:this.facets>
  </xe:dynamicContent>
</xp:view>
With this snippet you get the following Hash set in the URL of your XPage: #content=…

Getting value of Hash property

I suggest to use one of the following 2 options to collect the value of the hash property:

  1. Use a custom function
  2. Use Dojo

The first option is another XSnippet by Thomas Adrian. The second is a suggestion by Mark Leusink to (re)use Dojo.

getHashUrlVars

Include the function in a shared CSJS library and call it via the function getHashUrlVars()["content"].

dojo.queryToObject

The dojo approach is also straightforward. Call the function dojo.queryToObject( dojo.hash() ); and then abstract the content property var active = hash[“content”];.

Pills navigation for Dynamic Content Control

Since there is no list item control in XPages it is a bit hard to set the properties via computation. However via CSJS you can manipulate the DOM a lot and add some responsiveness yourself.

The following script builds an unorderlist containing links to set a part of the Dynamic Content control as visible:

<xp:div id=”nav-pills-container”>
<ul class=”nav nav-pills”>
<li id=”Pictures” class=”dclist”>
<xp:link escape=”true” text=”Pictures” id=”link4″>
<xp:eventHandler event=”onclick” submit=”true”
refreshMode=”partial”>
<xp:this.action>
<![CDATA[#{javascript:getComponent(“dynC”).show(“Pictures”)}]]>
</xp:this.action>
</xp:eventHandler>
</xp:link>
</li>

<li id=”Albums” class=”dclist”>
<xp:link escape=”true” text=”Albums” id=”link1″>
<xp:eventHandler event=”onclick” submit=”true”
refreshMode=”partial”>
<xp:this.action>
<![CDATA[#{javascript:getComponent(“dynC”).show(“Albums”)}]]>
</xp:this.action>
</xp:eventHandler>
</xp:link>
</li>
<li id=”Profiles” class=”dclist”>
<xp:link escape=”true” text=”Profiles” id=”link2″>
<xp:eventHandler event=”onclick” submit=”true”
refreshMode=”partial”>
<xp:this.action>
<![CDATA[#{javascript:getComponent(“dynC”).show(“Profiles”)}]]>
</xp:this.action>
</xp:eventHandler>
</xp:link>
</li>

</ul>

The second script checks which content section is set “visible” for the Dynamic Content control via the hash property in the URL. The corresponding list item in the Pills navigation is set as active.

<xp:eventHandler event=”onClientLoad” submit=”false”>
<xp:this.script><![CDATA[function setActive(id){
$( “li.dclist” ).removeClass( “active” );
var id = “#” + id;
$(id).addClass( “active” );
}

var hash = dojo.queryToObject( dojo.hash() );
var active = hash[“content”];
setActive(active);
]]></xp:this.script>
</xp:eventHandler>

As a result I get the correct list item displayed as active for my Pills navigation:

navpills

Code

In case you want to see the code working in an example download the Bildr application on OpenNTF. There I have applied this technique for the search function.

 

Re-Unite Gallery & FIX AMD loading for XPages

Introduction

In my previous post “Another draw (unite gallery) in the waste-basket” I wrote the complication I had with getting the Unite Gallery to work properly within XPages.

It turns out that this is related to Asynchronous module definition complications within XPages., although you do not get error messages in your web browser console.

Fixing the XPages R9 dojo define.amd problem once and for all

Marky Roden wrote a post about a solution a while ago which was inspired by a x-code snippet Ferry Kranenburg posted.

The principle is quiet simple but saves you a lot of headaches:

  • duplicates define.amd to define._amd
  • deletes define.amd
  • loads the AMD enabled jquery plugin (successfully)
  • duplicates define._amd back to define.amd
  • deletes define._amd

Using a Theme

In stead of setting the fix for individual pages I am using a Theme to fix it (since I am using a Select2 combobox in my navbar header).

amd fix

Step 1 – Adding two JS libraries to your WebContent

These files contain the same code as in the XSnippet.

Step 2 – Load your resources via a Theme design element

amd_theme

As you can see I load my JavaScript libraries who use AMD within the two previous set up libraries.

Result

As a result the buttons on the XPage containing the Unite Gallery work fine again !

Happy development =)

 

Another draw (unite gallery) in the waste-basket

Gallery galore

For an application I want to provide an image slider, in stead of the default Bootstrap carousel. I remembered a blog post of Johnny Oldenburger about Unite Gallery a while ago so I gave that a try.

Repeat and xp:image

Where Johnny demonstrated some static images I want to populate the image collection so I set up a repeat control with an xp:image control inside and calculate it’s properties.

Here is what the code looks like:

Code

The code of the XPage you can download here.

 

Code walk-through

After loading the JS resources I have a script block that build the gallery when the document is ready. The div gallery functions as container for the gallery.

Then I have a repeat control. An album object is just a Notes document that contains references to other Notes documents containing the picture files. So my Album.getPictures method returns a vector of references for which I have to make a lookup (via a Picture object) and collect some values (which size picture to use (thumbnail or original).

I separate the design from the data, so I also have to collect the location of the data db.

In the example I have two ways to define the image, one is via a xp:text control and the other is via the xp:image control. I rather prefer this last option.

Result

 

Below you can see a snippet of how the result. At first it looks nice, does’t it?unitegallery

Complications with XPages

So after I got the gallery on my XPage with dynamic content I started to notice undesired behaviour. My Bootstrap navbar links (xp:link) didn’t work anymore. Buttons on the same page stopped behaving and more.

A quick search after “Unite Gallery and XPages” lead me to some stackoverflow questions where people described similar problems.

Untill now I have not found a solution to get it working.

Lesson learned

Before implementing a plugin in XPages, do some research on possible complications.

 

 

Quick way to get dynamic (BS) list groups from Notes views

Introduction

For an app I needed lists to let users quickly to have access to a subset of documents. Bootstrap has a nice feature called List Groups which gives you a nice basic UI for unordered lists with list-items.

A list group can contain badges which I find a nice alternative for a tag cloud which to me are not very mobile friendly.

Before we get started let’s show how the end-result could look like:

list-group computed

So how do we do this in XPages & Java?

First I want to point to a snippet Oliver Busse posted about Iterating through a HashMap in a repeat control. If you use that basic principle of binding a HashMap to a Repeat Control  and use a categorized view for filling the Hashmap you are there!

Java class snippet

Below is the snippet from my Java class:

public Set<Entry<String, Integer>> categoriesMap(String viewName) throws NotesException{
HashMap<String, Integer> categories = new HashMap<String, Integer>();
Database dataDB = dao.getDatabase();
String ViewName = viewName;
ViewNavigator nav = dataDB.getView(viewName).createViewNav();
ViewEntry entry = nav.getFirst();
while (entry != null && !entry.isTotal()){
categories.put(entry.getColumnValues().firstElement().toString(), entry.getChildCount());
ViewEntry tmpentry = nav.getNextSibling(entry);
entry.recycle();
entry = tmpentry;
}
nav.recycle();
return categories.entrySet();
}

The code will run through the provided Notes view and go through the categories and places the name/label and value/totals in a Map and returns a set view of the mappings contained in this map.

Custom Control

Next step is to place the list-group code from Bootstrap in a custom control and make it dynamic by generating the list-items via a Repeat control and bind that control to my Java code:

<?xml version=”1.0″ encoding=”UTF-8″?>
<xp:view xmlns:xp=”http://www.ibm.com/xsp/core”&gt;
<h2>
<xp:text
escape=”true”
id=”computedField1″
value=”#{javascript:compositeData.header}”>
</xp:text></h2>
<ul class=”list-group”>
<xp:repeat
id=”repeat1″
rows=”30″
var=”obj”
indexVar=”idx”>
<xp:this.value><![CDATA[#{javascript:var viewName = compositeData.viewName;
Picture.categoriesMap(viewName);}]]></xp:this.value>
<li class=”list-group-item”>
<span class=”badge”>
<xp:text
escape=”true”
id=”computedField2″
value=”#{javascript:obj.getValue()}”>
</xp:text>
</span>
<xp:link
escape=”true”
text=”#{javascript:obj.getKey()}”
id=”link1″>
<xp:eventHandler
event=”onclick”
submit=”true”
refreshMode=”complete”>
<xp:this.action>
<xp:openPage>
<xp:this.name><![CDATA[#{javascript:var target = compositeData.targetPage;
return target + “?filter=” + obj.getKey();}]]></xp:this.name>
</xp:openPage>
</xp:this.action></xp:eventHandler>
</xp:link>
</li>
</xp:repeat>
</ul>
</xp:view>

Properties

The custom controls takes in some properties set in the Property Definition section. Via this way I can re-use the Custom control for multiple dynamic list groups:

Conclusion

As you see, with a minimal amount of code you can create dynamic lists for your apps. In the sample above I do not handle views with more than 30 categories but I am sure you fix this yourself. Also the active class property is not implemented.

Happy development =)

 

Using HTML Entities in XPages

Introduction

Often when you’re designing a web page or application you want to use HTML entities or special characters. This happens quiet often when you are using Bootstrap in your design.

Problem

For example the header in the Bootstrap modal has a close button which is defined as followed:

<div class=“modal-header”>
<button type=“button” class=“close” data-dismiss=“modal”>&times;</button>
<h4 style=“color:red;”><span class=“glyphicon glyphicon-lock”></span> Login</h4>
</div>

When you past this code in your XPage you get the following error:

The entity “times” was referenced, but not declared.

So you cannot use this in your XPage source because it is not valid XML.

Answer

The short and quick answer is to use the numerical equivalents. In our case for &times; you will want to use & #215; (mind the space since WordPress is formatting it otherwise).

In the table on this linked page you will find on overview and description of (all) HTML Entities  and their corresponding Unicode decimals.

Below is a summary of HTML Entities I use often:

&nbsp; … & #160;

&times; … & #215;

&amp; … & #38;

&euro; … & #8364;

&quot; … & #34;

&reg; … & #174;

A simple tip, but useful none the less. Happy development =)

Responsive StandbyDialog & Cancel that operation

Introduction

Fredrik Norling has posted a new XSnippet on OpenNTF for a responsive standby  widget.  Great because in my current project a) I use Bootstrap for responsive behavior b) some operations take way more time that others.

Below is a sample how the dialog looks like:

standby_widget

When investigating the code I noticed 2 things:

  • A typo for the bootstrap model
  • The close button does not actually cancel the operation. It just closes the dialog.

Small dialog

The typo is a quickfix. Change

 '<div class="modal-dialog modal-m"><div class="modal-content">' +

into

‘<div class=”modal-dialog modal-sm”><div class=”modal-content modal-content-sm”>’ +

so you use Bootstrap’s small modal and not the default large one.

Cancel the partial refresh

For the partial refresh I found the solution in a post on Sven Hasselbach’s (holy) blog. Sven also posted the solution as an XSnippet on OpenNTF.

Scriptblock

First add a new scriptblock on the custom control that contains the Standby Dialog widget:

<xp:scriptBlock id=”scriptBlockXHRHandler”>
<xp:this.value><![CDATA[

var xhrCall = null;

dojo.addOnLoad( function(){

/*** hijack dojo’s xhrRequest ***/
dojo._xhrPost = dojo.xhrPost;
dojo._xhrGet = dojo.xhrGet;

dojo.xhrPost = function( args ){
xhrCall = dojo._xhrPost( args );
}

dojo.xhrGet = function( args ){
xhrCall = dojo._xhrGet( args );
}
});
]]>
</xp:this.value>
</xp:scriptBlock>

Then I included the call to the cancel function of the xhrCall object just before the allowSubmit function of the XSP object.

xhrCall.cancel();
XSP.allowSubmit();

Now if you click the close button in the Bootstrap modal the partial refresh is aborted.standby_widget abort