Titanium Mobile: Database Driven Tables with SQLite – Part 3

Titanium Mobile: Database Driven Tables with SQLite – Part 3

Tutorial Details
  • Technology: Titanium Mobile
  • Difficulty: Intermediate
  • Completion Time: 30 - 45 Minutes
This entry is part 3 of 3 in the series Titanium Mobile: Database Driven Tables with SQLite

Welcome to the third part in our series on database driven Titanium Mobile development. This tutorial will continue to build on the principles introduced in earlier posts, but we will be altering our app design in one very significant way. Instead of using a TableView object to display a custom interface, we are going to switch to using a WebView object. The reason for this is that the WebView object will allow us to control our interface using CSS and HTML, and TableView will not. For those of you coming from a web development background, you should feel right at home with this approach.


Recap

The first part of this series laid the foundation for a database driven application. The second part extended the functionality by creating the ability to read a remote database and insert values into our tables. This tutorial will utilize the source code from the first two parts in this series.


Step 1: Setup the Application

Open Titanium Developer and create a new project. Choose Mobile and fill in all of the required information. Then click Create Project. Copy the products folder from your previous application and the products.sqlite database into the new resources directory. Download jQuery and place it into the resources directory as well. Also, create a new folder and name it images. Now you should be ready to go.


Step 2: Stripping app.js

We are going to strip the app.js file down for simplicity sake. Remove everything except where we created the tabGroup. Open the tabGroup and create our first, and only, tab. Here it is:

	var tabGroup = Ti.UI.createTabGroup();
	var main = Ti.UI.createWindow({
	    title:'Product Categories',
		url:'products/product_category.js'
	});
	var tab = Ti.UI.createTab({
	    icon:'images/tabs/KS_nav_ui.png',
	    title:'Products',
	    window:main
	});
	tabGroup.addTab(tab);
	tabGroup.open();

If you launch the application, it should look familiar:

Main product categories

NOTE: If you copied your database from Part 2 of this series, you may wonder why any additionally added values (those added from inside the application) are not there. This is because the active local database is installed on the device. Our local file remains untouched. If you remove the previous application from the simulator and reinstall it, your database will also be back to its original state.


Step 2: Extending The Existing Product Files

The first file that is loaded by our application is "product_category.js". We are going to leave this file completely alone. The next thing we can do to keep things clean is to delete the "product_specs.js" file. We will be using a WebView to replace the function of this file. Open "products.js" and let's get hacking. Aside from the eventListener, there is only one thing we need to change in this file. Remove the path from where we build our array:

	var dataArray = [];
	while (rows.isValidRow())
	{
	    dataArray.push({title:'' + rows.fieldByName('name') + '', hasChild:true});
	    rows.next();
	};
...

If we run our application at this point, it will likely fail due to us deleting files and changing the above information. However, this is what it would look like:

Product category individual products table.

Our eventListener is now completely in charge of the outcome from the click event. The first thing we need to change is our if statement. Previously, we were checking if there was a path. Since we removed the path, we need to attach something else to check for. We also need to use this to determine which item was selected. Use "title." This is the only value we have that is a unique identifier. Make sure to change this value when we create the window also.

    tableview.addEventListener('click', function(e)
    {
        if (e.rowData.title)
        {
            var win = Ti.UI.createWindow({
            title:e.rowData.title,
            backgroundColor: "#000"
        });
...

Step 3: Adding To the eventListener

Let’s think through the next steps. We need to create a WebView and set its path, query the database based on the selected row (we have to do this here because Ti.Database is not accessible from a WebView), create our variables from our database query, and finally pass them to the WebView. One thing to keep in mind is that we cannot pass anything to the WebView until it is created. We will use a setTimeout function to delay our passing of the variables. Here is how the code ends up looking:

    var web = Ti.UI.createWebView({
        url:'productsWebview.html'
    });
    var db2 = Ti.Database.install('../products.sqlite','products');
    var specs = db2.execute('SELECT * FROM Products WHERE name="' + e.rowData.title + '"');
    var name = specs.fieldByName('name');
    var pwidth = specs.fieldByName('pwidth');
    var pheight = specs.fieldByName('pheight');
    var pcolor = specs.fieldByName('pcolor');
    var qty = specs.fieldByName('qty');
    var stripName = removeSpaces(specs.fieldByName('name'));
    function removeSpaces(strip) {
        return strip.split(' ').join('');
    };
    // Add the webView to the window
    win.add(web);
    // Create a timeout - we want time for the window to be ready before we fire the event
    setTimeout(function(){
        Ti.App.fireEvent("webPageReady", {name:name, pwidth:pwidth, pheight:pheight, pcolor:pcolor, qty:qty, stripName:stripName});
    },500);
    Ti.UI.currentTab.open(win,{animated:true});
    ...

We created a second instance of the database (As a local variable inside the function. We are using scope here to make sure we don't mess anything up.) and created our query with the data from the selected row title value. Created our individual variables for the database column data, and created a second variable using the product name, only removing the spaces with a "removeSpaces" function. This is so we can create a path to an image using the product name in our WebView.

We then add our WebView to the current windows. Our variables are passed to the WebView and named "webPageReady" by the TI.App.fireEvent. This fires once the specified amount of time has passed from the setTimeout function. In our case, it is 500 milliseconds, or half of one second.


Step 4: Adding an Activity Indicator

If we were to create our WebView right now, everything would work fine. However, there would be something that just didn't feel right to the user. The WebView would be created and the data set, but there would be a second or so of time where the page was blank, or missing items. It would appear that something is broken. Users have become accustomed to always knowing something is happening. For Windows users, there is that nice little hour glass, OS X users have the spinning color wheel, etc.

Users have become accustomed to always knowing something is happening.

For our activity indicator, we need to create a new window, create the activity indicator, set the length of time for it to display, and then hide the window. We need to also make sure to add the activity indicator to the new window and the new window to the current view.

			var actWin = Ti.UI.createWindow({
				backgroundColor: '#000',
				opacity: 0.8
			});
			var actInd = Ti.UI.createActivityIndicator({
				style:Ti.UI.iPhone.ActivityIndicatorStyle.PLAIN
			});
			actWin.add(actInd);
			if (Ti.Platform.name == 'iPhone OS') {
				actInd.show();
				actInd.color = '#FFF';
				actInd.message = 'Loading...';
				setTimeout(function()
				{
					actInd.hide();
				},1500);
			};
			setTimeout(function()
			{
				actWin.hide();
			},1500);
			// Add the webView to the window
			win.add(web,actWin);
...

When we create the indicator, we are setting the style to look like the standard iPhone indicator. We then also want this to look as native as possible and let the user know what is going on. We use an if statement, setting it to check for the platform type, add a message, and set the length of time before we hide the window.

This timeout is inside the if statement, so we need to create another timeout function to hide the window. In both cases we are setting the time to 1500 milliseconds. Thinking back to the last step, we allowed 500 milliseconds before our data is passed. This will allow another second for the data to be actually loaded into the WebView.

Activity indicator as WebView data loads.

Step 5: Building Our Table In the WebView

Create a new html file with the name and location that we set above when we created our WebView. It is not necessary to add all of the standard HTML header information. I like to declare my doctype and add the <head> tags. We will need this because we need to use some scripts.

First, create the content of the body. Using a table, add the required amount of rows and columns. For this tutorial, we need four rows and two columns. The label will be in the left column and the spec in the right. Use a non-breaking space (&nbsp;) in the spec cell. This is what our table looks like:

<!DOCTYPE html>
<html>
    <head>
        <link rel="stylesheet" href="productStyle.css">
    </head>
        <body id="body">
            <div id="container">
                    <div id="content">
					<div id="name"> </div>
					<div id="picture"> </div>
					<div id="mainTable">
						<table border="0" align="center" cellpadding="20" cellspacing="0" id="mainTable">
							<tr>
								<td class="prodTitle">Width</td>
								<td class="prodSpec" id="pwidth"> </td>
							</tr>
							<tr>
								<td class="prodTitle">Height</td>
								<td class="prodSpec" id="pheight"> </td>
							</tr>
							<tr>
								<td class="prodTitle">Color</td>
								<td class="prodSpec" id="pcolor"> </td>
							</tr>
							<tr>
								<td class="prodTitle">Quantity</td>
								<td class="prodSpec" id="qty"> </td>
							</tr>
						</table>
					</div>
				</div>
			</div>
		</body>
	</html>

It is also a great idea to add a stylesheet so we can customize how our page displays. Directly underneath the "content" div we want to create some form of our own header. I think it is a great idea to display the product name and show a picture. The most important thing to do when building our table is to add an "id" to the cell that will contain our specs. For the sake of styling, we should also add a class to the similar cells.


Step 6: Catching Variables From the Database

In the previous tutorials, we created a variable on one page and attached it to the new window. This variable was accessed using Ti.UI.currentWindow.varName and then globally accessible to the page. Because this is a WebView we cannot do this. What we need to do is to add an EventListener and catch the passed variables.

Titanium Developer info window with passed variables being displayed.

The console in Titanium Developer logs the variables and the values that are being passed. This can be very handy for debugging.

In the EventListener we specify the event we are listening for (webPageReady) and declare our variables using our callback.

<script>
        Ti.App.addEventListener('webPageReady',function(e) {
            var name = e.name;
            var pwidth = e.pwidth;
            var pheight = e.pheight;
            var pcolor = e.pcolor;
            var qty = e.qty;
            var stripName = e.stripName;
        });
</script>

Step 7: Setting the Variables to the Table

Now that we have our variables declared we need to figure out how to use them in our page. We cannot return them from the function and then use the variables in our table, so we have to get creative. Load jQuery on your page and wrap the current EventListener inside of a document.ready function.

Then use the id for each cell defined in the previous step and replace the element using jQuery and the correct variable.

<script src="../jquery-1.4.4.min.js"></script>
<script>
    $(document).ready(function() {
        Ti.App.addEventListener('webPageReady',function(e) {
            var name = e.name;
            var pwidth = e.pwidth;
            var pheight = e.pheight;
            var pcolor = e.pcolor;
            var qty = e.qty;
            var stripName = e.stripName;
            $("#name").html(name);
            $("#pwidth").html(pwidth);
            $("#pheight").html(pheight);
            $("#pcolor").html(pcolor);
            $("#qty").html(qty);
            $("#picture").html('<img src ="../images/productThumbs/' + stripName + '.png">');
        });
    });
</script>

The last element we are replacing in the above code is our product image variable. Use the variable from the product name that we removed the spaces from to build the link to the image. Also, make sure to create the directory and images for each product. For the sake of styling, it is a great idea to make all of the product images the same size. Something like 150px by 150px is a good size for most screens.

Finally we should have a working application with all the code completed. Here is how the WebView will look:

WebView with product image and specs.

WebView with product image and specs.

Wrap Up

Once our images are created for all of the products it is very easy to use some standard CSS in our stylesheet and make our product pages look exactly how we want them to appear. If the information we are displaying and using in our application is updated frequently, a remote database may be the way to go.

Series Navigation«Titanium Mobile: Database Driven Tables with SQLite – Part 2

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • rafael

    Please can you put the source files for this one in the blog? Your tutorials are getting extremly nice.

    • Dan Stever
      Author

      Thanks rafael.

      It is great to know that these tutorials are helping people out. I will check if the source can get added.

  • BJ

    It’s great stuff. Please provide us with the source code. Thanks!

  • Dan Stever
    Author

    (Just to have some sort of update on the post…)

    The source code has been added.

    Hope it helps you guys.

  • Pat

    Hey,

    nice selection of tutorials.

    Just one question though, is there any sort of event that we can hook into in Appcelerator (similar to how jQuery’s dom ready works) which tells us when our webview is loaded. This would be better than just having some arbitrary number of milliseconds to show/hide our loading screen.

  • Eric

    Compiling using 1.6 for the Android and I’m getting a run-time error: removeSpaces is not a function, it is undefined, products.js line#74

  • ade

    ive noticed that sometimes the db details dont show in the sim in either titanium or when i open the project in xcode even though the debug window is showing the details – also when ive tried installing on my iphone device no details show – any ideas of where to start ?

  • Terry Tibbs

    How about sync with a hosted MySQL database?.

  • http://www.myersdesign.com Liz Myers

    I see my db details within the webView in the Emulator – BUT my db details do not show up in the webView on device. Is this due to sandboxing? Has anyone got any good workarounds. Thx in advance…

  • Chris Peoples

    Thank you for this tutorial! I was able to edit the products.sqlite database, edit the images, and launch the application within Titanium with the iPhone Emulator (perfectly) and the Android Emulator (with many bugs, kill process: Force Closes or Dismiss buttons, and am still unable to replicate the iPhone GUI style). However, I am at the point where I NEED to integrate advertisements above and/or below the top and/or bottom tabs of the listview application, but with MUCH trial-and-error have been unsuccessful to integrate any of the Titanium-or-Masuidrive Admob modules or integrate any web-view ad integrations for html Admob ads. I have been successful in launching this application in Xcode 4.2 with NO problems. But, the Titanium-built application does not contain a ‘main.xib’ or any delegate_view_controller files to integrate iAds. I am also trying to replicate the application through Eclipse for Android in order to integrate Admob/Smaato advertisements but I do not know how to import or copy certain files to a new Eclipse project to launch without any errors. Sorry for the extensive explanation/message but I am at a loss here. I am currently resorting to finding sample listview app source codes and learning both Xcode and Eclipse to mimic the application I already have edited with your titanium mobile example. Any and all help will be appreciated. Thank you so much for your time. I look forward to hearing from you.

  • York

    Hi

    Thank you for these great tutorials, they are very very helpful for novices.
    I guess you could say it is the only existing documentation detailing the database for titanium on the web.

    It would be really nice if you could add an explanation on the use of header and filter alphabetical for tableview of “product_category.js” (as can be seen in “Contact”).
    It would be very useful for big lists.

    Thanks again for your stuff !

  • Rogério

    Hi,

    Nice tutorial. Thanks a lot. It guided me through an idea, but now i’m stuck.

    I made something like this, but now i’m having problem inserting some images to a local db. I’d like to insert and retrieve the images of the products.

    I saw some examples, but i think it’s not working. My idea is to take a pic with the cam to feed the db.

    Here’s how i’m trying to save images.

    Take the photo, put it in a imageview, resize, convert the imageview to blob and insert into the db.
    To retrieve, i select from the table, put it in an imageview again, convert the imageview using toImage(),
    put it again in another imageview and place in the window.

    But nothing appears. I tried a lot of ways. Putting the result of query directly into imageview, saving into a file, converting to a blob again, but… nothing happens.

    Any sugestion?

    Thanks in advance.

  • Jaelson

    how to generate an image from a given query in the database SqlLite?
    I do a query in the table which is returning a field of type BLOB SqlLite. how to generate an image made ​​in this consultation database sqllte?

  • Matt Ritter

    Works great in the emulator. Does not work on the actual device.

    Nice try though.

  • johan

    I have had great use of this tutorial. I was searching high and low for a good tutorial on data persistence with sqlite databases and database driven layout and i found it all in this tutorial. Big thanks!