Display JSON pretty in a textarea control

In an application I am using heavily JSON default as the data-format. To make debugging a bit more easy I am working on a toolbar to be able to check the content of the JSON objects.

Normally I use JSONLint most of the time to prettify (and validate) the JSON so it becomes a bit easier to read and check. But this copy and paste habit becomes more and more obnoxious so there I wanted to have a toolbar (like the debug toolbar) to have a good overview of the JSON objects.

However if you write the JSON object to a computed field or text area field (inputTextarea control in the XPages world) it is just plain, unformatted text. Not pretty for the eye and not much helpful for the brain.

So I tested a simple approach which turns out to run just fine. I display the json object in a textarea input control and then with CSJS I format the content. Here is some sample code:

<xp:inputTextarea id=”input-fieldx” defaultValue=”#{javascript:App.currCollection}”>
</xp:inputTextarea>
<xp:button value=”Make Pretty” id=”button1″>
<xp:eventHandler event=”onclick” submit=”false”>
<xp:this.script>
<![CDATA[var name = x$(‘#{id:input-fieldx}’).attr(“name”);
prettyPrint(name);]]>
</xp:this.script>
</xp:eventHandler>
</xp:button>
<xp:scriptBlock id=”scriptBlock1″>
<xp:this.value>
<![CDATA[function prettyPrint(id) {
var obj = dojo.byId(id);
var ugly = obj.value;
var json = dojo.fromJson(ugly);
var pretty = JSON.stringify(json, undefined, 4);
obj.innerHTML = pretty;
dojo.setAttr(id,”rows”,10);
}]]>
</xp:this.value>
</xp:scriptBlock>

Here is what it looks like:

json-before

The content how it looks initially.

json-after

The content how it looks like after running the code.

Note I had to include the [ ] signs in my code to make the JSON valid due to circumstances in my code, I removed it from the sample code above.

At least now I understand my JSON objects again and I can avoid some copy and paste to JSONLint 🙂

This is probably my last blog of this year so I would like to wish you some wonderful Christmas days. Take care and best wishes for a healthy 2017!

 

 

Update on my Cloudant sample XPages application

I made some additions to my XPages sample app that I am using to understand and demonstrate the usage of IBM Cloudant within XPages.

I am not fully there yet where I want to be, but it is a work in progress. During development I noticed a couple of things:

  • Cloudant’s Lite Pricing Plan has its limitations 🙂
  • Migrating Notes documents to Cloudant goes fast enough. A set of 10 thousands of simple, small documents takes just a couple of seconds.
  • Loading the same amount of documents into XPages e.g. a Repeat Control and navigate between sets via a Pager control has some delay time. This could be due the limitations of my local application server.

The code is available on Github.

cloudant_progress

MapReduce views

Introduction

Frank van der Linden posted he made an update to his Cloudant connector plugin, so from now it should be possible to manage design documents.

To become more familiar with couchDB’s design documents I can recommend reading ‘Writing and Querying MapReduce Views in CouchDB‘.

Design documents

Design documents you mostly manage in CouchDB / Cloudant are documents that describe views of documents. Great if you can manage them via http and not as in IBM Notes via the Domino Designer client 😉

You could store other documents in your couchDb which are part of the design of your application (e.g. html, css, js files but that is another story).

Design documents are nothing more than JSON documents which are stored under the convention: must start with the _design/ pattern followed by the identifier e.g.: _design/default.

MapReduce

Views in couchdb are based on the MapReduce principle:

A MapReduce program is composed of a Map() procedure that performs filtering and sorting (such as sorting students by first name into queues, one queue for each name) and a Reduce() procedure that performs a summary operation (such as counting the number of students in each queue, yielding name frequencies).

This video explains it by using playing cards.

Mapping

The filtering and sorting are defined by a JavaScript function. You can state conditions in the function to return the documents you are interested in e.g.:

function(doc) {
if (doc.labels) {
for (var idx in doc.labels) {
if (doc.labels[idx] === “organic”) {
emit(doc._id, doc.name);
}
}
}
}

//docs have to have the field doc.labels and only the id and name from the ones that have the label organic are returned

Reducing

Reducing is optional and results in a summary operation. The options in couchDb are:

  • _sum
  • _count
  • _stats

Count is probably the most common function to use. Notice that the returned key value might be null if values appear in multiple documents. The key is no longer unique and therefor null.

{
“rows”: [{
“key”: null,
“value”: 7
}]
}

If you apply the group parameter in the requested url the values will be grouped so the response could look like:

{
“rows”: [{
“key”: “appel”,
“value”: 3
}, {
“key”: “banaan”,
“value”: 2
}, {
“key”: “peer”,
“value”: 2
}
]
}

Sum works only on numeric values (of course) so if your mapping functions looks like:

function(doc) {
if (doc.fruits) {
for (var i in doc.fruits) {
emit(doc.fruits[i], doc.calories);
}
}
}

with the _sum reduce option the response could look like:

{
“rows”: [{
“key”: “appel”,
“value”: 720
}, {
“key”: “banaan”,
“value”: 1368
}, {
“key”: “peer”,
“value”: 320
}]
}

The _stats option returns a JSON object containing the sum, count, minimum, maximum, and sum over all square roots of mapped values. So for the mapping function above the response could be:

{
“rows”: [{
“key”: “appel”,
“value”: {
“sum”: 720,
“count”: 2,
“min”: 272,
“max”: 448,
“sumsqr”: 274688
}
}, {
“key”: “banaan”,
“value”: {
“sum”: 1368,
“count”: 3,
“min”: 272,
“max”: 648,
“sumsqr”: 694592
}
}, {
“key”: “peer”,
“value”: {
“sum”: 720,
“count”: 2,
“min”: 272,
“max”: 448,
“sumsqr”: 274688
}
}]
}

With this knowledge fresh in mind you can use them in your view(s) setup. Remember the design documents can be managed via CRUD operations, e.g. on address http://localhost:5984/veganstore/_design/default

{
“_id”: “_design/default”,
“language”: “javascript”,
“views”: {
“store”: {
“map”: “function(doc) { if (doc.label) { emit(doc.label, doc.origin); }}”,
“reduce”: “_stats”
}
}
}

In one design document you can define multiple views…

You can query your views by a set of parameters:

  • conflicts (boolean) – Includes conflicts information in response. Ignored if include_docs isn’t true. Default is false
  • descending (boolean) – Return the documents in descending by key order. Default is false
  • endkey (json) – Stop returning records when the specified key is reached. Optional
  • end_key (json) – Alias for endkey param
  • endkey_docid (string) – Stop returning records when the specified document ID is reached. Requires endkey to be specified for this to have any effect. Optional
  • end_key_doc_id (string) – Alias for endkey_docid param
  • group (boolean) – Group the results using the reduce function to a group or single row. Default is false
  • group_level (number) – Specify the group level to be used. Optional
  • include_docs (boolean) – Include the associated document with each row. Default is false.
  • attachments (boolean) – Include the Base64-encoded content of attachments in the documents that are included if include_docs is true. Ignored if include_docs isn’t true. Default is false.
  • att_encoding_info (boolean) – Include encoding information in attachment stubs if include_docs is true and the particular attachment is compressed. Ignored if include_docs isn’t true. Default isfalse.
  • inclusive_end (boolean) – Specifies whether the specified end key should be included in the result. Default is true
  • key (json) – Return only documents that match the specified key. Optional
  • keys (json-array) – Return only documents where the key matches one of the keys specified in the array. Optional
  • limit (number) – Limit the number of the returned documents to the specified number. Optional
  • reduce (boolean) – Use the reduction function. Default is true
  • skip (number) – Skip this number of records before starting to return the results. Default is 0
  • sorted (boolean) – Sort returned rows (see Sorting Returned Rows). Setting this to false offers a performance boost. The total_rows and offset fields are not available when this is set to false. Default is true
  • stale (string) – Allow the results from a stale view to be used. Supported values: ok and update_after. Optional
  • startkey (json) – Return records starting with the specified key. Optional
  • start_key (json) – Alias for startkey param
  • startkey_docid (string) – Return records starting with the specified document ID. Requires startkey to be specified for this to have any effect. Optional
  • start_key_doc_id (string) – Alias for startkey_docid param
  • update_seq (boolean) – Response includes an update_seq value indicating which sequence id of the database the view reflects. Default is false

With this summary on views in couchDBcloudant fresh in mind let’s try to create some views programmatically in Cloudant with Frank’s updated plugin =)

Build JSON from NotesView, support for multiple value fields

The problem

An old-fashioned NotesView can be a fast way to define JSON objects. Here is a xsnippet that give you some inpsiration.

If you have fields with multiple values then the formula does not work anymore. You want to store an array in the JSON.

The fix

The following formula checks if the desired output fields contains more than 1 value  and if so transforms it to an array if not, then you get a normal key:value pair returned.

_fld:=”refName”:”refSub”:”refStrength”:”refWeakness”;

json:=””;

@For(n :=1; n<=@Elements(_fld); n:= n + 1;

@If(
@Elements(
@GetField(_fld[n])
)>1;
json := json + @Transform ( _fld[n]; “_fn” ; “\”” + _fn + “\”:”) + “[” + @Implode(@Transform( @GetField(_fld[n]) ; “_fn” ; “\”” + _fn + “\”” ) ; “,”) + “],” ;
json := json +
@Transform (
_fld[n]; “_fn” ; “\”” + _fn + “\”:\”” + @Text ( @GetField ( _fn) ) + “\”” + “,”)
)
);
“{ \”unid\”:\””
+@Text(@DocumentUniqueID)+”\”,” + @LeftBack(json;1) + + “}”

Test

I added my formula to a column and calling several single and multiple text fields. Here is my output in a browser:

notesview-as-json

If I validate the json it says it is valid:

valid-json

I added my code to the XSnippets database.

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 !

Building a search function with DataTables plugin

Introduction

In Domino you have multiple options how to provide a search function for an application. I have seen many examples where a FT search query is build for a database, where the form type is defined, which fields should be used in the search etcetera. Most of them are a maintenaince nightmare, layout is little flexible and usability is lacking (e.g. perform a search towards a query which will result in zero hits).

What if you make it easier to maintain, more flexible for user specific desires and add facetted search principles? A win situation?

DataTables

In the following posts I will write how to use the jQuery DataTables plugin to provide a quick search function for your Domino apps. I assume you use XPages.

Stage 1 – Getting & presenting the data

In the initial stage we just try to get and present the data we need. As example I will be using the FakeNames application. I have created a public repo which has at the moment:

  • an xpage to present the data (dtPersons.xsp)
  • an xpage (api.xsp) which will contain rest services
  • a Java class that generates the data in JSON format
  • a CSJS library to transform the HTML table on dtPersons.xsp to  DataTables object
  • the required resources from DataTables.net
  • a Theme resource to have the resources available
  • a Notes view to use as data source.

In order to create some fake documents I have set up an LS agent to do so.

If you bind this all together, you have already a search function:

datatables01

Right on top you have a search field where you can query the columns in the table. The columns are sortable too. Probably it is matching most of the search functions you have seen for Domino. But wait! There is more possible…

 

Working with JSON in your XPages application – Refined view

In a previous post I wrote how you could use a Notes view as the data source for JSONObjects. In a recent project I needed to minimize the data amount to transfer so I had to exclude the fields that contain no value. Here is the @formula I can up with for the column value:

REM { Patrick Kwinten

Quick way to deliver ‘cached’ JSON objects from Notes documents

};

REM {JSONObject bricks};

jsonOpener := “{“;

jsonSeparator := “,”;

jsonClosure := “}”;

REM {String helper};

src := “\\”:”‘”:@NewLine:”\””;

dst :=  “\\\\”:”\\'”:”\\n”:”\\\””;

REM {Define which fields to exclude/include in JSON object};

REM {“Idea taken from http://www.eknori.de/2012-06-01/use-transform-to-build-json-and-consume-the-output-in-an-xagent/&#8221;};

_exclude:=”$FILE”:”$Fonts”:”form”:”$Revisions”:”ID”:”ModifiedBy”:”$TUA”;

_fld:=@Trim(@ReplaceSubstring(@DocFields;_exclude;@Nothing));

REM {Output for “custom” fields. Empty values are excluded};

_fldOutput := “”;

@For(

n := 1;

n <= @Elements(_fld);

n := n + 1;

@Do(

_fldVal := @Text ( @GetField ( _fld[n] ) );

@If(_fldVal != “” ; _fldOutput := _fldOutput + “\”” + _fld[n] + “\”:\”” + @ReplaceSubstring( _fldVal; Src; Dst )  + “\”” + @If(n != @Elements(_fld); jsonSeparator;””); “”)

)

);

REM {Prepare output};

outputStr := “”;

outputStr := outputStr + jsonOpener;

REM {Output for “default” fields};

tmpUNID:= @Text(@DocumentUniqueID);

outputStr := outputStr + “\”docUNID\”:\”” + tmpUNID  + “\”” + jsonSeparator;

outputStr := outputStr + _fldOutput;

REM {Finalize JSONObject};

outputStr := outputStr + jsonClosure;

REM {Return output};

@Return(outputStr)

As a result I have “lesser” JSON Objects in my view:

Screen Shot 2016-03-01 at 10.20.40

Bildr 5 released on OpenNTF

Announcement

Today I released a new version of Bildr on OpenNTF ! There have been spent quiet a few long evenings coding but it was fun (I may drink at home) and interesting!

So just before the holiday season and January’s  IBM Connect buzz I thought it would be a proper moment for a release. So consider it my Xmas present to the community 🙂

Modernization

I label this version 5.0 since I (almost) completely rewrote the application from scratch. Lot of the logic I have placed in Java classes and the data format is mainly JSON.

I also separated the design from the data so in principle you should be able to place the app in the IBM Bluemix environment.

Because of these decisions there is no backward compatibility so a simple replace design will not work for your existing installation.

Below you can find some screenshots of the new interface. I hope you like it.

Screenshots

startpage

uploadprofilepicture

Thank you

I would like to thank all the teachers in our community who post their presentations, code samples and answers on the platforms out there.

I have tried to post the code on Github but this failed with the Github desktop client. If some can get me started with that I will distribute the project there too.

For now:

A Merry Christmas and fortune in 2016 !

Carousel with dynamic content

Introduction

For an application I needed a carousel to display images on the frontpage. Since we go “all-in” Bootstrap the “natural choice” would be the Carousel component from the Extension Library. Unfortunately that control currently only accepts “static” slideNodes so I had to come up with something similar.

In the beginning…

First I just took the sample code from W3schools how to setup a basic Bootstrap Carousel component.

car_code01

Second I set up a Notes view that contains the data I need for my carousel. This view will be the source for an XPages Rest Service control (PDF alert) which I call after page load with an Ajax call using jQuery’s getJSON.

car_code02

car_code03

As you can see I update the Bootstrap sample code with the received result(s).

As a result I get the Carousel component from Bootstrap but now with ‘dynamic content’.

carousel

(for those who have never seen a Carousel)

The only complication I experienced was due to the fact that I separate the design from the data and accessing the data is easier in SSJS/Java then CSJS.

Here is the code: Link Dropbox

Happy development =)

Working with JSON in your XPages application (3) – getEntriesByKey

Introduction

In our third example we will extend the loadPictures method in the first blog entry in this serie to detect whether or not we want to load just all the entries in a view or entries that match a key in the first sorted column.

Use case

In the following simple scenario a user makes an selection (e.g. from a combobox) and corresponding picture documents will be displayed, as in the image:

sample3json

Whenever a different value is selected the list with corresponding documents will be updated.

So how do we do this?

Switch between Notes views

The main difference is the key send if we do or don’t (empty value) want a filtering of the result list. To collect this key we provide a combobox:

</div>

The loadCategories method provides an ArrayList of String values:

public ArrayList<String> loadCategories(String ViewName) throws NotesException{
System.out.println(appRef + “.loadCategories()”);

NotesContext nct = NotesContext.getCurrent();
Session session = nct.getCurrentSession();

String ServerName = “dev1”;
String DatabaseName = session.getCurrentDatabase().getFilePath();
Database DB = session.getDatabase(ServerName, DatabaseName);

View luView = DB.getView(ViewName);
ViewNavigator vwnav = null;
ViewEntry vwentry = null;
ViewEntry vwentrytmp = null;

// all results will be added to this
ArrayList<String> categories = new ArrayList<String>();

// disable autoupdate
luView.setAutoUpdate(false);

vwnav = luView.createViewNav();

// setting buffer for fast view retrieval
vwnav.setBufferMaxEntries(400);

// perform lookup
vwentry = vwnav.getFirst();
while (vwentry != null){

if(!categories.contains(vwentry.getColumnValues().elementAt(0).toString()))
categories.add(vwentry.getColumnValues().elementAt(0).toString());
// Get entry and go recycle
vwentrytmp = vwnav.getNext(vwentry);
vwentry.recycle();
vwentry = vwentrytmp;
}
luView.setAutoUpdate(true);

return categories;
}

The xp:Combobox control is bound to a viewScope variable and it’s onChange event triggers a partial refresh on a panel:

<xp:panel id=”pictures”>
<xp:pager layout=”Previous Group Next” partialRefresh=”true” id=”pager1″ for=”repeat1″></xp:pager>
<table class=”table table-hover”>
<thead>
<tr>
<th>Thumb</th>
<th>Subject</th>
<th>Category</th>
<th>Description</th>
<th>Author</th>
</tr>
</thead>
<xp:repeat id=”repeat1″ rows=”15″ var=”pix”>

<xp:this.value><![CDATA[#{javascript:var active = viewScope.get(“activeCategory”);
if (active == “All”){
PictureProvider.loadPictures(“”);
}
else{
PictureProvider.loadPictures(active);
}}]]></xp:this.value>
<tr>
<td>
<xp:text escape=”false”>
<xp:this.value><![CDATA[#{javascript:/*
getAttachmentURL and getBaseURL from:
http://www.wissel.net/blog/d6plinks/SHWL-86QKNM
*/

function getAttachmentURL(docID:java.lang.String, attachmentName:java.lang.String) {
var base = getBaseURL();
var middle = “/xsp/.ibmmodres/domino/OpenAttachment”;
if (base.substr(0,4) == “/xsp”) {
middle += base.substr(4);
} else {
middle += base;
}
var result = base + middle + “/” + docID + “/$File/” + attachmentName + “?Open”;
return result;
}

function getBaseURL() {
var curURL = context.getUrl();
var curAdr = curURL.getAddress();
var rel = curURL.getSiteRelativeAddress(context);
var step1 = curAdr.substr(0,curAdr.indexOf(rel));

// Now cut off the http
var step2 = step1.substr(step1.indexOf(“//”)+2);
var result = step2.substr(step2.indexOf(“/”));
return result;
}
var thumb = pix.imgthumb;
var id = pix.docUNID;

return “<img src='” + getAttachmentURL(id, thumb) + “‘>”;
}]]></xp:this.value>
</xp:text>
</td>
<td><xp:text escape=”true” value=”#{pix.subject}”></xp:text></td>
<td><xp:text escape=”true” value=”#{pix.category}”></xp:text></td>
<td><xp:text escape=”true” value=”#{pix.descr}”></xp:text></td>
<td><xp:text escape=”true” value=”#{pix.author}”></xp:text>
</td>
</tr><!– /.row –>
</xp:repeat>

</table>
</xp:panel>

Depending on the value in the viewScope we call the loadPictures method empty or with a key.

private ArrayList<JsonJavaObject> loadJSONObjects(String ServerName, String DatabaseName, String ViewName, String Key, Integer ColIdx) throws NotesException {
ArrayList<JsonJavaObject> JSONObjects = new ArrayList<JsonJavaObject>();

NotesContext nct = NotesContext.getCurrent();
Session session = nct.getCurrentSession();
Database DB = session.getDatabase(ServerName, DatabaseName);

if (!(DB==null)) {
View luView = DB.getView(ViewName);

if (!(luView == null)) {
JsonJavaFactory factory = JsonJavaFactory.instanceEx;

ViewEntryCollection vec = luView.getAllEntriesByKey(Key, true);

ViewEntry entry = vec.getFirstEntry();
while (entry != null) {

Vector<?> columnValues = entry.getColumnValues();
String colJson = String.valueOf(columnValues.get(ColIdx));
JsonJavaObject json = null;

try {
json = (JsonJavaObject) JsonParser.fromJson(factory, colJson);
if (json != null) {
JSONObjects.add(json);
}

} catch (JsonException e) {
// TODO:
}

ViewEntry tempEntry = entry;
entry = vec.getNextEntry();
tempEntry.recycle();
}
luView.recycle();
}
DB.recycle();
}
return JSONObjects;
}

Similar as in the previous examples the JsonJavaObject class from the com.ibm.commons.util.io.json package is used to return an arraylist of json objects.

In the next post I will describe how we can manipulate the JSON objects.

Working with JSON in your XPages application (2) – getEntryByKey

Introduction

In the previous post we started with the first steps to modernize our xpages application by defining our Notes view data in JSON format, create objects from them in Java with help from  com.ibm.commons.util.io.json and return them to the xpage (repeat control).

In our example we used the AllEntries method which is well known across the languages Domino supports (LotusScript/JavaScript/Java).

getEntryByKey

As Domino also supports other methods to work with Notes view data we have to extend our Java class with them. Next in line lies the getEntryByKey method.

Changes in setup

Removed Content Delivery Network

In the previous post we used CDN for loading Bootstrap. Since we work with XPages and the latest and greatest Extension Library at our service we simple apply the Bootstrap theme for our application.

New Notes view

In our *NonSQL* database we will add a new view with the first column categorized. I have chosen the following document properties:

varUNID:=@Text(@DocumentUniqueID);
varAuthor:=Au_Author;
varCategory:= Photo_Category;
varCreated:=@Text(@Date(@Created));
@Return(Photo_Title:varUNID:varAuthor:varCategory:varCreated)

I also extended the second column compared with the first view. The column formula is as followed:

REM {This column builds a JSON string};

varUNID := @Text(@DocumentUniqueID);
varCategory:= Photo_Category;
varTitle := Photo_Title;
varAuthor:=Au_Author;
varDesc:=PhotoDescription;
varCreated:=@Created;
varThumb:=Photo_ThumbFilename;
varSmall:=Photo_SmallFilename;

jsonOpener := “{“;
jsonClosure := “}”;
jsonSeparator := “\”,”;
jsonLastItem := “\””;

@Return(
jsonOpener +
“\”docUNID\”: \”” + varUNID + jsonSeparator +
“\”author\”: \”” + @Name([CN]; varAuthor) + jsonSeparator +
“\”category\”: \”” + varCategory + jsonSeparator +
“\”subject\”: \”” + varTitle + jsonSeparator +
“\”descr\”: \”” + varDesc + jsonSeparator +
“\”created\”: \”” + @Text(@Date(varCreated)) + jsonSeparator +
“\”imgthumb\”: \”” + varThumb + jsonSeparator +
“\”imgsmall\”: \”” + varSmall + jsonLastItem +
jsonClosure
)

Notice I added an additional key:pair for a smaller (preview) image. I also find the @Return function a good best practice to explicit indicate something is being returned.

With this new view available we will update our Java class.

Java class additions

Our Java class will have two new functions:

  • loadPicture(Key)
  • loadJSONObject

The first indicates we will send along a unique identifier and the second indicates we will return a single JSON object.

public JsonJavaObject loadPicture(String Key) throws NotesException {
JsonJavaObject json = null;

NotesContext nct = NotesContext.getCurrent();
Session session = nct.getCurrentSession();

String ServerName = “dev1”;
String DatabaseName = session.getCurrentDatabase().getFilePath();
String ViewName = “$v-pixJSONCategories”;

try {
json = loadJSONObject(ServerName, DatabaseName, ViewName, Key, 1);
} catch (NotesException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return json;
}

and

private JsonJavaObject loadJSONObject(String ServerName, String DatabaseName, String ViewName, String Key, Integer ColIdx) throws NotesException {

JsonJavaObject json = null;

NotesContext nct = NotesContext.getCurrent();
Session session = nct.getCurrentSession();
Database DB = session.getDatabase(ServerName, DatabaseName);

if (!(DB==null)) {

if (!(DB.isOpen())) {
DB.open();
}

View luView = DB.getView(ViewName);
if (!(luView == null)) {

ViewEntry entry = luView.getEntryByKey(Key);
if (!(entry == null)) {

Vector<?> columnValues = entry.getColumnValues();
String colJson = String.valueOf(columnValues.get(ColIdx));

try {
JsonJavaFactory factory = JsonJavaFactory.instanceEx;
json = (JsonJavaObject) JsonParser.fromJson(factory, colJson);
} catch (JsonException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

entry.recycle();
}
luView.recycle();
}
DB.recycle();
}
return json;
}

Probably during development (and acceptance) you would like include a logging mechanism. I have left the System.out.println statements out of the examples…

A new XPage

Since we will call the loadPicture method with a key we need to provide a mechanism to capture this key. In our first example we capture the key from the unid parameter that we attach to the URL e.g. page2.xsp?unid=1234. Notice that the unid has to respond to a category value set in the category column in the Notes view. So my proposal is to use categories that are sufficient distinguished from each other.

In my xpage I drag in a panel and define a dataContext for it:

<xp:this.dataContexts>
<xp:dataContext var=”data”>
<xp:this.value><![CDATA[#{javascript:var paramUNID = paramValues.get(“unid”);
strUNID = paramUNID.toString();
PictureProvider.loadPicture(strUNID);}]]></xp:this.value>
</xp:dataContext>
</xp:this.dataContexts>

Notice I am using the paramValues global object available in XPages to capture the key. With this key I call the loadPicture method.

Within the panel I define a Bootstrap form:

https://www.dropbox.com/s/gxfofuu6ujec2qt/bootstrap%20form.txt?dl=0

(for some reasons WordPress want to transform my text into HTML)

Data is the variable I have defined for the dataContext. Hereby I can easily access the JSON object properties and collect a value by accessing a key e.g. data.docUNID.

With these new additions our result looks and followed:

isn't she lovely?