Notes on Mobile
Back on track

Hairshirt or not…. the experiment rolls forward!

Now that I got the JSON bug solved worked around I can get back to fleshing out the non-XPages version of the Customers On the GO! app.

The next page is the contact details. The XPages version of this page wasn’t too complicated and I wouldn’t normally dedicate an entire post about it, but this will give me a chance to walk through my Notes document as JSON function I talked about yesterday.

After clicking the ‘Contacts’ link at the bottom of the app, you are sent to the ‘contactsList.html’ page. The page itself is almost identical to the app’s home screen. The content for the page is collected using the JSON formatted output of a view in the CRM applciation using the following JS:

function loadContactsPageData(){
	var cat = getURLParameter("c");
	var parentId = ('00000000'+getURLParameter("documentId")).slice(-8);
	// Get the list data.
	
    $.getJSON('http://10.1.1.39/unplugged/tdtool.nsf/dataProxy?OpenAgent&db=unplugged/rm.nsf&v=JSONContacts&RestrictToCategory='+cat,
        
    	function(data) {
    		var contactArray = convertViewEntryToJSONObject(data);
    		$('#contactDetailsTemplate')                  // Select the template. 
    			.tmpl(contactArray)              // Bind it to the data. 
    			.appendTo('#contactsContainer');   // Render the output.
    		
    		$('#contactsContainer').listview('refresh');
       }
   );
}

The contact template is very basic as well and looks like this:

	<script id="contactDetailsTemplate" type="text/x-jquery-tmpl">
		<li>
		<a href="contact.html?openfileresource&documentId=${noteid}">
		{{if fpjrFunction_WB=="Developer"}}
		<img style="jobImage" src='designer.png'>
		{{else}}
		<img style="jobImage" src='admin.png'>
		{{/if}}
		<span style='contactName'>${fpnhName_WB}</span>
    	<br/><span style='contactDetails'>${fpjrPosition_WB}</span>
    	<br/><span style='contactDetails'>${fppbPhone_WB}</span>
		{{if fppbXtn_WB}}
    	<span style='contactDetails'>  Ext: ${fppbXtn_WB}</span>
		{{/if}}
		</a>
		</li>
	</script>

Not that complicated, and very similar to the first page, so it was really easy to throw together. 

Each item in the above list has a link that brings you to the ‘contact.html’ page where the individual contacts information is displayed. In the XPages version, there was nothing much to this page, and really didn’t offer any more information than the list did. Just the customers name, job role, phone number, and a big button called ‘Call’. So here is where the dataProxy agent comes into play. To get a document all I need is the following URL:
  unplugged/tdtool.nsf/dataProxy?OpenAgent&db=unplugged/crm.nsf/&outputformat=JSON&documentId=00000000

(Of course I would use the real note id, but this is an example). This URL I can use in my JQuery.getJSON call in the same way I was using it with the views.

NotesDocument.js

To make it easier to deal with the document object I create a few helper functions/objects. First was a little JS function to wrap the getJSON call for documents:

function getNotesDocument( db, docid, callback ){
	var url = 'http://10.1.1.39/unplugged/tdtool.nsf/dataProxy?OpenAgent';
	url += "&db=" + db;
	url += "&documentId=" + docid;
	url += "&outputformat=JSON";
    $.getJSON(url,function(data){
    	var doc = new NotesDocument(data);
    	callback(doc);
    });
}

This function takes 3 parameters, the db and docid as strings and the callback that the getJSON function should call. The passed in callback will be called with a NotesDocument object though instead of the direct results from the getJSON call itself. 

The NotesDocument object is simple and only has two functions, a constructor and a method called getItem. This is what it looks like:

var NotesDocument = function(val){
	this.JSON = val.document;
	this.items = {};
	// convert the array of items to named properties in this object
	// this will make accessing them a bit faster than looping through
	// the entire list looking for the item by name
	for( var i = 0; i< this.JSON.item.length; i++ ){
		for( val in this.JSON.item[i] ){
			if( val !== "name"){
				// values get put into a field named the same as their data type
				this.items[this.JSON.item[i].name] = this.JSON.item[i][val];
			}
		}
	}
	delete this.JSON.item; // no need to store the items twice
};

NotesDocument.prototype.getItem = function(item){
	if( this.items.hasOwnProperty(item) ){
		return this.items[item];
	}
	return "";
}

This little bit of code lets me access the item names in much the same way as if I was working directly with the built in NotesDocument object in Domino.

Finally the function that gets the document can be reduced down to this bit of JS:

function loadContactPage(){
		
	var cat = ('00000000'+getURLParameter("documentId")).slice(-8);
	// Get the list data.
	getNotesDocument( "unplugged/crm.nsf", getURLParameter("documentId"), 
		function(doc) {
			$('#contactTemplate')                  // Select the template. 
				.tmpl(doc)              // Bind it to the data. 
				.appendTo('#contactDetails');   // Render the output.
			
			// need to re-apply styles so the 'call' button will
			// be shown normally. For some reason the only way to 
			// get this to work with the link button is to 
			// 'refresh' the entire page.
			$('#contactDetail').trigger('create');
		}
	);
}

One of my biggest frustrations with JQuery Mobile is that it doesn’t deal with dynamic content very well. You probably noticed that there is a call to the ‘trigger()’ function above. After much trial and error, and google searching, it was the only way I could get the ‘Call’ button to render as a JQM Button widget and not a link. The reason all this is needed is that JQM loads a page and does all it’s markup tweaking before the getJSON() call returns. As a result, by the time the template code has been processed, JQM thinks it’s done with the page. To get around it you either need to add all the JQM classes and styles to the template code manually, or you need to force JQM to reprocess the page - which is what calling trigger(‘create’) does when you specify the DIV for the page.

And just like the rest of the pages, I add a event handler to call this function when the contactDetails page is loaded:

$(document).bind("mobileinit", function(){
    [...snip...]
	$('#contactDetail').live('pagebeforeshow',function(event, ui){
		loadContactPage();	
	});
    [...snip...]
}

With the Javascript ready to go all that is left is the page template that will display the data from the document. For that I’m going to use the getItem function in the NotesDocument object directly in my page template:

	<script id="contactTemplate" type="text/x-jquery-tmpl">
		<span class='contactName'>${$data.getItem("fpnhName_WB")}</span><br/>
	    <span class='contactDetails'>${$data.getItem("fpjrPosition_WB")}</span><br/>
		<span class='contactDetails'>${$data.getItem("fppbPhone_WB")}</span>
		{{if $data.getItem("fppbXtn_WB")}}
	    <span class='contactDetails'>  Ext: ${$data.getItem("fppbXtn_WB")}</span>
		{{/if}}
		<a id='callButton' href="tel:${$data.getItem("fppbPhone_WB")}" data-icon="grid" data-role="button">Call</a>
	</script>

When the JQuery template code evaluates the template, you can access some of the objects it passes in directly. The object here we are using is the $data object, which corresponds to the data that you passed to the jQuery.tmpl() function. With that ability we can call the getItem() function and get the value out of the field.

I’m feeling pretty happy with what I have up to this point. The dataProxy agent and the JSON document output is working well, and I’ve been able to keep the look and feel of the app intact.

Next up is the license page.

Notes documents as JSON

There are a couple of ways to do this online, but having a Java agent to do view JSON gave me a good opportunity to do it my own way. I have a couple things going for me at the moment - I don’t need rich text (yet), and I don’t need to be able to create or update documents (yet).

The ‘Document’ class in Java has a function called generateXML which has been around for a long time (5.0.3 according to the doc) so it should be relatively stable, but as with all things Domino… we shall see if it is in practice. That got me the DXL version of a Notes document. To get the JSON I went to github and got the org.json classes and imported them into a Java script library in Domino:

This has a lot of really handy classes in it, but what I really wanted was the XML class that has a toJSONObject method. With all these pieces, it was really easy to put the following method together:

    private void writeDocument(Database db, String documentId, boolean asJSON) throws NotesException, IOException, JSONException {
    	Document doc = db.getDocumentByID(documentId);
    	if( null== doc){
    		doc = db.getDocumentByUNID(documentId);
    	}
     	if( null== doc){
     		System.out.println("document not found");
     		return;
     	}
     	
		// write the document out
		java.io.Writer sw = new java.io.StringWriter();
     	doc.generateXML(sw);
     	sw.flush();     
		PrintWriter ao = getAgentOutput();
		if( asJSON ){
			ao.println("Content-type: application/json");
			ao.print(XML.toJSONObject(sw.toString()).toString());
		}
		else{
			ao.println("Content-type: text/xml");
			ao.print(sw.toString());
		}
     	ao.flush();
    }

I threw that into the dataProxy agent and added 2 URL parameters:

  • documentId= Lets me specify the document I want to access
  • outputformat=JSON Omitting this parameter keeps the output in DXL.

That’s it! With the next post I’ll show you how to integrate this into the mobile app.

Working around Domino

Seems like I do this a lot… so after running into a bug in the Domino JSON view output I was faced with a few choices. The first, and probably easier option, was to just rewrite the views I was using to fix the encoding problem I ran into. I ended up not using that option because it would mean that I would have to have custom views for all the data I was accessing. Even though I had created the views new for this exercise, ultimately I wanted to transition to preexisting views in the apps once I was ready to move to production. So now that I was faced with having to make design changes to the back end apps anyway, I decided to explore my other option which was to create a custom agent to serve up the data.

A custom agent has a couple disadvantages. First, it will require some special considerations when it comes time to deploy this regarding ACLs and agent signers. Second, it means that I have to re-implement something that already exists in Domino. The first one is just a fact of life and can be dealt with. The second one is more problematic because, I hate doing it, and I won’t be able to implement all the possible options and it will require a lot more testing.

On the up side, a custom agent does have many advantages also. First, I can make the output anything I want, or include as much or as little of the data. Second, I now have a chance to standardize an API that I can easily port/re-implement on other back-end systems without impacting my front end (big plus at the moment). Third, the ReadViewEntries does not support JSONP, which might come in handy at some point in this project I’m sure. And Finally, it can give me access to data a view cannot, like rich text. With these in mind the choice to custom code the agent became much easier to accept.

The dataProxy Agent

I wrote the agent in Java because LotusScript is just no fun anymore, and it gives me the most flexibility when I comes time to port this to some other platform. The basics of the agent was to try and get as close to the ReadViewEntries format as possible so I took the output from that and loaded it up in a text compare program so I could see it side-by-side with my output. Form there I just tweaked my code until I had the same, or nearly the same output. In about an hour I had the following code:

	private void writeViewJSON(Database db, String viewName) throws NotesException {
		// get the view
		View v = db.getView(viewName);
		v.setAutoUpdate(false);
		// get the navigator
		ViewNavigator nav = null;
		if (params.containsKey("RestrictToCategory")) {
			nav = v.createViewNavFromCategory((String) params.get("RestrictToCategory"));
		} else {
			nav = v.createViewNav();
		}

		// write the view out
		PrintWriter ao = getAgentOutput();
		ao.println("Content-type: application/json");
		ao.println("{");
		ao.println("\"@timestamp\": \"" + v.getLastModified() + "\",");
		ao.println("\"@toplevelentries\": \"" + v.getTopLevelEntryCount()
				+ "\",");
		ao.println("\"viewentry\": [");
		ViewEntry e = nav.getFirst();
		for (int i = 0; i < nav.getCount(); i++) {
			ao.println("{");
			ao.println("\"@position\": \"" + e.getPosition('.') + "\",");
			ao.println("\"@unid\": \"" + e.getUniversalID() + "\",");
			ao.println("\"@noteid\": \"" + e.getNoteID() + "\",");
			ao.println("\"@siblings\": \"" + e.getSiblingCount() + "\",");
			ao.println("\"entrydata\": [");
			int writeCount = 0;
			for (int j = e.getIndentLevel(); j < e.getColumnValues().size(); j++) {
				ViewColumn c = (ViewColumn) v.getColumns().elementAt(j);
				if (!c.isHidden()) {
					if (writeCount++ > 0) {
						ao.println(",");
					}
					Vector cv = writeColumn(ao, e, c);

					if (c.isCategory()) {
						break;
					}
				}
			}
			ao.println("]");

			ao.println("}");
			if (i + 1 < nav.getCount()) {
				ao.println(",");
			}
			ao.println();
			e = nav.getNext(e);
		}
		ao.println("]");
		ao.println("}");
	}

Some of the differences, though minor, are to do with categories and the NoteId and UNID. In the Domino output you get special NoteId’s and in my version they are just empty strings. Also, the timestamp that I output is a different format than the domino version.

The agent supports a few URL parameters:

  • s= Lets you specify the server.
  • db=   Lets you specify the database the view is defined in.
  • v= Lets you specify the view to read.

using these, I can get to any view in any database.

That was all I needed at this point on the server. On the client side, it only required a single change to the Javascript to change the URL in the JSON calls. 

Not the end of the story…

This isn’t the end of the agent though. I have also added another function that makes a huge difference in the client that I didn’t have the ability to do before. I will cover that in my next post.

JSON bug….

OK… this project isn’t as easy as it was yesterday…. So in the process of creating the contacts page I found a bug in the JSON output for my view. It really wasn’t easy to find either… 

I started as I normally do by creating the basic page template and JS code that I have done for the past two pages. I ran into a problem when my getJSON call wasn’t returning any results (the success callback wasn’t being called). I took the URL and pasted into a browser and every looked fine. The server was the same, so no cross domain issues. No errors on the server console. ACL was the same as the other database…. what was going on!

Reading though the doc on JQuery.getJSON I found this note:

Important: As of jQuery 1.4, if the JSON file contains a syntax error, the request will usually fail silently. Avoid frequent hand-editing of JSON data for this reason. JSON is a data-interchange format with syntax rules that are stricter than those of JavaScript’s object literal notation. For example, all strings represented in JSON, whether they are properties or values, must be enclosed in double-quotes. For details on the JSON format, see http://json.org/.

Hmmm….. could this be my problem? I went back to the output I got from the browser window and pasted it into a JSON validator and sure enough there was a problem.

Parse error on line 77:
...               "0": "Teamstudio CIAO\! C
-----------------------^
Expecting 'STRING', 'NUMBER', 'NULL', 'TRUE', 'FALSE', '{', '['

So it looks as if Domino is trying to escape a the ‘!’ character when it shouldn’t be. This won’t fly with the JSON.parse call that JQuery.getJSON uses.

CRAP!

I have to say, I seem to run into this way too often with Notes and Domino. There are so many useful features that are just not robust enough to use. Even worse is most of the bugs you find are not easily worked around and you never seem to find them until you are a week into a project. It is extremely frustrating!

I have a few alternatives that I can explore so unless anyone has any ideas how to fix this, I think I’m gonna ditch the views and switch to an agent. I think that even though the agent is more work, there are a lot of cool possibilities that it offers that wouldn’t have been possible with just a view. Though, I really did like the simplicity and ease of reuse the views offered…. they just don’t do what they advertise.

Small tweak to JS from yesterday

In prep for tomorrows post, I made a few tweaks to the Javascript I posted yesterday. I’m posting this now since I think the post tomorrow will be long enough, and I’m not ready to write it up yet. This function is handy in a general sense also, so this way it doesn’t get lost in a longer post.

To get the contacts list I needed to make a few changes to the JS functoin convertViewEntryToJSONObject:

function convertViewEntryToJSONObject(data){
	var entry = [];
	for( var j=0; j<data.viewentry.length; j++ ){
		var obj = {};
		obj.noteid = data.viewentry[j]["@noteid"];
		for(var i=0;i<data.viewentry[j].entrydata.length;i++){
			var prop = data.viewentry[j].entrydata[i]["@name"];
			var val = "";
			if('undefined'=== typeof data.viewentry[j].entrydata[i].text){
				val = data.viewentry[j].entrydata[i].textlist.text[0][0];
			}
			else{
				val = data.viewentry[j].entrydata[i].text[0];
			}

			obj[prop]=val;
		}
		entry.push( obj );
	}
	return entry;
}

The version I needed yesterday worked well when I only got one view entry back. Now it works with views that return multiple entries. This should make the view object I get back a lot easier to work with long term. 

Just the basic details

Like with the XPages version, this page will be pretty simple in it’s overall structure:

<body>
	<div data-role="page"  id="details">
	<div  data-position="fixed" data-role="header">
	<a rel="externa" href="index.html" data-icon="arrow-l" data-iconpos="left">Home</a>
		<h1 id='companyName'></h1>
	</div>	
	<div data-role="content">
	<div id='companyDetails'></div>
	<div id='mapCanvas' style="margin-right:20px;width: 100%; height: 360px;"></div>
	
	<a href="#" data-icon="forward" data-role="button">Directions</a>
	<a href="#" data-icon="grid" data-role="button">Call</a>
	
	</div><!-- /content -->
	<div  data-position="fixed" data-role="footer">
		<div data-role="navbar">
		<ul id='footerNavLinks'>
			<li><a href="#" class="ui-state-persist ui-btn-active">Details</a></li>
			<li><a href="#" onclick="changeCategory('contactList.html')">Contacts</a></li>
			<li><a href="#" onclick="changeCategory('licenseList.html')">Licenses</a></li>
			<li><a href="#" onclick="changeCategory('notesList.html')">Notes</a></li>
		</ul>
		</div><!-- /navbar -->
	</div><!-- /footer -->
</div><!-- /page -->
</body>

In this block of code there are two things to keep an eye on. First is in the header <div>:

<h1 id='companyName'></h1>

Next is the <div> with the id ‘companyDetails’:

<div id='companyDetails'></div>

Also you may have noticed that in the footer, each link contains an onclick handler, but I’ll get to that later.

The two id’s above are going to be where my replacement text ends up. Notice how the replacement target it can be any thing you want, not just a <div>.

Here are the templates I’m going to use:

	<!-- 
		JQM only loads the [data-role='page'] element into the linked pages
		so the templates all need to be here, in that <div> not in the <head>
	-->
	<script id="companyNameTemplate" type="text/x-jquery-tmpl">
		${fcftCompany_WB}
	</script>
	<script id="companyDetailsTemplate" type="text/x-jquery-tmpl">
		<span style="display:none" id="address">${faddLocation_SS}</span>
		<span style="display:none" id="companyName">${fcftCompany_SS}</span>
		<span class='companyAddress'>${faddAddress_SS}</span><br/>
		{{if faddAddress_SS_1.length}}
		<span class='companyAddress'>${faddAddress_SS_1}</span><br/>
		{{/if}}
		<span class='companyAddress'>${faddTown_SS}, ${faddCounty_SS}   ${faddPostcode_SS}, ${faddCountry_SS}</span><br/>
	</script>
	

It is very important that these two <script> blocks go inside the <div data-role=’page’>. The reason, which took me a while to come to realize has to do with the way JQuery Mobile follows links. JQM uses AJAX to load the content of the page pointed to by all <a> tags unless you use the rel=”external” or its variants. This is done so the next page can have fancy transitions. The way a page slides in from the right is an example of this effect. The way it works is that in the background, JQM gets the div from the new page that contains the data-role=’page’ attribute and only loads that into the current page by replacing the current data-role=’page’ div. The effect is neat but it actually can cause really bizarre things to happen if you don’t watch out. For example, in the first page I created, I placed the template blocks in the <head> of my page. In the first version of the details page I did the same thing. After my first test, I couldn’t get the customer name to show up in the title bar and I would be getting ‘object has no method ‘appendTo” errors in the JS Console:

It took a while to figure this out, but the cause was that when JQM loaded details.html page, it only use the data-role=’page’ div. At that point I had two options. The first was to load the template into the <head> of the first page. Since that page is used as the target for the next page to load into, my template will be there. The other option, which I ended up going with, was to put all the things I wanted to load into the page div. I like the second approach because it keeps the ‘page’ more intact. Keeping everything in the first page just seems really messy to me.

The next step is to update the ‘mobileinit’ function in my app.js with the following code for my new details page:

$(document).bind("mobileinit", function(){
	$('#main').live('pageshow',function(event, ui){
		loadMainPageData();
	});
        $('#details').live('pagebeforeshow',function(event, ui){
		loadDetailsPageData();	
		loadMap();		
	});
});

Here is where I hit my first real problem. Getting a list of customers from a view is pretty easy. Getting a single customer document is not. There is no Domino URL parameter to read a document as JSON. This will have to be the first design change I make. I figure I have a couple options though. First option would be to create an agent I can call that returns the document I want in JSON format. The upside to this is the flexibility it would give me as I could add what ever I wanted, and it would make RichText possible. The downside to this is that I would have to be able to run code on the server which means signing the database with the correct Id. I don’t really want to deal with that right now. The second option is to still use a new view and categorize it on the Note ID or customer name of the document I want, and add all the fields I need as columns. This has two distinct advantages - it’s easy to create views and change them without needing special access, and it keeps the native Domino changes to a minimum. Agents require testing and they are not easily ported if I ever want to move the data off of Notes (gasp!! - I’m not in charge of IT so who knows what will happen tomorrow….). So the view I created is really simple. Categorized on NoteID and I added columns for each field I wanted off of the document. I have no RichText in these documents that I need, so this will work fine for now. With the view created I have the following function for loading a customer ‘document’:

function loadDetailsPageData(){
	var cat = ('00000000'+getURLParameter("documentId")).slice(-8);
	// Get the list data.
    $.getJSON('http://10.1.1.39/unplugged/rm.nsf/JSONCustomers?readviewentries&outputformat=json&RestrictToCategory='+cat,
    	function(data) {
	    	var doc=convertViewEntryToJSONObject(data);
	    	$('#companyNameTemplate')                  // Select the template. 
    			.tmpl(doc)              // Bind it to the data. 
    				.appendTo('#companyName');   // Render the output.
    		$('#companyDetailsTemplate')                  // Select the template. 
    			.tmpl(doc)              // Bind it to the data. 
    			.appendTo('#companyDetails');   // Render the output.

      		codeAddress();
       }
   );
}

function convertViewEntryToJSONObject(data){
	var obj = {};
	obj.noteid = data.viewentry[0]["@noteid"];
	for(var i=0;i<data.viewentry[0].entrydata.length;i++){
		obj[data.viewentry[0].entrydata[i]["@name"]]=data.viewentry[0].entrydata[i].text[0];
	}	
	return obj;
}

The document I want is loaded via a URL parameter just like before, and the URL I use is almost the same to get the JSON data. The only difference is that I added ‘RestrictToCategory’ to the url and used the NoteID as the parameter. This data returned by the view JSON is not quite what I want so the function convertViewEntryToJSONObject() takes the normal view JSON

and converts to another object that looks more like a document

Having this faux document object makes it much easier to create and maintain my templates in the html. My template can refer to actual field names just like in the Xpage version, instead of viewentry[0].entrydata[2].text[0]. Much easier to read.

Loading the map and displaying the address happen in exactly the same way, and I was able to use the same functions from earlier to do it. All I did was copy that code from the XPage and put it into the app.js file.

So this is a lot to cover in one post. I know it seems like a lot of code, but everything done today is laying the ground work for all the pages to come, so the rest should be much easier. Next time I’ll finish with this page and take a look at the footer and the links there.

We don’t need no stinking XPages!

With my mobile app complete, I wanted to see if I could start to decouple my UI from Domino and let it serve as just a backend data source. There are a couple reasons why I want to do this, which I’ll get to later, but for now I just want to stick with some more standard techniques.

Even though I’m going to be working with HTML files exclusively, I’m going to store them in my ‘Customers To GO!’ database for simplicity. At some point I would really like to get my design out of Domino Designer, but that will have to wait until later.

The Plan

The plan at the moment comes down to two main ingredients.

With these two things I think I can recreate the COG application with little change to the source data’s design. Remember the key goal here is to keep the UI completely Domino agnostic.

I’m going to walk though this in pretty much the same way I did with the XPages version and work through each page as I go. So the first page I’m going to work with is the customer list. I created a file resource in my database called ‘index.html’ and ‘app.js’. In addition to all the js script imports for JQuery, etc. the interesting part here is the body of my HTML:

<body>
	<div data-role="page" id="main">
		<div data-position="fixed" data-role="header">
			<h1>Customers On the GO!</h1>
		</div>
		<!-- /header -->
		<div data-role="content" class="companyList">
			<ul data-role="listview" data-inset="true" id="companiesContainer"></ul>
		</div>
	</div>
</body>

Much like the XPages version there really isn’t much to see here. To that, I’m going to add a <script> block that will contain my template that JQuery will use to do my replacements:

<script id="companyTemplate" type="text/x-jquery-tmpl">
	<li>	
	<a href="details.html?openfileresource&documentId=${$data['@noteid']}" >
	
	<span class='companyName'>${entrydata[0].text[0]}</span><br/>
	<span class='companyAddress'>${entrydata[2].text[0]}</span><br/>
	{{if entrydata[3].text[0].length}}
	<span class='companyAddress'>${entrydata[3].text[0]}</span><br/>
	{{/if}}
	<span class='companyAddress'>${entrydata[5].text[0]}, ${entrydata[6].text[0]}   ${entrydata[7].text[0]}</span><br/>
	
	</a></li>
</script>

So the way JQuery templates works is that you define a template like above and give it an ID and set the type to “text/x-jquery-tmpl” then inside that you place HTML that contains replacement sequences that some JavaScript will process later with data that you provide. Everything between the ‘{’ and ‘}’ refers to variable in the JSON object we get later. In my case I’m working with viewentry data so it’s the entrydata variable I want to access. Templates are actually powerfull enough that you can even include conditionally rendered parts using if statements or loops using the each statement.

For a more comprehensive tutorial on JQuery Templates check out this page. Or even just the examples in the templates doc.

For now, I’m going to place this script block in the <head> of my page.

The next thing I need is some JavaScript to do the replacements. I’m going to store almost all of my JavaScript in ‘app.js’.  This is what I have for the start:

/*
 * Everything starts here!
 */
$(document).bind("mobileinit", function(){
	$('#main').live('pageshow',function(event, ui){
		loadMainPageData();
	});
});

function loadMainPageData(){
	// Get the list data.
    $.getJSON(
        'http://10.1.1.39/unplugged/rm.nsf/ALLCOMPANIES?readviewentries&outputformat=json', 
            function(data) {
 
               $('#companyTemplate')                  // Select the template. 
               .tmpl(data.viewentry)              // Bind it to the data. 
               .appendTo('#companiesContainer');   // Render the output.
           }
       );
}

The first part is the JQM equivilant of JQuery’s $(document).ready() function. It let’s you bind actions to particular events that JQM raises. In this case, we can bind some init code to the pageshow event for the <DIV> with the id ‘main’. The function we want to call is loadMainPageData()

loadMainPageData() is where the heavy lifting for the template code does it’s work, though it’s pretty simple. First we use JQuery’s getJSON function to make a call to Domino and get my list of companies from CRM database (This is a view that already existed so double bonus!). Using the outputformat=JSON tells Domino to return the view in JSON (imagine that…). The second parameter to the getJSON call is a callback function that receives the JSON Object.

To actually perform the replacement you use the tmpl() call on the script block that contains the template you want to use - in this case the id I want is ‘companyTemplate’. This it’s just a matter of using appendTo() with the id of the DOM object where you want the new HTML to go - in this case ‘companiesContainer’. Then as they say, ‘Bob’s your uncle’!

Dang! OK, so here is the first thing that you will probably run into do this kind of thing with JQM and JQuery. Things don’t always happen in the way you want them to and stylizing lists is one of them. Because the HTML doesn’t actually contain any <li> tags, JQuery doesn’t make any changes to the underlying DOM when it sets up. Once the page is ready, only then do we add the <li> from the AJAX call to Domino. The work around is to tell JQuery to reapply it’s style markup to the list now that we have all the items in place. Simply adding the following call:

$('#companiesContainer').listview('refresh');

after the tmpl() call and all is well.

And that is pretty much it. This didn’t require any changes what so ever to the original applications and I ended up with exactly the result. So far, so good!