Notes on Mobile
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!

Adding JQuery Mobile to an XPage

So I now have a very basic view on the first page of my mobile app. The list is really kinda ugly though even though I did do a little bit of styling with a basic CSS file.

Since this is intended to be a mobile app I don’t want plain old links because those are hard to see and press when viewed on a small screen. This is where JQuery Mobile comes in. JQM is a smallish JS and CSS library that works to create pages that look and transition more like native phone apps do. It’s not iOS or Android specific in it’s behavior, but tries to create a native like experience that is the same across each of these devices.

To use JQM you need to do a few things to your markup according to the following documentation page. I’m using the hosted version, so to begin with I added the following to the source of my XPage:

<xp:script
	src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"
	clientSide="true">
</xp:script>
<xp:styleSheet
	href="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.css" />
<xp:script
	src="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.js"
	clientSide="true">
</xp:script>

The first is JQuery and that is needed by JQuery mobile. The second two are what you need for JQM.

The next step is to add the mark up that JQM uses to identify a page. It’s only a few lines:

<div data-role="page">
	<div data-role="content">
		<!-- <ul> and <xp:repeat> tags go in here --> 
	</div> <!--content-->
</div> <!--page-->

We still haven’t gotten anything new yet though. The page still looks the same. The big changes come when we apply a JQM style called “listview” to the list we created.

<ul data-role="listview" >

JQM uses the data-role attribute to apply it’s styles and things since there is more going on actually than just a style change. So now with just those little changes look what we have now!

Almost there!

Whoops! That isn’t quite what I was expecting! Turns out there is one more simple thing we need to do for this to all work right.

Simple now…. but this actually took a long time to figure out!

Back in the source to the XPage, remove the id=”repeat1” attribute form the repeat control. Once that was done I had what I wanted:

There is a lot more that JQM can do for us besides some simple list formatting. In addition it’s important to understand how JQm handles links and page transitions, which is what I will do next!