Adding a sliding menu to your Bootstrap Application Layout control

Introduction

Since responsive webdesign (RWD) is on top of the list in most application development projects these days I assume most of us have been looking at the options within IBM Notes.

Bootstrap

In case you use the Bootstrap plugin in the Extension Library you have noticed that the menu in the left column get presented above the main column in smaller devices.

bs_desktop

Image: Display on desktop

bs_phone

Image: Display on phone

As you can see in a more advanced application the menu options already suppress the content too much at the bottom. So what are your options?

Off Canvas Slide Menu For Bootstrap

In this post I will describe how I implemented in XPages a slide menu which is described here. We still use the Application Layout control with the Bootstrap RWD functionality since it contains more functionality than just to provide a navbar.

Step 1 – Provide a Menu icon

We need something (text, button, icon) from where we can initiate the appearance of the menu. In our case I choose to include a ‘hamburger’ menu icon in the navBarLogo property:

<xe:this.configuration>
<xe:simpleResponsiveConfiguration navbar=”true”
loaded=”true” navbarLogo=”/1432224591_menu-alt.png”
navbarLogoStyleClass=”extraMenu” navbarText=”MyApp”>
</xe:simpleResponsiveConfiguration>
</xe:this.configuration>

For example iconfinder provide great icons.

Step 2 – Register Click event for Menu icon

I included “extraMenu” as an additional styleclass. This styleclass we will use when the document is ready. We do this in a Script Block control:

<xp:scriptBlock id=”onLoadScript” type=”text/javascript”>

<xp:this.value><![CDATA[
$(document).ready(function(){
$(“.extraMenu“).attr(“id”,”nav-expander“);
//Navigation Menu Slider
$(‘#nav-expander‘).on(‘click‘,function(e){
e.preventDefault();
$(‘body’).toggleClass(‘nav-expanded’);
});
$(‘#nav-close’).on(‘click’,function(e){
e.preventDefault();
$(‘body’).removeClass(‘nav-expanded’);
});

// Initialize navgoco with default options
$(“.main-menu”).navgoco({
caret: ‘<span class=”caret”></span>’,
accordion: false,
openClass: ‘open’,
save: true,
cookie: {
name: ‘navgoco’,
expires: false,
path: ‘/’
},
slide: {
duration: 300,
easing: ‘swing’
}
});
});]]></xp:this.value>
</xp:scriptBlock>

When the document is ready we assign the ID attribute to the navbarLogo since this ID is used to bind an click event on. We could have bind the event directly on the class but I am not sure if the ID is used in any other way (don’t break what isn’t broken).

Step 3 – Provide a menu that slides in.

In this example I simply provide the menu that is used in the demo:

<nav>
<ul class=”list-unstyled main-menu”>
<!–Include your navigation here–>
<li class=”text-right”>
<a href=”#” id=”nav-close”>X</a>
</li>
<li><a href=”#”>Menu One<span class=”icon”></span></a></li>
<li><a href=”#”>Menu Two<span class=”icon”></span></a></li>
<li><a href=”#”>Menu Three<span class=”icon”></span></a></li>
<li>
<a href=”#”>Dropdown</a>
<ul class=”list-unstyled”>
<li class=”sub-nav”><a href=”#”>Sub Menu One<span class=”icon”></span></a></li>
<li class=”sub-nav”><a href=”#”>Sub Menu Two<span class=”icon”></span></a></li>
<li class=”sub-nav”><a href=”#”>Sub Menu Three<span class=”icon”></span></a></li>
<li class=”sub-nav”><a href=”#”>Sub Menu Four<span class=”icon”></span></a></li>
<li class=”sub-nav”><a href=”#”>Sub Menu Five<span class=”icon”></span></a></li>
</ul>
</li>
<li><a href=”#”>Menu Four<span class=”icon”></span></a></li>
<li><a href=”#”>Menu Five<span class=”icon”></span></a></li>
</ul>
</nav>

Step 4 – Include the supporting files

The demo uses the following files which you should add to your application as resources:

js

<script
src=”/jquery.navgoco.js”>
</script>

css

<xp:this.resources>
<xp:styleSheet href=”/main.css”></xp:styleSheet>
</xp:this.resources>

The result

The image below gives you an indication what the result looks like. When you click the hamburger icon the menu appears from the right. With the X marker you can close the menu again.

result

Notice that my content is not suppressed below the menu as the Bootstrap plugin would do.

Download

An demo XPages application is available on my dropbox account

Domino Access Service – Posting a document

Introduction

In our app described in previous posts I have included an interface to post a new document using Domino Access Services. Here is how I did it.

HTML structure

The main part of the ‘form’ is the HTML structure.

form

Since we use Bootstrap I included form-groups to layout the fields. In my example I still use XPages, but the inputfields you can replace with HTML input elements.

<form role=”form”>

<h2>Registration</h2>
<div id=”entryForm”>
<div class=”form-group”>
<xp:label value=”Firstname:” id=”label2″></xp:label>
<xp:inputText id=”inpFirstname” styleClass=”firstName”>
<xp:this.attrs>
<xp:attr name=”placeholder” value=”Enter firstname”></xp:attr>
</xp:this.attrs>
</xp:inputText>
</div>
<div class=”form-group”>
<xp:label value=”Lastname:” id=”label3″></xp:label>

<xp:inputText id=”inpLastname” styleClass=”lastName”>
<xp:this.attrs>
<xp:attr name=”placeholder” value=”Enter lastname”></xp:attr>
</xp:this.attrs>
</xp:inputText>
</div>
<div class=”form-group”>
<xp:label value=”Email:” id=”label1″></xp:label>
<xp:inputText id=”inpEmail” styleClass=”email” type=”email”>
<xp:this.attrs>
<xp:attr name=”placeholder” value=”Enter email”></xp:attr>
</xp:this.attrs>
</xp:inputText>
</div>
<div class=”form-group”>
<xp:label value=”Password:” id=”label4″></xp:label>
<xp:inputText id=”inpPassword” type=”” styleClass=”wannebepassword”>
<xp:this.attrs>
<xp:attr name=”placeholder” value=”Enter password”></xp:attr>
</xp:this.attrs>
</xp:inputText>
</div>
<xp:button value=”Submit” id=”button1″ styleClass=”btnRegister btn btn-default”>
</xp:button>
</div>
</form>
<div id=”formResponse”></div>

JavaScript

On the onClientLoad event of the XPage I have included the following script.

Parameters

Keep in mind to include the form name in the URL parameter (?form=Person) since you cannot include that pair in the JSON object that is send to Domino Access Service.

The &computewithform=true parameter ensures the HTTPPassword field will be translated with the @Password function.

<xp:eventHandler event=”onClientLoad” submit=”false”>
<xp:this.script>
<![CDATA[$(document).ready(function() {
$(“.wannebepassword”).attr(“type”, “password”);
$(“.btnRegister”).click(function(e) {
var firstName = $(“.firstName”).val();
var lastName = $(“.lastName”).val();
var email = $(“.email”).val();
var password = $(“.wannebepassword”).val();
var newName = $(“.firstName”).val() + ” ” + $(“.lastName”).val();
if (newName !== ” “) {
var newPersonObj = {
FirstName: firstName,
LastName: lastName,
FullName: newName,
InternetAddress: email,
HTTPPassword: password,
Form: “Person”
};
$.ajax({
url: ‘http://server/fakenames40k.nsf/api/data/documents?form=Person&computewithform=true&#8217;,
type: ‘POST’,
data: JSON.stringify(newPersonObj),
dataType: ‘xml’,
accepts: {
xml: ‘text/xml’,
text: ‘text/plain’
},
contentType: “application/json”
}).done(function(data, textStatus, jqXHR) {
var newPersonLocation = jqXHR.getResponseHeader(“Location”);
$(“#entryForm”).hide();
$(“#formResponse”).html(‘Registration successfull.<br/><a class=”btn btn-default” href=”‘ + newPersonLocation + ‘”>Check it out!</a>’);
}).fail(function(jqXHR, textStatus, errorThrown) {
alert(“Registration has failed:” + errorThrown);
console.log(‘Registration has failed.’);
});
} else {
alert(“Please enter a name”);
}
return false;
});
});]]>
</xp:this.script>
</xp:eventHandler>

Password attribute

XPages does not allow you to define the password type as attribute. I found the answer from Tim Tripcony on Stackoverflow helpful to define a work-around.

Wrap up

That is about it!

This is just a simple example. Probably you would normally include some more validation (avoid double entries) but you get the idea.

Adding search to our app

Introduction

In this post I will demonstrate how to apply a search to our application described in previous posts, in which we read the JSON from Domino Access Service and parse it with the Jackson library to display it thereafter with a Repeat control.

Since I am using in our application the Application Layout control with a responsive Bootstrap UI (shameless pitch to glory the persons who delivered this great functionality to XPages) which includes a searchBar section and scrolling through 40K of person records is not something you want to anguish your thumb with, a search functionality gives the advantage to skip scrolling through unnecessary documents.

searchBar section

Here is how I set up the searchBar section in the Application Layout control:

searchbar

As you can see I have defined a result page for the search, called search.xsp.

Here is how the search bar will look like for the user:

searchbox

XPage search

beforePageLoad event

The beforePageLoad event has slightly changed compared with event in our initial XPage. This time we search for the search parameter defined in the searchBar section:

var query = context.getUrlParameter(“search”);
var persons = new Array();
persons = personsBean.getPersons(“http://dev1/fakenames40k.nsf/api/data/collections/name/people?count=25&search=” + query);
viewScope.put(“names”,persons);

Next is the result list. I have added an additional list item and added the active class:

This item will display the search query we have provided:

acgtive

The Repeat control has also changed slighty. The rows property has been adjusted. We calculate the number of documents returned by the full text search.

<![CDATA[#{javascript:database.updateFTIndex(true);
var query:string = context.getUrlParameter(“search”);
var vw:NotesView = database.getView(“people”);
var totNums:Integer = vw.FTSearch(query).toFixed();
return totNums}]]>

Also the text that indicates where we are in the result set has been modified:

Tip: since we are actually performing FT searches here perhaps it is better to put the counted results already from our initial call in a scope variable and re-use it =(

The button in our infinite scroll function/custom control has also been adapted. Here is the code:

Here is what the end result looks like after a search:

endresult

Infinite scroll for our Domino Access Services & Jackson app

Introduction

In a previous post I demonstrated how to process JSON data from Domino Access Services with the Jackson library. In this post I follow up on that and demonstrate how you can apply an infinite scroll function to it so you can navigate through the presented list in a mobile app kinda way:

app

Class DASRest

Include Serializable

Since we are going to add more Java objects to our list we will use a scope variable to store the values in. Because of the usage we need to include serializable in our DASRest class:

package com.quintessens.FakeNames;

import java.io.Serializable;
import javax.ws.rs.core.MediaType;
import org.codehaus.jackson.map.ObjectMapper;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;

public class DASRest implements Serializable {

private static final long serialVersionUID = 1 L;

public static Person[] getPersons(String url) {
Person[] persons = null;
try {
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource service = client.resource(url);
String json = service.accept(MediaType.APPLICATION_JSON).get(String.class);
ObjectMapper mapper = new ObjectMapper();
persons = mapper.readValue(json, Person[].class);

} catch (Exception e) {
System.out.println(“catch…”);
e.printStackTrace();
}
return persons;
}
}

Managed Bean

We will also register our class as a Managed Bean in the faces-config.xml file:

<?xml version=”1.0″ encoding=”UTF-8″?>
<faces-config>
<managed-bean>
<managed-bean-name>personsBean</managed-bean-name>
<managed-bean-class>com.quintessens.FakeNames.DASRest</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
</faces-config>

beforePageLoad event

In the beforePageLoad event of the XPage where we will display the result list we will make the initial call to the Domino Access Service and collect the fist set of documents:

var persons = new Array();
persons = personsBean.getPersons(“http://dev1/fakenames40k.nsf/api/data/collections/name/people?count=25&#8221;)
viewScope.put(“names”,persons);

As you can see we still use the fakenames application. The received result we place in a viewScope variable. Now we need to bind this variable to a repeat control.

Repeat Control

Our repeat control looks pretty straight forward. I included already some mark-up since we will utilize Bootstrap for our UI presentation:

<ul class=”list-group”>
<xp:repeat id=”rptPersons” indexVar=”persIndex” var=”persData”>
<xp:this.value>
<![CDATA[#{javascript:viewScope.get(“names”)}]]>
</xp:this.value>
<xp:this.rows>
<![CDATA[#{javascript:var vw:NotesView = database.getView(“people”);
return vw.getAllEntries().getCount().toFixed();}]]>
</xp:this.rows>
<li class=”list-group-item”>
<xp:text escape=”true” id=”computedField1″ value=”#{javascript:return persData.name}”>
</xp:text>

</li>
</xp:repeat>
</ul>

Infinite Scroll

My infinite scroll function is inspired by the excellent snippet from Frank Kranenburg and available on OpenNTF.

<?xml version=”1.0″ encoding=”UTF-8″ ?>
<xp:view xmlns:xp=”http://www.ibm.com/xsp/core&#8221; xmlns:xe=”http://www.ibm.com/xsp/coreex”&gt;

<!– make sure ‘add rows’ component is hidden –>
<style>
.infiniteScroll {
display: none;
}
</style>

<xp:button value=”Label” id=”button2″ styleClass=”infiniteScroll”>
<xp:eventHandler event=”onclick” submit=”true” refreshMode=”partial” refreshId=”#{javascript:compositeData.repeatId}”>
<xp:this.action>
<![CDATA[#{javascript:var repeater = compositeData.repeatControlId;

var counter = getComponent(repeater).getRowCount();
var numb = compositeData.increment;
var url = compositeData.baseURL;

var scope = compositeData.scopeVarName

var currPersons = viewScope.get(scope);
var morePersons = personsBeanTest.getPersons(url + counter + “&count=” + numb);
var allPersons = currPersons.concat(morePersons);

viewScope.put(scope,allPersons);

}]]>
</xp:this.action>
</xp:eventHandler>
</xp:button>

<!– small script to check if we need to auto-click the ‘add rows’ button –>
<xp:scriptBlock id=”scriptBlock1″>
<xp:this.value>
<![CDATA[$(window).scroll(function(){
if($(window).scrollTop() == $(document).height() – $(window).height()) {
$(“.infiniteScroll”).click();
}
});]]>
</xp:this.value>
</xp:scriptBlock>
</xp:view>

Under the event handler of the button we collect the current array with objects from the viewscope, we make a new call to Domino Access Services with our current position and we join the received next set of objects with the current set and then we write things back to the viewscope.

We then partial refresh the design element (a panel) that contains both the repeat control and the infinite scroll.

I defined some properties for the custom control in case I might re-use it for other views…

infscroll

That is basically it! On my Xpage I included some text to indicate where we are in the view and that there are more documents available:

<xp:text escape=”true” id=”computedField2″><xp:this.value><![CDATA[#{javascript:var vw:NotesView = database.getView(“people”);
var currNums:Integer = getComponent(“rptPersons”).getRowCount();
var totNums:Integer = vw.getAllEntries().getCount().toFixed();

return “Displaying rows ” + currNums + ” out of ” + totNums;
}]]></xp:this.value></xp:text>

Processing JSON data from Domino Access Services with Jackson

Introduction

In order to separate the data model from the business logic you could use Domino Access Services as your default data provider and process the JSON client- or server-side.

In this blog I demonstrate how you can use the Jackson library to process the incoming JSON and bind it to a repeat control.

About Jackson

Jackson is a multi-purpose Java library for processing JSON data format. Jackson aims to be the best possible combination of fast, correct, lightweight, and ergonomic for developers.

Find out more about the library here.

Sample – Fakenames application

In this example we will use the infamous fakenames application from codestore (grab it while it still out there). I have altered the data in my example a bit more so it contains company and job title information.

If you happen to have a script who can fill in the database with more sensible data please drop a line here.

People view

We will use the people view as our data-source  and access it via Domino Access Service. The  URI for the view is something as followed (depending on your installation):

http://server1/fakenames.nsf/api/data/collections/name/people

XPage

We will use and XPage to display the data from the view. The xpage contains a repeater and a couple of fields e.g.:

<?xml version=”1.0″ encoding=”UTF-8″?>
<xp:view
xmlns:xp=”http://www.ibm.com/xsp/core&#8221;
xmlns:xc=”http://www.ibm.com/xsp/custom”&gt;

<xp:panel>
<xp:repeat id=”rptPersons” rows=”10″ var=”persons”
value=”#{javascript:com.quintessens.FakeNames.DASRest.getPersons(‘http://server1/fakenames.nsf/api/data/collections/name/people?count=100&#8217;)}”>
<xp:panel id=”personsPanel”>
<h1>
<xp:text escape=”true” id=”computedField1″
value=”#{javascript:persons.getName()}”
>
</xp:text>
</h1>
<h3>
<xp:text escape=”true” id=”computedField2″
value=”#{javascript:persons.getCompanyname()}”
>
</xp:text>
</h3>
<xp:text escape=”true” id=”computedField3″
value=”#{javascript:persons.getJobtitle()}”
style=”font-style:italic”>
</xp:text>

</xp:panel>
</xp:repeat>
<xp:pager layout=”Previous Group Next”
partialRefresh=”true” id=”pager1″ for=”rptPersons”>
</xp:pager>
</xp:panel>
</xp:view>

Note I call in the repeater control a method getPersons in the class DASRest and provide an URL of the data-source.

I add the count parameter to get more initial results than set in the server configuration.

I also have added a pager control to navigate through the returned collection.

DASRest class

The DASRest class looks as followed:

package com.quintessens.FakeNames;

import javax.ws.rs.core.MediaType;
import org.codehaus.jackson.map.ObjectMapper;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;

import com.quintessens.FakeNames.Person;

public class DASRest {

public static Person[] getPersons(String url) {
Person[] persons = null;
try {
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource service = client.resource(url);
String json = service.accept(MediaType.APPLICATION_JSON).get(String.class);
ObjectMapper mapper = new ObjectMapper();
persons = mapper.readValue(json, Person[].class);
} catch (Exception e) {
e.printStackTrace();
}
return persons;
}
}

Jersey

This class uses the Jersey library to setup a client to contact the Domino Access Service.

Developing RESTful Web services that seamlessly support exposing your data in a variety of representation media types and abstract away the low-level details of the client-server communication is not an easy task without a good toolkit. In order to simplify development of RESTful Web services and their clients in Java, a standard and portable JAX-RS API has been designed.

The received JSON is then converted into a Java object. I find this a good site to learn more about mapping JSON and Java.

Person class

The class also uses a Person class. In this class we specify which fields we want to map and how they should look like:

package com.quintessens.FakeNames;

import javax.xml.bind.annotation.XmlRootElement;

import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;

@XmlRootElement
@JsonIgnoreProperties(ignoreUnknown = true)
public class Person {
private String name;//$17
private String companyName;
private String jobtitle;

@JsonProperty(“$17”)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@JsonProperty(“CompanyName”)
public String getCompanyname() {
return companyName;
}
public void setCompanyname(String companyname) {
this.companyName = companyname;
}
public String getJobtitle() {
return jobtitle;
}
public void setJobtitle(String jobtitle) {
this.jobtitle = jobtitle;
}
}

As you can see @JsonProperty(“$17”) makes the code a bit more user-friendly. In this case it maps the programmatic column name with a variable name.

The result

The following image shows the result of the code above:

jackson

As you see the markup is still basic but you can easily beautify it with Bootstrap or preferred CSS framework of choice.

The purpose of this blog was to demonstrate separating the data model and the business logic. You could have received the same result just using a repeat control and server-side javascript. But since the JSON & Java combination is more hype in the Domino world I guess it is interesting to take a look at the possibilities.

Wrap up

Some thoughts I have after this experiment:

  • What about the other documents?

In my example I make a call to DAS and include the count = 100 parameter. The number 100 is the limit that is set on my server. I have not figured out yet how to load a next set of documents and include it to the existing collection (please drop a note in case you have an answer to this).

  • What about performance?

Is Domino Access Services faster than e.g. defining my own data provider e.g. via a viewnavigator? I don’t know. In case you have suggestions on the preferred or fastest way to provide the data then let me know.

  • What about the language?

Java is not my native language in the Domino world. But the example above is understandable for a lot of us I would think. But who died out of a bit of curiosity?

Thanks for reading. Happy coding!

Born social – Create a Social Notes application from scratch

Introduction

A long time ago (it seems) I had the idea to prepare integration of Connections with Xpages development via the SBTSDK. I thought this would be a good way to learn Connections and it’s API more in details. By setting up custom controls you would be able to build mash-up apps piece by piece.

However there seemed to be little interest for this in the market (correct me if I am wrong) so I continued with other technologies.

Lately Mark Roden posted a couple of posts on his blog regarding the social business toolkit so I hop on to this and post my previous work. Hopefully it can help someone getting started.

Downloads

Born Social – the doc

The document Born Social describes how to setup your server, development environment and application to get started developing with the SBTSDK.

It also provide code samples of custom controls to display information from IBM Connections.

born social

Previous Posts on SBT SDK

Perhaps it helps to read some previous posts I wrote on the SBT SDK.

Born Social – the app

The following file contains an NSF file with all the code, custom controls and some sample xpages.

born social app

Summary

I hope the provided samples and guidance will help you to get started with the SBT SDK.

At IBMConnect at the IBM developers booth I asked what the upcoming plans where for SBT SDK and if we could get more controls for XPages but the answer was that IBM had received very few questions for application development with Connections :-/

Perhaps if more people debunk this mind-set at IBM, perhaps by sharing their samples, interest will grow.