Playtime with Cordova: pt.5 Cordova, jQuery mobile, SQLite and dynamic data-driven pages

July 18, 2014

DISCLAIMER: This is me sharing my learning, I have not yet created a final application, and also, some of the process’ below may not be best practice. BUT as a place to start playing, which is really what we all want to do, I think it works.

As I am feeling particularly lazy right now, I am going to start by quoting my previous (part 3) article.

“have a read of this, as we will be doing some dynamic generation of pages soon using data from a csv file (both local, and a remote one).

http://demos.jquerymobile.com/1.1.1/docs/pages/page-dynamic.html

The main things to take home in there are how the ‘pagebeforechange’ binding is required, aswell as the ‘enhancement’ parts. The enhancements are needed, as it triggers the jQuery mobile functionality to get all the layouts and listeners behaving on your new page as if it was hard coded.”

So, what I want to have aresome pages whichgeneratedynamically. I wont get you a tidied project here, but you will have enough to see how it all ties together and get building some cool stuff. We begin with creating a csv, you will likely want to think long-term with this, so maybe create a spreadsheet in google docs and export from there so you/client can easily update in the future. My system was a set of recipes, each having ingredients and steps attached to it. So let’s start with our recipe csv file, and get it looking like this:

id,name1,recipe 12,recipe 23,recipe 34,recipe 45,recipe 56,recipe 67,recipe 78,recipe 8

Save this into a folder in your www folder, I used www/data/recipes.csv for my app.

We then want to quickly add our page placeholder to the template in index.html. use something like this:

       <div data-role="page" id="recipe">           <div data-role="header">               <h1>Recipe</h1>               <a href="#recipes" class="ui-btn-left">Recipes</a>           </div>           <div data-role="main">           </div>       </div>

Now we create our bind listener to tell it to keep an eye on that query string, and do some exciting stuff when it needs to

   // Listen for any attempts to call changePage().   $(document).bind("pagebeforechange", function(e, data) {       // Check page is loaded by url       if (typeof data.toPage === "string") {           var u = $.mobile.path.parseUrl(data.toPage);           var res = /^#recipes$/;           if (u.hash.search(res) !== -1) {               // Load page content               showRecipes(u, data.options);               // Disable defaults               e.preventDefault();           }       }   });

Next we want to setup the function which gets run when that triggers

   function showRecipes(urlObj, options) {       var pageSelector = urlObj.hash.replace(/\?.*$/, "");       // Set some global variables for other functions       // (there is a better way to do this somewhere)       window.urlObj = urlObj;       window.options = options;       // Get the page we are going to dump our content into.       var $page = $(pageSelector);       window.output = '<p>some intro copy goes here</p>';       // Populate ingredients -> populate steps -> render page       // These COULD be nested all in one command if preferred       dbShell.transaction(populateRecipes, errorCB);   }

Nearly there, now let’s get our content into the output variable

   function populateRecipes(tx) {       // Get recipes       tx.executeSql('SELECT * FROM recipes', [], function(tx, results) {           if (results.rows.length > 0) {               window.output = '<h2>Recipes</h2>';               window.output += '<ul data-role="listview" data-inset="true">';               for (var i = 0; i < results.rows.length; i++) {                   window.output += '<li class="recipe-item recipe-item-' + results.rows.item(i).rid + '"><a href="#recipe?rid=' + results.rows.item(i).rid + '">' + results.rows.item(i).name + ' (' + results.rows.item(i).rid + ')' + '</a></li>';               }               window.output += '</ul>';           } else {               window.output = 'Sadly there are no recipes in the database';           }           // Render the page           renderDynamicPage();       }, errorCB);   }

Run the final renderer for the content

   function renderDynamicPage() {       urlObj = window.urlObj;       var pageSelector = urlObj.hash.replace(/\?.*$/, "");       // Get the page we are going to dump our content into.       var $page = $(pageSelector);       var $header = $page.children(":jqmData(role=header)");       var $content = $page.children(":jqmData(role=main)");       // Set the header and content       $header.find("h1").html("the title");       $content.html(window.output);       // 'Enhance' the page and listview       $page.page();       $content.find(":jqmData(role=listview)").listview();       // We don't want the data-url of the page we just modified       // to be the url that shows up in the browser's location field,       // so set the dataUrl option to the URL for the category       // we just loaded.       options.dataUrl = urlObj.href;       // Change the page using variables       $.mobile.changePage($page, options);   }});

Useful resources