Save your day, Save the state of your repeat control

I am writing a demo application with a faceted search functionality. For displaying the “results” I am using a repeat control.

Now users want to switch back and forth between the “view” and the “document”. If you display the “results ” in a “view” with an “view panel” control you can apply a pager to that view and for that pager you can apply a “pager save state” control. So when you go from “results” xpage to the “document” xpage and back you land in the same state (same page in that view) and the user does not have to navigate again to the last page in the view panel control.

Now with the repeat control that is a different thing. The “pager save state” was not designed for it. Luckily there is an approach which will deliver you the same result. Add for your repeat control the property: first=”#{javascript:return (sessionScope.first != null)?sessionScope.first:0;}”. Than from the link (could be a button or whatever) in the repeat control add the following in the onClick event handler: sessionScope.put(‘first’,getComponent(“rpCollection”).first);. Then do your normal stuff (like redirect to a page). If you from that redirected page go back to the “results” page the repeat control will show the last set of items from where you left.

I hope it will save your day and your project. Happy coding! 🙂

Quickly adding localization to an XPages app

I needed to quickly make an application available in multiple languages. Luckily I had all strings already gathered in a properties file. So the following things I needed to add:

  • A translated version of the strings properties file
  • Some sort of navigation to choose another language than the browser default one.

For the first step Google Translate was my friend. Step two was not so hard either:

context.setLocaleString(‘en’)
context.reloadPage()

So a couple of minutes work and a surprised customer 🙂

Capture

Passing a SSJS function to a custom control

Introduction

I am building a custom control that mimics the viewPanel control but it is a repeat control which data is not a view but a managed bean that returns an arraylist of java objects (representing Notes documents).

I am using a Bootstrap table for display I in a previous post I have demonstrated how I can provide a JSON object to have flexibility in the columns I want to display and the values I want to display.

Next step is the option to provide custom actions to the custom control, in a way that the code under an action button in the custom control is provided via a property. So in one case hitting the button could print all selected documents, in the other case it could remove all documents from the database.

Step 1 – Creating an arraylist of unid’s

Key here is that I have an arraylist of unid’s to work with. The technique how to select documents in a repeat control was demonstrated by David Leedy in Notes in 9 episode 25. Instead of buttons I use a checkboxgroup:

So now I want from my xp:button in my custom control do something with this arraylist of unid’s…

Step 2 – set up the property definition

Next step is to setup the property in the property definition of the custom control:

Not the most common type of class and editor you use for a property.

Step 3 – setup the event handler for the button

In order to have the button to understand that the action to be performed is coming from a action property we need to specify that in the onClick event:

(Note: my property resides in the group property actionButton)

Step 4 – set up the SSJS  function you want to run

So now our button knows it’a action comes from a propert we need to write the SSJS function for the button. I have placed it in a SSJS script library. Here is an example to remove documents from a database:

Step 5 – Add the SSJS to the property

The last step is the most tricky one. In the property you are not allwed to provide any parameters or parentheses for the SSJS function. So our action property becomes as followed:

Result

As a result I have the following UI:

  • A custom control with a button which onClick action is provided via a property on that host xpage.

IBM Champion Nomination

Is this blog-article useful to you? Perhaps you can nominate me as IBM Champion.

Quick responsive type-ahead function

For an XPages form I needed a workaround for a combobox with a large data-set. At first I tried the select2 jQuery plugin so I could also have a filter option as some form of type-ahead. However it turned out that my select2 list became quiet unresponsive when an entry far in the list was selected (generating the list and filtering was fine).

So my next option was to go for a type-ahead approach. First hit on Google was a NotesIn9 vid about a fancy type based upon the original post of Tim Tripcony (hello 2009).

Due to the large data-set instead of the getAllEntriesByKey method I decided to go for a getEntryByKey method and from the found view entry continue with createViewNavFrom. This turned out to be extremely responsive!

Further I noticed in the original code a hash object is used so that does not guaranteed me a correct order of my result list so I added an additional array, just for a returning a list with correct sort order.

I like especially in this approach the option to control the returned markup so think here about Bootstrap Media object lists. And of course the ease to set the value that you want to have returned in the edit box.

This is not my first blog-post on delivering search function to your application so now you have just another solution available for your toolbox.

The script is available as a gist.

Happy development =)

 

Reading files stored in the NSF webcontent folder

In an XPages application I am creating presentations (openxml presentations is perhaps a more correct defintion I guess).

Since I would like to use the template for house-styling I have to read it in from somewhere and use it in my code. I do not want to go for the option to read it from a server location, so after a while I ran into Sven Hasselbach’s NAPIUtils class available on GitHub.

If you search a bit further I found this handy description on Notesin9 how to add the lwpd.domino.napi.jar file to the build path of your application, which is required.

What the NAPIUtils class makes possible is to read files that reside inside your NSF e.g. the webcontent folder.

Just call something like

var fileIn:java.io.FileInputStream = NAPIUtils.loadBinaryFile(database.getServer(),database.getFilePath(),”your_file_in_webcontent_folder.correctextension”)

And you have a your file in a FileInputStream!

In my case I am using

var inputstream = NAPIUtils.loadBinaryFile(database.getServer(),database.getFilePath(),”Duffbeers.pptx”);
var ppt: XMLSlideShow = new XMLSlideShow(inputstream);

And I have my presentation template residing in my NSF in my XMLSlideshow object =)

So no more hustling and asking your administrators for a folder on your Domino server to read/write to.

Ofcourse, they probably have to change the java security settings, but that is another discussion. 😕

In some cases you do not want users to have access directly to an URL of a resource. In such cases you could read the file in and write it out in the browser. E.g.:

<?xml version=”1.0″ encoding=”UTF-8″?>
<xp:view xmlns:xp=”http://www.ibm.com/xsp/core”&gt;
<xp:this.beforeRenderResponse><![CDATA[#{javascript:importPackage( ch.hasselba.napi );

var exCon = facesContext.getExternalContext();
var response = exCon.getResponse();
var out = response.getOutputStream();
try {
response.setContentType(“application/octet-stream”);
response.setHeader(“Content-Disposition”,”attachment;filename=cv_patrick_kwinten.pdf”);
response.setHeader(“Cache-Control”, “no-cache”);
var fileIn:java.io.FileInputStream = NAPIUtils.loadBinaryFile(database.getServer(),database.getFilePath(),”Patrick_Kwinten_visualcv_resume.pdf”)
var buffer = new byte[10000];
var len;
while ((len = fileIn.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
} catch (e) {
// some basic error logging here…
} finally {
if (fileIn != null) {
fileIn.close();
}
if (out != null) {
out.close();
}
facesContext.responseComplete();
}}]]></xp:this.beforeRenderResponse>
</xp:view>

Happy development =)

Add 20 years of experience to your workforce

You can 20 years of experience within IBM Notes and Web development to your workforce by hiring me.

Interested? Read my curriculum vitae on LinkedIn: http://www.linkedin.com/in/patrickkwinten and get in contact.

I am happy to work WITH you !

Show / hide columns dynamically

To show or hide columns dynamically is a nice feature in the jQuery dataTables component. With a few simple steps you can deliver something similar in your XPages application as I demonstrate in the video.

The technologies used: XPages, Extension Library, Bootstrap.

Add 20 years of experience to your workforce

You can 20 years of experience within IBM Notes and Web development to your workforce by hiring me.

Interested? Read my curriculum vitae on LinkedIn: http://www.linkedin.com/in/patrickkwinten and get in contact.

I am happy to work WITH you !

Question: Is it possible to set via a resource via a theme so it is applied to any XPage in an application?

I received a question if it is possible to set (or add) a resource via a Theme resource so it will become available to any XPage or Custom control?

Yes ofcourse you can e.g. via:

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

but you should keep in mind is that a Theme is ONLY applied in the Render Response phase. So if for example you include an SSJS library to the Theme, you will not be able to use it in the beforePageLoad or afterPageLoad event. A mistake that is common made.

My recommendation would therefor be to use a Theme more for applying default settings or to overwrite properties to a specific type of component, e.g. a default pageName property on the ViewRoot component.

In terms of adding resources I would advice to use a Custom control design element. SSJS that will be computed dynamically only run once per page load / refresh in a theme (just in Render Response), compared to every phase of the lifecycle in a Custom control.

Happy development =)

JavaScript Internationalization & placeholders

Introduction

Some time ago I wrote about moving a Teamroom application up to IBM Bluemix. This month the XPages runtime is now general available.

In the post I mentioned I would come back on some design gotchas I saw while analyzing and preparing the Teamroom application for staging it to Bluemix. So here is (at least9 a first post.

What I noticed for example was the usage of the l18n library in SSJS and placeholders in properties files.

i18n formatting

This is a server script library with methods to format messages for localization, display dates in a locale-specific manner and miscellaneous Internationalization utilities.

So how does this works?

Create two or more strings properties file e.g.

  • strings.properties
  • strings_sv.properties

The strings.properties file content:

compliment=Handsome

hello.world=Hello {0}

The strings_sv.properties file content:

compliment=Snygging

hello.world=Hejsan {0}

Now create an XPage and place e.g. a Computed Field control on it and calculate it’s value:

<xp: text><xp: this.value><![CDATA[#{javascript:compliment = strings.getString(“compliment”);

return i18n.format(strings.getString(“hello.world”), compliment);}]]></xp: this.value></xp: text>

(Do not forget to enable Internationalization for your application in the Application Properties under section International Options)

Placeholders

Notice in the properties file the usage of a placeholder: hello.world=Hello {0} and how it is being referred: strings.getString(“hello.world”), compliment. Here the variable compliment (another string property value) is put in the placeholder.

As a result in the default language I get returned “Hello Handsome” and when I choose Swedish as default language in my browser I get returned “Hello Snygging”.

By wrapping it and formatting it with the L18n library you ensure it is fitted according the active language. This is very handy for dates, amounts etcetera.

Links of interest

More details about JavaScript Internationalization can be found in the IBM Notes Domino Application Development wiki.

 

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 =)