Building a search function with DataTables plugin (II)

Introduction

In a previous post the base foundation was set up and a table was drawn on the xpage with the datatables jquery component. The data, residing in a Notes view, was delivered via a CustomServiceBean Rest service.

A datatable comes out of the box with a little search field which performs a search across the whole json data-set.

Individual column searching

In this post we will be adding select inputs for each column so you have a quick filter mechanism matching the values in the columns.

To achieve this we need to make some changes to the html table and our script to initialize the datatable object.

HTML table

We will place the select inputs on the bottom of the table, in the to be added footer section:

<table id=”persons” class=”table table-striped table-bordered”
cellspacing=”0″ width=”100%”>
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Company</th>
</tr>
</thead>
<tfoot>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Company</th>
</tr>
</tfoot>
</table>

initComplete

In our JS function that initializes the datatable we add a callback function that is triggered when the table is fully loaded:

initComplete: function(){
this.api().columns().every( function () {
var column = this;
var select = $(‘<select><option value=””></option></select>’)
.appendTo( $(column.footer()).empty() )
.on( ‘change’, function () {
var val = $.fn.dataTable.util.escapeRegex(
$(this).val()
);

column
.search( val ? ‘^’+val+’$’ : ”, true, false )
.draw();
} );

column.data().unique().sort().each( function ( d, j ) {
select.append( ‘<option value=”‘+d+'”>’+d+'</option>’ )
} );
} );
}

Progress

As a result of the activities above our table now looks like:

datatables02

The seach field works in conjunction with the select inputs. Not bad for a few lines of code!

JQM & Domino Data Service (2)

In a previous post I demonstrated how to use Domino Data Service (as part of Domino Access Service) to populate a listview for jQuery Mobile. Probably the real-world application you want to mobilize has multiple layers of data that is common in Notes (multiple forms & views). I followed Christophe Coenraets’s example and re-used it for the fakenames application.

First let me show you what you get:

Screenshot_1

 

Screenshot_2

 

At first an overview of the People’s view is presented, from here the user can open the Person details page. In Christophe’s example you can find a more nested navigation but our fakenames application is not setup like that.

What this demo includes:

  • 2 XPages; eg people.xsp and person.xsp.
  • Your modified version of employeelist.js and employeedetails.js in the download from Christophe page.
  • The CSS files in the download from Christophe page.
  • The jQuery JS files in the download from Christophe page.

For convenience I have dropped the files from the download via the Package Explorer in the WebContent folder of the fakenames application. In case you want to mobilize your fakenames application a step further e.g. by packaging it via PhoneGap to access device native api’s you don’t want to store these files here and you don’t want to use XPages to render the HTML.

Of course we assume you have Domino Access Service enabled for the Domino server and the fakenames application.

XPage people.xsp & person.xsp

These files do not differ from the index.html and employeedetails.html files in the download from Christophe page. In case you use XPages you should consider to turn of Dojo and Themes.

JS files employeelist.js & employeedetails.js

Since we will provide the information via Domino Data Service these JS files will be altered. Below you find their code:

employeelist.js

var serviceURL = “http://dev1//apps/fakenames.nsf/api/&#8221;;

var employees;

$(‘#employeeListPage’).bind(‘pageinit’, function(event) {
getEmployeeList();
});

function getEmployeeList() {
$.getJSON(serviceURL + ‘data/collections/name/People?count=1000’, function(data) {
$(‘#employeeList li’).remove();
employees = data;
$.each(employees, function(index, employee) {
var tmp = “”;
tmp+='<li>’;
tmp+='<a href=”person.xsp?id=’ + employee[“@unid”] + ‘”>’ +
‘<img src=”http://greenhouse.lotus.com/plugins/plugincatalog.nsf/NoPhotoPerson.gif”/>&#8217;;
tmp+='<h4>’ + employee.$17 + ‘</h4>’;
tmp+='<p>’ + employee.CompanyName + ‘</p>’;
if (employee.$12!=””) {
tmp+='<span class=”ui-li-count”>’ + employee.$12 + ‘</span>’;
}
tmp+='</a></li>’;
$(‘#employeeList’).append( tmp );
});
$(‘#employeeList’).listview(‘refresh’);
});
}

Walkthrough

At the page initiation the function getEmployeeList is called. This makes an Ajax request to the REST API of Domino Data Service and a document collection with the first 1000 entries (if available) in the view named People is being called.

I have not taken a look yet how to implement lazy loading or appending additional document collection(s) when scrolling. If you happen to know how to implement such function please drop me a line here or send me an email.

Then list items in the employee ID element on the Xpage people.xsp are removed (when available) and we loop through the results returned by Domino. We create a new list and establish a hyperlink to people.xsp and add the UNID of the document as parameter.

At last the result is appended to the (emptied) employee ID element and the listview is being refreshed. Wow! You got now a nice list of persons in the directory.

employeedetails.js

$(‘#detailsPage’).live(‘pageshow’, function(event) {
var id = getUrlVars()[“id”];
$.getJSON(serviceURL + ‘data/documents/unid/’+id, displayEmployee);
});

function displayEmployee(data) {
var employee = data;
console.log(employee);
$(‘#employeePic’).attr(‘src’, ‘http://greenhouse.lotus.com/plugins/plugincatalog.nsf/NoPhotoPerson.gif&#8217;);
$(‘#fullName’).text(employee.FirstName + ‘ ‘ + employee.LastName);
$(‘#employeeTitle’).text(employee.Title);
$(‘#city’).text(employee.OfficeCity);
console.log(employee.officePhone);
if (employee.Manager) {
$(‘#actionList’).append(‘<li><h3>Manager</h3><p>’ + employee.Manager + ‘</p></li>’);
}
if (employee.InternetAddress) {
$(‘#actionList’).append(‘<li><a href=”mailto:’ + employee.InternetAddress + ‘”><h3>Email</h3>’ +
‘<p>’ + employee.InternetAddress + ‘</p></a></li>’);
}
if (employee.OfficePhoneNumber) {
$(‘#actionList’).append(‘<li><a href=”tel:’ + employee.OfficePhoneNumber + ‘”><h3>Call Office</h3>’ +
‘<p>’ + employee.OfficePhoneNumber + ‘</p></a></li>’);
}
if (employee.CellPhoneNumber) {
$(‘#actionList’).append(‘<li><a href=”tel:’ + employee.CellPhoneNumber + ‘”><h3>Call Cell</h3>’ +
‘<p>’ + employee.CellPhoneNumber + ‘</p></a></li>’);
$(‘#actionList’).append(‘<li><a href=”sms:’ + employee.CellPhoneNumber + ‘”><h3>SMS</h3>’ +
‘<p>’ + employee.CellPhoneNumber + ‘</p></a></li>’);
}
$(‘#actionList’).listview(‘refresh’);
}

function getUrlVars() {
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf(‘?’) + 1).split(‘&’);
for(var i = 0; i < hashes.length; i++){
hash = hashes[i].split(‘=’);
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}

Walkthrough

The idea behind the details page is similar to the people’s list, except we have now a single object in the JSON.

First the ID provided by the parameter is abstracted from the URL and used in the call to a document source via the Domino Data Service.

From the result a set of details is abstracted and presented to the user. Some HTML5 attributes are used to deliver a more native behavior when clicking anchor links (telephone, mail, SMS).

Some thoughts

jQuery Mobile and Domino Access Service provide good foundations for creating mobile Web apps on Domino. However I am a bit thoughtful on the speed of Domino Access Service. If you happen to know how to speed up performance for this please drop a line here.

In a next post I will write the implementation of CRUD actions with jQuery and DDS.

 

listview in JQuery Mobile via Domino Access Service

Here follows an quick example how to create a listview in JQM created with data from a view via DAS. As data source I have used the infamous Fake Names Address book which you can (still) can find on Codestore.

In the example you can navigate between the view (People view) and details (Person view).

people_view

person_view

I have not really find out (yet) how to add the next set of documents when you scroll down the list (to be continued, or drop the solution in the comments or send me an email).

<?xml version=”1.0″ encoding=”UTF-8″?>
<xp:view xmlns:xp=”http://www.ibm.com/xsp/core”&gt;
<xp:this.data>
<xp:dominoView databaseName=”apps\fakenames.nsf”
viewName=”People” var=”viewPeople”>
</xp:dominoView>
</xp:this.data>
<xp:this.resources>
<xp:script src=”/global/jquery/jquery.js” clientSide=”true”></xp:script>
<xp:script src=”/global/jquery/jquery.mobile.js” clientSide=”true”></xp:script>
<xp:styleSheet href=”/global.jquery.mobile.css”></xp:styleSheet>
</xp:this.resources>

<xp:scriptBlock id=”scriptBlock1″>
<xp:this.value><![CDATA[$(document).on(‘pageinit’, function () {

$.ajax({
url: ‘http://dev1//apps/fakenames.nsf/api/data/collections/name/People&#8217;,
type: ‘GET’,
async: true,
success: function (result) {
ajax.handleData(result);
},
error: function (request, error) {
alert(‘Network error has occurred please try again!’);
}
});
});

$(document).on(‘pagebeforeshow’, ‘#headline’, function () {
$(‘#movie-data’).empty();

$.each(movieInfo.result, function (i, row) {
if (row[“@position”] == movieInfo.id) {
$(‘#movie-data’).append(‘<li><img src=”https://greenhouse.lotus.com/plugins/plugincatalog.nsf/NoPhotoPerson.gif”></li>&#8217;);
$(‘#movie-data’).append(‘<li>Name: ‘ + row.$17 + ‘</li>’);
$(‘#movie-data’).append(‘<li>Company’ + row.CompanyName + ‘</li>’);
$(‘#movie-data’).append(‘<li>UNID : ‘ + row[“@unid”] + ‘</li>’);
$(‘#movie-data’).listview(‘refresh’);
}

});
});

var movieInfo = {
id: null,
result: null
}

$(document).on(‘vclick’, ‘#movie-list li a’, function () {
movieInfo.id = $(this).attr(‘data-id’);
$.mobile.changePage(“#headline”, {
transition: “slide”,
changeHash: false
});
});

var ajax = {
handleData: function (result) {
movieInfo.result = result;
$.each(result, function (i, row) {
$(‘#movie-list’).append(‘<li><a href=”” data-id=”‘ + result[i][“@position”] + ‘”><img class=”yourProductSprite yourProductSprite-NoPhotoPerson32″ + src=”https://greenhouse.lotus.com/plugins/plugincatalog.nsf/NoPhotoPerson.gif”/><h3>&#8217; + result[i].$16 + ‘</h3><p>’ + result[i].CompanyName + ‘ / ‘ + result[i].$12 + ‘</p></a></li>’);
});
$(‘#movie-list’).listview(‘refresh’);
}
}]]></xp:this.value>
</xp:scriptBlock>

<div data-role=”page” id=”home”>
<div data-theme=”a” data-role=”header”>
<h3>
Pretenders Directory
</h3>
</div>
<div data-role=”content”>
<div class=”example-wrapper” >
<ul data-role=”listview” id=”movie-list” data-theme=”a”>

</ul>
</div>
</div>
<div data-theme=”a” data-role=”footer”>
<h1>Fake Names – Codestore</h1>
</div>
</div><!– /page –>

<div data-role=”page” id=”headline”>
<div data-theme=”a” data-role=”header”>
<a href=”#home” class=”ui-btn-left” data-transition=”slide” data-direction=”reverse”>Back</a>
<h3>Person details</h3>
</div>
<div data-role=”content”>
<ul data-role=”listview” id=”movie-data” data-theme=”a”></ul>
</div>
</div><!– /details –>
</xp:view>

jQCloud gone bad (jQuery Tag Cloud plugin in XPages)

I had the idea to make a custom control to make the display of a categorized view more attractive. Quickly you come to tag clouds. While the extension library has an easy to use tagcloud control you probably want to have a result that looks more like this:

jQCloud is a JQuery based plugin that does a bit of the trick for you. Unfortunately it seems the link option can not handle question mark parameters so an .xsp?filter= link does not work. This makes the plugin little of interest. Nevertheless here is my progress so far:

<?xml version=”1.0″ encoding=”UTF-8″?>
<xp:view xmlns:xp=”http://www.ibm.com/xsp/core”&gt;
<xp:this.beforePageLoad><![CDATA[#{javascript:var v:NotesView = database.getView(“country”);
v.setAutoUpdate(false);
var nav:NotesViewNavigator = v.createViewNav();
var entry:NotesViewEntry = nav.getFirst();
var data:String = “”;
while (entry != null && !entry.isTotal()) {
if (entry.isCategory()){
var cat = entry.getColumnValues().firstElement().toString();
var num = entry.getChildCount().toFixed();
//var link = “, link: ‘./jQueryCloud.nsf/country.xsp?cat=” + cat + “‘”;
var link = “, link: ‘./CountryFiltered.xsp'”
var data = data + “{text: ‘” + cat + “‘, weight: ” + num + link +”},”;
}
var tmpentry:NotesViewEntry = nav.getNext();
entry.recycle();
entry = tmpentry;
}
viewScope.put(“categories”,@LeftBack(data,”,”));

}]]></xp:this.beforePageLoad>
<head>
<title>jQCloud Example</title>
<link rel=”stylesheet” type=”text/css” href=”jqcloud.css” />
<script type=”text/javascript” src=”http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js”></script&gt;
<script type=”text/javascript” src=”jqcloud-1.0.3.js”></script>
<script type=”text/javascript”>
/*!
* Create an array of word objects, each representing a word in the cloud
*/
var word_array = [
{text: “Lorem”, weight: 150},
{text: “Ipsum”, weight: 90, link: “http://jquery.com/&#8221;},
{text: “Dolor”, weight: 60, html: {title: “I can haz any html attribute”}},
{text: “Sit”, weight: 7},
{text: “Amet”, weight: 5}
// …as many words as you want
]

</script>
</head>
<div id=”example” style=”width: 550px; height: 350px;”></div>
<xp:eventHandler event=”onClientLoad” submit=”false”>
<xp:this.script><![CDATA[var viewData = “”;
var viewData = viewData + “#{javascript:@Text(viewScope.get(“categories”).toString())}”;

var myStr = “var word_array = [” + viewData + “];”;

var p = eval(myStr);

$(function() {
// When DOM is ready, select the container element and call the jQCloud method, passing the array of words as the first argument.
$(“#example”).jQCloud(word_array);
});]]></xp:this.script>
</xp:eventHandler></xp:view>

Job Wanted

Looking for a creative brain? Choose me!

job-wanted

jQuery Mobile in XPages- Panel Widget

jQuery Mobile 1.3 is recently released. This release is focused on elevating responsive web design (RWD) and brings lots of cool new widgets including panels, dual handle range sliders, and two different responsive table modes.

Panel widget

One of the most common mobile UI patterns you see today are Facebook-style panels that open to reveal a menu, form or other content.

The flexible panel widget  allows panels to be positioned on the left or right of the screen. To take this widget responsive, it’s easy to add media queries to make the panel stay open at wider widths instead of auto-closing when you click on the page content. A great demo can be found here.

Currently I am working on a sort of development template that will allow to quickly build an XPages app using the panel widget.

I noticed however some steps you need to take to prepare your XPage application:

  • xsp.html.doctype=html in the xsp properties file sets your doctype.
  • xsp.theme=<empty> in the xsp properties files disables server default theming.
  • createForm=”false” in the XPage will not create the form element. This is important otherwise your relative links will not work.

Below is a sample code you can copy & paste in your XPage to get started:

<?xml version=”1.0″ encoding=”UTF-8″?>
<xp:view xmlns:xp=”http://www.ibm.com/xsp/core&#8221; createForm=”false”>
<head>
<meta charset=”utf-8″ />
<meta name=”viewport”
content=”width=device-width, initial-scale=1″ />
<title>Panel fixed positioning – jQuery Mobile Demos</title>
<link rel=”stylesheet”
href=”http://view.jquerymobile.com/1.3.0/css/themes/default/jquery.mobile.css&#8221; />
<link rel=”stylesheet”
href=”http://view.jquerymobile.com/1.3.0/docs/_assets/css/jqm-demos.css&#8221; />
<link rel=”shortcut icon”
href=”http://view.jquerymobile.com/1.3.0/docs/favicon.ico&#8221; />
<link rel=”stylesheet”
href=”http://fonts.googleapis.com/css?family=Open+Sans:300,400,700&#8243; />
<script src=”http://view.jquerymobile.com/1.3.0/js/jquery.js&#8221; />
<script
src=”http://view.jquerymobile.com/1.3.0/docs/_assets/js/&#8221; />
<script src=”http://view.jquerymobile.com/1.3.0/js/&#8221; />
<style>
.nav-search .ui-btn-up-a { background-image:none;
background-color:#333333; } .nav-search .ui-btn-inner {
border-top: 1px solid #888; border-color: rgba(255, 255,
255, .1); } .nav-search .ui-btn.ui-first-child {
border-top-width: 0; background: #111; } .userform {
padding: .8em 1.2em; } .userform h2 { color: #555; margin:
0.3em 0 .8em 0; padding-bottom: .5em; border-bottom: 1px
solid rgba(0,0,0,.1); } .userform label { display: block;
margin-top: 1.2em; } .switch .ui-slider-switch { width:
6.5em !important; } .ui-grid-a { margin-top: 1em;
padding-top: .8em; margin-top: 1.4em; border-top: 1px solid
rgba(0,0,0,.1); }
</style>
</head>
<body>
<div data-role=”page” class=”jqm-demos ui-responsive-panel”
id=”panel-fixed-page1″>
<div data-role=”header” data-theme=”f”
data-position=”fixed”>
<h1>Fixed header</h1>
<a href=”#nav-panel” data-icon=”bars”
data-iconpos=”notext”>
Menu
</a>
<a href=”#add-form” data-icon=”plus”
data-iconpos=”notext”>
Add
</a>
</div><!– /header –>

<div data-role=”content” class=”jqm-content”>
<h1>Panel</h1>
<h2>Fixed positioning</h2>
<p>
This is a typical page that has two buttons in the
header bar that open panels. The left button opens a
left menu with the reveal display mode. The right
button opens a form in a right overlay panel. We
also set position fixed for the header and footer on
this page.
</p>
<p>
The left panel contains a long menu to demonstrate
that the framework will check the panel contents
height and unfixes the panel so its content can be
scrolled.
</p>
<h2>Responsive</h2>
<p>
To make this responsive, the panel stays open and
causes the page to re-flow at wider widths. This
allows both the menu and page to be used together
when more space is available. This behavior is
controlled by CSS media queries. You can create a
custom one for a specific breakpoint or use the
breakpoint preset by adding the
<code>class=”ui-responsive-panel”</code>
to the page container. We have added this class on
this demo page.
</p>

<a href=”./” class=”jqm-button” data-ajax=”false”
data-role=”button” data-mini=”true” data-inline=”true”
data-icon=”arrow-l” data-iconpos=”left”>
Back to Panels
</a>

<div data-demo-html=”#panel-fixed-page1″
data-demo-css=”true” />
<!–/demo-html –>

</div><!– /content –>

<div data-role=”footer” data-position=”fixed”
data-theme=”f”>
<h4>Fixed footer</h4>
</div><!– /footer –>

<div data-role=”panel” data-position-fixed=”true”
data-theme=”a” id=”nav-panel”>
<ul data-role=”listview” data-theme=”a”
class=”nav-search”>
<li data-icon=”delete”>
<a href=”#” data-rel=”close”>Close menu</a>
</li>
<li>
<a href=”#panel-fixed-page2″>Accessibility</a>
</li>
<li>
<a href=”#panel-fixed-page2″>Accordions</a>
</li>
</ul>
</div><!– /panel –>

<div data-role=”panel” data-position=”right”
data-position-fixed=”true” data-display=”overlay” data-theme=”b”
id=”add-form”>
<form class=”userform”>
<h2>Create new user</h2>
<label for=”name”>Name</label>
<input type=”text” name=”name” id=”name” value=””
data-clear-btn=”true” data-mini=”true” />
<label for=”email”>Email</label>
<input type=”email” name=”email” id=”status”
value=”” data-clear-btn=”true” data-mini=”true” />
<label for=”password”>Password:</label>
<input type=”password” name=”password” id=”password”
value=”” data-clear-btn=”true” autocomplete=”off”
data-mini=”true” />
<div class=”switch”>
<label for=”status”>Status</label>
<select name=”status” id=”slider”
data-role=”slider” data-mini=”true”>
<option value=”off”>Inactive</option>
<option value=”on”>Active</option>
</select>
</div>
<div class=”ui-grid-a”>
<div class=”ui-block-a”>
<a href=”#” data-rel=”close”
data-role=”button” data-theme=”c” data-mini=”true”>
Cancel
</a>
</div>
<div class=”ui-block-b”>
<a href=”#” data-rel=”close”
data-role=”button” data-theme=”b” data-mini=”true”>
Save
</a>
</div>
</div>
</form>

</div><!– /panel –>

</div><!– /page –>

<div data-role=”page” id=”panel-fixed-page2″>

<div data-role=”header” data-theme=”f”>
<h1>Landing page</h1>
</div><!– /header –>
<div data-role=”content” class=”jqm-content”>
<p>This is just a landing page.</p>
<a href=”#panel-fixed-page1″ data-role=”button”
data-inline=”true” data-mini=”true” data-icon=”back”
data-iconpos=”left”>
Back
</a>
</div><!– /content –>
</div><!– /page –>
</body>
</xp:view>