<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Christophe Coenraets&#187; Christophe</title>
	<atom:link href="http://coenraets.org/blog/author/admin/feed/" rel="self" type="application/rss+xml" />
	<link>http://coenraets.org/blog</link>
	<description>Mobile, Cloud, HTML, JavaScript, Java, PHP, Flex</description>
	<lastBuildDate>Wed, 16 May 2012 19:36:45 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Simple Offline Data Synchronization for Mobile Web and PhoneGap Applications</title>
		<link>http://coenraets.org/blog/2012/05/simple-offline-data-synchronization-for-mobile-web-and-phonegap-applications/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=simple-offline-data-synchronization-for-mobile-web-and-phonegap-applications</link>
		<comments>http://coenraets.org/blog/2012/05/simple-offline-data-synchronization-for-mobile-web-and-phonegap-applications/#comments</comments>
		<pubDate>Wed, 16 May 2012 19:36:45 +0000</pubDate>
		<dc:creator>Christophe</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Backbone.js]]></category>
		<category><![CDATA[HTML 5]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Mobile]]></category>
		<category><![CDATA[PhoneGap]]></category>
		<category><![CDATA[Twitter Bootstrap]]></category>

		<guid isPermaLink="false">http://coenraets.org/blog/?p=3652</guid>
		<description><![CDATA[Being able to work offline is an expected feature of mobile applications. For data-driven applications, it means that you &#8212; the developer &#8212; will have to store (a subset of) your application data locally, and implement a data synchronization mechanism that keeps your local and server data in sync. In this article, I describe a [...]
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Simple Offline Data Synchronization for Mobile Web and PhoneGap Applications http://coenraets.org/blog/?p=3652" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F05%2Fsimple-offline-data-synchronization-for-mobile-web-and-phonegap-applications%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></description>
			<content:encoded><![CDATA[<p>Being able to work offline is an expected feature of mobile applications. For data-driven applications, it means that you &#8212; the developer &#8212; will have to store (a subset of) your application data locally, and implement a data synchronization mechanism that keeps your local and server data in sync.</p>
<p>In this article, I describe a simple data synchronization strategy that uses the device&#8217;s (or browser&#8217;s) SQLite database. The implementation currently leverages the Web SQL API (even though the W3C is no longer actively maintaining the spec) because both iOS and Android support it, but they don&#8217;t support IndexedDB, the official alternative. However, the API described below &#8212; getLastSync(), getChanges(), applyChanges() &#8212; defines a generic synchronization contract, and the solution can be expanded and made &#8220;pluggable&#8221;: You could create different synchronization objects, each providing a different implementation of these methods. You could then choose which object to plug in based on the context and the platform your application is running on.</p>
<h4>Try it in the Playground</h4>
<p><a href="http://coenraets.org/offline-sync/client-app"><img src="http://coenraets.org/blog/wp-content/uploads/2012/05/Screen-Shot-2012-05-16-at-21.jpg" alt="" title="Screen Shot 2012-05-16 at 2" width="640" height="760" class="aligncenter size-full wp-image-3734" /></a><br />
<span id="more-3652"></span><br />
Before looking at the code, you can try some offline syncing in this a hosted playground:</p>
<ol>
<li>Open the <a href="http://coenraets.org/offline-sync/client-app">Offline Client Playground</a> in Chrome or Safari (they both support Web SQL).</li>
<li>Click the Synchronize button.</li>
<li>Look at the log (the textarea in the middle of the screen): Because it&#8217;s the first time you use the application, all the employees have been downloaded from the server and inserted in your local SQLite database.</li>
<li>Clear the log, click the Synchronize button, and look at the log again: because you now have an up-to-date local version of the data, the server didn&#8217;t return any change and your local database remains unchanged.</li>
<li>In another tab, open the <a href="http://coenraets.org/offline-sync/server-app">Server Admin PlayGround</a>.</li>
<li>Modify an existing employee and click Save. (Don&#8217;t worry, it&#8217;s using your own session-based data set).</li>
<li>Go back to the Offline Client tab, click Synchronize, and notice that the server returned one change,  and that it was applied to your local database.</li>
<li>Go back to the Server Admin tab and modify (create, update, delete) other employees. Switch back to the Offline Client tab, click Synchronize, and see how these changes are applied to your local database.</li>
<li>You can also use the Resources Tab in the Chrome Developer Tools to inspect your local database.</li>
</ol>
<h4>Server API</h4>
<p>The only piece of infrastructure you need at the server side is an API that returns the items that have changed (created, updated, or deleted) since a specific moment in time expressed as a timestamp. </p>
<p>Here is the RESTful API call used in my application:</p>
<p><a href="http://coenraets.org/offline-sync/api/employees?modifiedSince=2012-03-01 10:20:56">http://coenraets.org/offline-sync/api/employees?modifiedSince=2012-03-01 10:20:56</a></p>
<p>The format of the data returned by the server is up to you and is part of the contract between the client and the server. In this application, the server returns the changes as an array of JSON objects. The server-side technology (RoR, PHP, Java, .NET, &#8230;) and database system (SQL, NoSQL, &#8230;) you use to generate the list of changes is also totally up to you. I provide a simple PHP implementation as part of the source code. That implementation manages a session-based data set that provides an isolated and transient playground. In a real-life application, you&#8217;d obviously get the data from some sort of database.</p>
<h5>Client API</h5>
<p>At the client side, our synchronization API consists of three methods.</p>
<h6>getLastSync()</h6>
<p>A method that returns a timestamp to be used as the query parameter for the next synchronization request. A common practice is to persist a timestamp after each synchronization request. But things can go wrong and the timestamp itself can get out-of-sync. I prefer to &#8220;recalculate&#8221; the lastSync timestamp before each synchronization request.   </p>
<pre class="brush: jscript; title: ; notranslate">
getLastSync: function(callback) {
    this.db.transaction(
        function(tx) {
            var sql = &quot;SELECT MAX(lastModified) as lastSync FROM employee&quot;;
            tx.executeSql(sql, this.txErrorHandler,
                function(tx, results) {
                    var lastSync = results.rows.item(0).lastSync;
                    callback(lastSync);
                }
            );
        }
    );
}
</pre>
<h6>getChanges()</h6>
<p>This is a wrapper around an Ajax call to the server-side API that returns the items that have changed (created, updated, or deleted) since a specific moment in time defined in the modifiedSince parameter. </p>
<pre class="brush: jscript; title: ; notranslate">
getChanges: function(syncURL, modifiedSince, callback) {

    $.ajax({
        url: syncURL,
        data: {modifiedSince: modifiedSince},
        dataType:&quot;json&quot;,
        success:function (changes) {
            callback(changes);
        },
        error: function(model, response) {
            alert(response.responseText);
        }
    });

}
</pre>
<h6>applyChanges()</h6>
<p>A method that persists the changes in your local data store. Notice that SQLite supports a convenient &#8220;INSERT OR REPLACE&#8221; statement so that you don&#8217;t have to determine if you are dealing with a new or existing employee before persisting it.</p>
<pre class="brush: jscript; title: ; notranslate">
applyChanges: function(employees, callback) {
    this.db.transaction(
        function(tx) {
            var l = employees.length;
            var sql =
                &quot;INSERT OR REPLACE INTO employee (id, firstName, lastName, title, officePhone, deleted, lastModified) &quot; +
                &quot;VALUES (?, ?, ?, ?, ?, ?, ?)&quot;;
            var e;
            for (var i = 0; i &lt; l; i++) {
                e = employees[i];
                var params = [e.id, e.firstName, e.lastName, e.title, e.officePhone, e.deleted, e.lastModified];
                tx.executeSql(sql, params);
            }
        },
        this.txErrorHandler,
        function(tx) {
            callback();
        }
    );
}
</pre>
<h4>Synchronization Logic</h4>
<p>With these server and client APIs in place, you can choreograph a data synchronization process as follows: </p>
<pre class="brush: jscript; title: ; notranslate">
sync: function(syncURL, callback) {

    var self = this;
    this.getLastSync(function(lastSync){
        self.getChanges(syncURL, lastSync,
            function (changes) {
                self.applyChanges(changes, callback);
            }
        );
    });

}
</pre>
<h4>Final Notes</h4>
<ul>
<li>This solution currently supports unidirectional (server to client) data synchronization. It could easily be expanded to support bidirectional synchronization.</li>
<li>This solution currently implements &#8220;logical deletes&#8221;: items are not physically deleted from the table, but the value of their &#8220;deleted&#8221; column is set to true.</li>
<li>As mentioned above, you could replace the Web SQL implementation with another data access strategy. For example, take a look at <a href="http://brian.io/lawnchair/">Brian Leroux&#8217; Lawnchair</a> for another local persistence solution.</li>
</ul>
<h4>Source Code</h4>
<p>The source code is available in <a href="https://github.com/ccoenraets/offline-sync">this GitHub repository</a>.</p>
<p>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal"  data-lang="en"  data-url="http://coenraets.org/blog/2012/05/simple-offline-data-synchronization-for-mobile-web-and-phonegap-applications/"></a>
<!-- End WP Socializer Plugin - Retweet Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - +1 Button -->
<g:plusone size="medium" href="http://coenraets.org/blog/2012/05/simple-offline-data-synchronization-for-mobile-web-and-phonegap-applications/" ></g:plusone>
<!-- End WP Socializer Plugin - +1 Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Facebook Button -->
<iframe src="http://www.facebook.com/plugins/like.php?&amp;href=http://coenraets.org/blog/2012/05/simple-offline-data-synchronization-for-mobile-web-and-phonegap-applications/&amp;layout=button_count&amp;show_faces=0&amp;width=80&amp;action=like&amp;font=arial&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:80px; height:21px;" allowTransparency="true"></iframe>
<!-- End WP Socializer Plugin - Facebook Button -->
</span>
</p>
<p>
<a href="https://twitter.com/ccoenraets" class="twitter-follow-button" data-show-count="false" data-lang="en" data-size="large">Follow @ccoenraets</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</p>
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Simple Offline Data Synchronization for Mobile Web and PhoneGap Applications http://coenraets.org/blog/?p=3652" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F05%2Fsimple-offline-data-synchronization-for-mobile-web-and-phonegap-applications%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></content:encoded>
			<wfw:commentRss>http://coenraets.org/blog/2012/05/simple-offline-data-synchronization-for-mobile-web-and-phonegap-applications/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Single-Page CRUD Application with Backbone.js and Twitter Bootstrap</title>
		<link>http://coenraets.org/blog/2012/05/single-page-crud-application-with-backbone-js-and-twitter-bootstrap/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=single-page-crud-application-with-backbone-js-and-twitter-bootstrap</link>
		<comments>http://coenraets.org/blog/2012/05/single-page-crud-application-with-backbone-js-and-twitter-bootstrap/#comments</comments>
		<pubDate>Thu, 03 May 2012 14:23:34 +0000</pubDate>
		<dc:creator>Christophe</dc:creator>
				<category><![CDATA[Backbone.js]]></category>
		<category><![CDATA[JQuery]]></category>
		<category><![CDATA[Twitter Bootstrap]]></category>

		<guid isPermaLink="false">http://coenraets.org/blog/?p=3575</guid>
		<description><![CDATA[A few weeks weeks ago, I posted a first Backbone.js and Twitter Bootstrap sample application. While interesting, &#8220;Employee Directory&#8221; is a read-only application. As such, it doesn’t show off the full power of Backbone&#8217;s models or the coolness of some of Bootstrap&#8217;s data entry features such as forms, validation, etc. To demonstrate these features, I [...]
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Single-Page CRUD Application with Backbone.js and Twitter Bootstrap http://coenraets.org/blog/?p=3575" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F05%2Fsingle-page-crud-application-with-backbone-js-and-twitter-bootstrap%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></description>
			<content:encoded><![CDATA[<p>A few weeks weeks ago, I <a href="http://coenraets.org/blog/2012/02/sample-app-with-backbone-js-and-twitter-bootstrap/">posted</a> a first <a href="http://documentcloud.github.com/backbone/">Backbone.js</a> and <a href="http://twitter.github.com/bootstrap/">Twitter Bootstrap</a> sample application. While interesting, &#8220;Employee Directory&#8221; is a read-only application. As such, it doesn’t show off the full power of Backbone&#8217;s models or the coolness of some of Bootstrap&#8217;s data entry features such as forms, validation, etc.</p>
<p>To demonstrate these features, I decided to revisit my <a href="http://coenraets.org/blog/2011/12/backbone-js-wine-cellar-tutorial-part-1-getting-started/">wine cellar application</a>, which was in need of a serious UI makeover.</p>
<p>You can run the application <a href="http://coenraets.org/backbone-cellar/bootstrap">here</a>.</p>
<p><a href="http://coenraets.org/backbone-cellar/bootstrap"><img src="http://coenraets.org/blog/wp-content/uploads/2012/05/cellar012.jpg" alt="" width="640" height="553" class="aligncenter size-full wp-image-3600" /></a></p>
<p><a href="http://coenraets.org/backbone-cellar/bootstrap"><img src="http://coenraets.org/blog/wp-content/uploads/2012/05/cellar021.jpg" alt="" title="cellar02" width="640" height="584"  class="aligncenter size-full wp-image-3603" /></a><br />
<span id="more-3575"></span><br />
This online version uses an in-memory datastore: all your changes will be lost the next time you start the application or hit your browser’s refresh button. The image upload feature is also disabled, but you can still drag an image from your file system and drop it  in the Wine Form, which is pretty cool (see the note about browser support below). The <a href="https://github.com/ccoenraets/backbone-cellar">source code</a> available on GitHub includes a persistent back-end (in addition to the in-memory datastore), and a fully functional implementation of the file upload feature.</p>
<h4>Code Highlights</h4>
<h6>Template Loader</h6>
<p>This application features a template loader that now uses jQuery “deferreds” to load the HTML templates more efficiently.</p>
<h6>Drag-and-Drop File Upload and HTML 5 File API</h6>
<p>To upload an image file for a Wine, drag a file from your file system and drop it in the image box inside the Wine Form: The image is automatically displayed inside the img tag. This is done using the HTML 5 File API and doesn’t require a server roundtrip. </p>
<p>NOTE: This feature only works in Chrome at this time. I will implement an alternative implementation in a future version of the application to provide a consistent behavior across browsers.</p>
<p>The image is uploaded to the server using Ajax (XHR) when you save the form: no page refresh or iframe hack.</p>
<h6>Paging</h6>
<p>Twitter Bootstrap provides easy markup and styles to create paginated lists. In this application, the Bootstrap markup and styles are “componentized” or “widgetized” into a Backbone View (Paginator), which adds the appropriate pagination behavior.</p>
<p>NOTE: In the current implementation of this application, the entire data set is always retrieved from the server and paging is provided for a cosmetic/layout reason. You could easily replace this implementation with a true paging strategy where pages are lazy-loaded from the server as needed.</p>
<h6>Forms</h6>
<p>Backbone Cellar also uses Twitter Bootstrap’s forms, which greatly help with form layouts (see WineView). </p>
<h6>Validation</h6>
<p>Twitter Bootstrap also provides simple markup and styles to highlight validation errors in forms. In Backbone Cellar, Bootstrap’s validation markup and styles are wired with validation rules defined in the Backbone model. Note that at this time, the application doesn’t use the Backbone model’s default validate() method, but custom validateItem() and validateAll() methods instead. </p>
<p>The application also uses other Twitter Bootstrap features including thumbnails, dropdowns, alerts, etc.</p>
<h4>Source Code</h4>
<p>The source code is available in the <a href="https://github.com/ccoenraets/backbone-cellar/bootstrap">bootstrap</a> folder of the <a href="https://github.com/ccoenraets/backbone-cellar">backbone-cellar</a> repository on GitHub.</p>
<h4>Disclaimer</h4>
<p>This is a sample application, not a production application. Some trade-offs were made to keep the code generic, simple and readable. In a real-life application, you should consider implementing a namespacing scheme to keep the global namespace clean, and other optimization techniques such as view and/or data caching.</p>
<p>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal"  data-lang="en"  data-url="http://coenraets.org/blog/2012/05/single-page-crud-application-with-backbone-js-and-twitter-bootstrap/"></a>
<!-- End WP Socializer Plugin - Retweet Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - +1 Button -->
<g:plusone size="medium" href="http://coenraets.org/blog/2012/05/single-page-crud-application-with-backbone-js-and-twitter-bootstrap/" ></g:plusone>
<!-- End WP Socializer Plugin - +1 Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Facebook Button -->
<iframe src="http://www.facebook.com/plugins/like.php?&amp;href=http://coenraets.org/blog/2012/05/single-page-crud-application-with-backbone-js-and-twitter-bootstrap/&amp;layout=button_count&amp;show_faces=0&amp;width=80&amp;action=like&amp;font=arial&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:80px; height:21px;" allowTransparency="true"></iframe>
<!-- End WP Socializer Plugin - Facebook Button -->
</span>
</p>
<p>
<a href="https://twitter.com/ccoenraets" class="twitter-follow-button" data-show-count="false" data-lang="en" data-size="large">Follow @ccoenraets</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</p>
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Single-Page CRUD Application with Backbone.js and Twitter Bootstrap http://coenraets.org/blog/?p=3575" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F05%2Fsingle-page-crud-application-with-backbone-js-and-twitter-bootstrap%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></content:encoded>
			<wfw:commentRss>http://coenraets.org/blog/2012/05/single-page-crud-application-with-backbone-js-and-twitter-bootstrap/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Building Mobile Apps with HTML and a Local Database</title>
		<link>http://coenraets.org/blog/2012/04/building-mobile-apps-with-html-and-a-local-database/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=building-mobile-apps-with-html-and-a-local-database</link>
		<comments>http://coenraets.org/blog/2012/04/building-mobile-apps-with-html-and-a-local-database/#comments</comments>
		<pubDate>Wed, 11 Apr 2012 16:03:22 +0000</pubDate>
		<dc:creator>Christophe</dc:creator>
				<category><![CDATA[Backbone.js]]></category>
		<category><![CDATA[HTML 5]]></category>
		<category><![CDATA[Mobile]]></category>
		<category><![CDATA[PhoneGap]]></category>

		<guid isPermaLink="false">http://coenraets.org/blog/?p=3474</guid>
		<description><![CDATA[After my recent post, Crafting Native Looking iOS Apps with HTML, a number of you asked for an offline version that would use a Local Database (instead of the simple in-memory store) and provide a mechanism to automatically keep the local database in sync with a server database. I&#8217;ll save automatic data synchronization strategies for [...]
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Building Mobile Apps with HTML and a Local Database http://coenraets.org/blog/?p=3474" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F04%2Fbuilding-mobile-apps-with-html-and-a-local-database%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></description>
			<content:encoded><![CDATA[<p><a href="http://coenraets.org/blog/wp-content/uploads/2012/04/empdirdb4.jpg"><img src="http://coenraets.org/blog/wp-content/uploads/2012/04/empdirdb4.jpg" alt="" title="empdirdb4" width="436" height="607" class="aligncenter size-full wp-image-3555" /></a></p>
<p>After my recent post, <a href="http://coenraets.org/blog/2012/03/crafting-native-looking-ios-apps-with-html-backbone-js-and-phonegap/">Crafting Native Looking iOS Apps with HTML</a>, a number of you asked for an offline version that would use a Local Database (instead of the simple in-memory store) and provide a mechanism to automatically keep the local database in sync with a server database.<br />
<span id="more-3474"></span><br />
I&#8217;ll save automatic data synchronization strategies for a future post, but here is the first step: an &#8220;offline&#8221; version of the application that uses the device&#8217;s or the browser&#8217;s local database as its data provider. This version still uses <a href="http://backbonejs.org/">Backbone.js</a> as its architectural framework. Backbone.js makes it easy to change its default data access mechanism (which assumes RESTful services). You just replace the default <strong>Backbone.sync</strong> implementation and provide your own data access logic: in this case, some local SQL logic.</p>
<h5>Web SQL vs IndexedDB</h5>
<p>As you probably know, there have been two competing database APIs for HTML. From the W3C web site:</p>
<ul>
<li>The <a href="http://dev.w3.org/html5/webdatabase/">Web SQL specification</a> defines an API for storing data in databases that can be queried using a variant of SQL. This specification is no longer in active maintenance and the Web Applications Working Group does not intend to maintain it further.</li>
<li>The <a href="http://www.w3.org/TR/IndexedDB/">Indexed Database specification</a> defines APIs for a database of records holding simple values and hierarchical objects. It is a working draft, and &#8220;work in progress&#8221;.</li>
</ul>
<p>Even though the W3C is no longer actively maintaining the spec, this application uses the Web SQL API because, as a mobile <em>app</em>, its two main target platforms are iOS and Android, which both currently support Web SQL but not IndexedDB. More detailed platform support information can be found on caniuse.com (<a href="http://caniuse.com/#feat=sql-storage">Web SQL</a> and <a href="http://caniuse.com/indexeddb">IndexedDB</a>).</p>
<p>Chrome, Safari, and Opera on the desktop also support Web SQL, which means that you can run the application in these browsers. Try it <a href="http://coenraets.org/backbone/directory/localdb">here</a>. For example, using the Chrome Developer Tools you could debug the application and inspect the database as shown in this screenshot:</p>
<p><a href="http://coenraets.org/blog/wp-content/uploads/2012/04/debugdb2.gif"><img src="http://coenraets.org/blog/wp-content/uploads/2012/04/debugdb2.gif" alt="" title="debugdb2" width="640" height="469" class="alignnone size-full wp-image-3516" /></a></p>
<p>Firefox and IE don&#8217;t support Web SQL. You could easily create an alternative version of EmployeeDAO (described below) that uses IndexedDB instead. You could also create a version of the application that uses either Web SQL or IndexedDB depending on the platform it&#8217;s running on.</p>
<h5>Code Highlights</h5>
<p>The source code is available in the <a href="https://github.com/ccoenraets/backbone-directory/tree/master/localdb">localdb</a> folder of the <a href="https://github.com/ccoenraets/backbone-directory">backbone-directory</a> repository on GitHub. Here is a quick walkthrough&#8230;</p>
<p>The data access logic is encapsulated in EmployeeDAO, which also has a “populate” function to populate the employee table with sample data.</p>
<pre class="brush: jscript; title: ; notranslate">
directory.dao.EmployeeDAO = function(db) {
    this.db = db;
};

_.extend(directory.dao.EmployeeDAO.prototype, {

    findByName: function(key, callback) {
        this.db.transaction(
            function(tx) {

                var sql = &quot;SELECT e.id, e.firstName, e.lastName, e.title, count(r.id) reportCount &quot; +
                    &quot;FROM employee e LEFT JOIN employee r ON r.managerId = e.id &quot; +
                    &quot;WHERE e.firstName || ' ' || e.lastName LIKE ? &quot; +
                    &quot;GROUP BY e.id ORDER BY e.lastName, e.firstName&quot;;

                tx.executeSql(sql, ['%' + key + '%'], function(tx, results) {
                    var len = results.rows.length,
                        employees = [],
                        i = 0;
                    for (; i &lt; len; i = i + 1) {
                        employees[i] = results.rows.item(i);
                    }
                    callback(employees);
                });
            },
            function(tx, error) {
                alert(&quot;Transaction Error: &quot; + error);
            }
        );
    },

    findById: function(id, callback) {
        // removed for brevity
    },

    findByManager: function(managerId, callback) {
        // removed for brevity
    },

    populate: function(callback) {
        // removed for brevity
    }
});
</pre>
<p>Models are annotated with a “dao” attribute to indicate which data object to use to access their underlying data.</p>
<pre class="brush: jscript; title: ; notranslate">
directory.models.Employee = Backbone.Model.extend({

    dao: directory.dao.EmployeeDAO,

});

directory.models.EmployeeCollection = Backbone.Collection.extend({

    dao: directory.dao.EmployeeDAO,

    model: directory.models.Employee,

});
</pre>
<p>With that infrastructure in place, you can then override Backbone.sync to access data from the local database instead of RESTful services:</p>
<pre class="brush: jscript; title: ; notranslate">
Backbone.sync = function(method, model, options) {

    var dao = new model.dao(directory.db);

    if (method === &quot;read&quot;) {
        if (model.id) {
            dao.findById(model.id, function(data) {
                options.success(data);
            });
        } else if (model.managerId) {
            dao.findByManager(model.managerId, function(data) {
                options.success(data);
            });
        }
        // removed for brevity
    }

};
</pre>
<h5>Source Code</h5>
<p>The source code is available in the <a href="https://github.com/ccoenraets/backbone-directory/tree/master/localdb">localdb</a> folder of the <a href="https://github.com/ccoenraets/backbone-directory">backbone-directory</a> repository on GitHub.</p>
<p>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal"  data-lang="en"  data-url="http://coenraets.org/blog/2012/04/building-mobile-apps-with-html-and-a-local-database/"></a>
<!-- End WP Socializer Plugin - Retweet Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - +1 Button -->
<g:plusone size="medium" href="http://coenraets.org/blog/2012/04/building-mobile-apps-with-html-and-a-local-database/" ></g:plusone>
<!-- End WP Socializer Plugin - +1 Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Facebook Button -->
<iframe src="http://www.facebook.com/plugins/like.php?&amp;href=http://coenraets.org/blog/2012/04/building-mobile-apps-with-html-and-a-local-database/&amp;layout=button_count&amp;show_faces=0&amp;width=80&amp;action=like&amp;font=arial&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:80px; height:21px;" allowTransparency="true"></iframe>
<!-- End WP Socializer Plugin - Facebook Button -->
</span>
</p>
<p>
<a href="https://twitter.com/ccoenraets" class="twitter-follow-button" data-show-count="false" data-lang="en" data-size="large">Follow @ccoenraets</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</p>
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Building Mobile Apps with HTML and a Local Database http://coenraets.org/blog/?p=3474" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F04%2Fbuilding-mobile-apps-with-html-and-a-local-database%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></content:encoded>
			<wfw:commentRss>http://coenraets.org/blog/2012/04/building-mobile-apps-with-html-and-a-local-database/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Appliness, a New Digital Magazine for Web Developers</title>
		<link>http://coenraets.org/blog/2012/03/appliness-a-new-digital-magazine-for-web-developers/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=appliness-a-new-digital-magazine-for-web-developers</link>
		<comments>http://coenraets.org/blog/2012/03/appliness-a-new-digital-magazine-for-web-developers/#comments</comments>
		<pubDate>Thu, 29 Mar 2012 14:42:12 +0000</pubDate>
		<dc:creator>Christophe</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://coenraets.org/blog/?p=3462</guid>
		<description><![CDATA[My multi-talented colleague Michael Chaize has been working hard on Appliness, a new Digital Magazine for Web developers. Appliness features interactive tutorials, video tutorials, news, application showcases, interviews, etc. Check out the video: Appliness is available on the iPad and Android tablets. If you have an iPad, you can get it from the &#8220;Newsstand&#8221;: http://itunes.apple.com/au/app/appliness/id510636049 [...]
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Appliness, a New Digital Magazine for Web Developers http://coenraets.org/blog/?p=3462" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F03%2Fappliness-a-new-digital-magazine-for-web-developers%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></description>
			<content:encoded><![CDATA[<p>My multi-talented colleague <a href="http://www.riagora.com/">Michael Chaize</a> has been working hard on <a href="http://www.appliness.com">Appliness</a>, a new Digital Magazine for Web developers. Appliness features interactive tutorials, video tutorials, news, application showcases, interviews, etc. </p>
<p>Check out the video:</p>
<p><iframe width="640" height="360" src="http://www.youtube.com/embed/ihbV09hi8cg" frameborder="0" allowfullscreen></iframe></p>
<p>Appliness is available on the iPad and Android tablets.<br />
<span id="more-3462"></span><br />
If you have an iPad, you can get it from the &#8220;Newsstand&#8221;: <a href="http://itunes.apple.com/au/app/appliness/id510636049">http://itunes.apple.com/au/app/appliness/id510636049</a></p>
<p>If you have an Android tablet, Appliness is available as a standalone application on Google Play: <a href="https://play.google.com/store/apps/details?id=com.appliness.applinessAndroid">https://play.google.com/store/apps/details?id=com.appliness.applinessAndroid</a></p>
<p>Appliness was created using the <a href="http://www.adobe.com/products/digital-publishing-suite-family.html">Adobe Digital Publishing Suite</a> (DPS).</p>
<p>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal"  data-lang="en"  data-url="http://coenraets.org/blog/2012/03/appliness-a-new-digital-magazine-for-web-developers/"></a>
<!-- End WP Socializer Plugin - Retweet Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - +1 Button -->
<g:plusone size="medium" href="http://coenraets.org/blog/2012/03/appliness-a-new-digital-magazine-for-web-developers/" ></g:plusone>
<!-- End WP Socializer Plugin - +1 Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Facebook Button -->
<iframe src="http://www.facebook.com/plugins/like.php?&amp;href=http://coenraets.org/blog/2012/03/appliness-a-new-digital-magazine-for-web-developers/&amp;layout=button_count&amp;show_faces=0&amp;width=80&amp;action=like&amp;font=arial&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:80px; height:21px;" allowTransparency="true"></iframe>
<!-- End WP Socializer Plugin - Facebook Button -->
</span>
</p>
<p>
<a href="https://twitter.com/ccoenraets" class="twitter-follow-button" data-show-count="false" data-lang="en" data-size="large">Follow @ccoenraets</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</p>
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Appliness, a New Digital Magazine for Web Developers http://coenraets.org/blog/?p=3462" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F03%2Fappliness-a-new-digital-magazine-for-web-developers%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></content:encoded>
			<wfw:commentRss>http://coenraets.org/blog/2012/03/appliness-a-new-digital-magazine-for-web-developers/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Crafting Native Looking iOS Apps with HTML, Backbone.js, and PhoneGap</title>
		<link>http://coenraets.org/blog/2012/03/crafting-native-looking-ios-apps-with-html-backbone-js-and-phonegap/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=crafting-native-looking-ios-apps-with-html-backbone-js-and-phonegap</link>
		<comments>http://coenraets.org/blog/2012/03/crafting-native-looking-ios-apps-with-html-backbone-js-and-phonegap/#comments</comments>
		<pubDate>Wed, 28 Mar 2012 18:00:04 +0000</pubDate>
		<dc:creator>Christophe</dc:creator>
				<category><![CDATA[Backbone.js]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[PhoneGap]]></category>

		<guid isPermaLink="false">http://coenraets.org/blog/?p=3359</guid>
		<description><![CDATA[If you just want to try the application in your browser, click here. The data is based on Dwight&#8217;s original org chart :) I&#8217;ve been blogging a lot about Backbone.js recently. Backbone.js is a lightweight architectural framework that brings structure to your Web applications. Backbone is not, however, a user interface framework that helps you [...]
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Crafting Native Looking iOS Apps with HTML, Backbone.js, and PhoneGap http://coenraets.org/blog/?p=3359" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F03%2Fcrafting-native-looking-ios-apps-with-html-backbone-js-and-phonegap%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></description>
			<content:encoded><![CDATA[<p>If you just want to try the application in your browser, click <a href="http://coenraets.org/backbone/directory/iphone">here</a>. The data is based on Dwight&#8217;s original <a href="http://www.nbc.com/The_Office/downloads/dunder_mifflin_org_chart.pdf">org chart</a> :)</p>
<p><a href="http://coenraets.org/backbone/directory/iphone"><img src="http://coenraets.org/blog/wp-content/uploads/2012/03/empdirios04.jpg" alt="" title="empdirios04" width="300" height="574" style="float: left;" /></a></p>
<p><a href="http://coenraets.org/backbone/directory/iphone"><img src="http://coenraets.org/blog/wp-content/uploads/2012/03/empdirios05.jpg" alt="" title="empdirios05" width="300" height="574" style="margin-top:-20px;"/></a></p>
<p>I&#8217;ve been blogging a lot about Backbone.js recently. Backbone.js is a lightweight architectural framework that brings structure to your Web applications. Backbone is not, however, a user interface framework that helps you with the way your application looks.</p>
<p><em>So, where do you turn to for help when you need to make your application look good? </em><br />
<span id="more-3359"></span><br />
For traditional web apps (delivered through a browser), Twitter Bootstrap can help (read <a href="http://coenraets.org/blog/2012/02/sample-app-with-backbone-js-and-twitter-bootstrap/">here</a>). But what about Mobile apps? I explored Backbone.js + jQuery Mobile <a href="http://coenraets.org/blog/2012/03/employee-directory-sample-app-with-backbone-js-and-jquery-mobile/">here</a>. Depending on what you are looking for, it may or may not be the right solution: jQM provides mobile skins, but they don&#8217;t look native. It&#8217;s also more of a full stack framework than a lightweight UI toolkit that you can easily layer on top of your app.  </p>
<p>The alternative to using an existing UI toolkit is to roll your own styles to make your application look and behave like a native app. Sounds easy enough, but when you consider all the details and want to achieve &#8220;pixel perfection&#8221;, it becomes a daunting task. </p>
<p>As I was getting ready to tackle the challenge, and build a new native looking version of my Employee Directory app, I came across <a href="http://cheeaun.com/blog/2012/03/how-i-built-hacker-news-mobile-web-app">this great blog post</a> by Chee Aun where he documents the process he went through to build his own Hacker News mobile app. His post is a real gem, and I ended up reusing a lot of the Hacker News app styles.</p>
<p>Compared to the Hacker News app, the Employee Directory page flow is more random. Here are a few examples:</p>
<ol>
<li>SearchPage -> EmployeePage -> ReportsPage -> EmployeePage -> &#8230; </li>
<li>SearchPage -> EmployeePage -> EmployeePage (manager) -> Reports -> &#8230; </li>
<li>SearchPage -> EmployeePage -> EmployeePage (manager) -> EmployeePage (manager&#8217;s manager) -> &#8230; </li>
</ol>
<p>As you can see, the page flow includes &#8220;same page transitions&#8221;, when the user navigates from one employee to his/her manager. To accommodate the Employee Directory page flow requirements, my Backbone.js infrastructure creates and destroys pages as needed with the appropriate slide-in/slide-out transitions. The implementation of these transitions was inspired by Wesley Hales&#8217; <a href="http://www.html5rocks.com/en/mobile/optimization-and-performance/">article</a>.</p>
<h5>PhoneGap</h5>
<p>Even though you can run this application in a browser (<a href="http://coenraets.org/backbone/directory/iphone">here</a>), I built it with the intention of packaging it as a native app with <a href="http://phonegap.com/">PhoneGap</a> so  that you could start it like any other app from your iPhone home screen. If you are not familiar with PhoneGap, I&#8217;ll provide more details on packaging this app as a native app in my next post.</p>
<h5>Source Code</h5>
<p>I updated the <a href="https://github.com/ccoenraets/backbone-directory">backbone-directory</a> GitHub repository to include this version: It is available in the <a href="https://github.com/ccoenraets/backbone-directory/tree/master/iphone">iphone</a> directory.<br />
<br/><br/></p>
<p>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal"  data-lang="en"  data-url="http://coenraets.org/blog/2012/03/crafting-native-looking-ios-apps-with-html-backbone-js-and-phonegap/"></a>
<!-- End WP Socializer Plugin - Retweet Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - +1 Button -->
<g:plusone size="medium" href="http://coenraets.org/blog/2012/03/crafting-native-looking-ios-apps-with-html-backbone-js-and-phonegap/" ></g:plusone>
<!-- End WP Socializer Plugin - +1 Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Facebook Button -->
<iframe src="http://www.facebook.com/plugins/like.php?&amp;href=http://coenraets.org/blog/2012/03/crafting-native-looking-ios-apps-with-html-backbone-js-and-phonegap/&amp;layout=button_count&amp;show_faces=0&amp;width=80&amp;action=like&amp;font=arial&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:80px; height:21px;" allowTransparency="true"></iframe>
<!-- End WP Socializer Plugin - Facebook Button -->
</span>
</p>
<p>
<a href="https://twitter.com/ccoenraets" class="twitter-follow-button" data-show-count="false" data-lang="en" data-size="large">Follow @ccoenraets</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</p>
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Crafting Native Looking iOS Apps with HTML, Backbone.js, and PhoneGap http://coenraets.org/blog/?p=3359" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F03%2Fcrafting-native-looking-ios-apps-with-html-backbone-js-and-phonegap%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></content:encoded>
			<wfw:commentRss>http://coenraets.org/blog/2012/03/crafting-native-looking-ios-apps-with-html-backbone-js-and-phonegap/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Employee Directory Sample App with Backbone.js and jQuery Mobile</title>
		<link>http://coenraets.org/blog/2012/03/employee-directory-sample-app-with-backbone-js-and-jquery-mobile/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=employee-directory-sample-app-with-backbone-js-and-jquery-mobile</link>
		<comments>http://coenraets.org/blog/2012/03/employee-directory-sample-app-with-backbone-js-and-jquery-mobile/#comments</comments>
		<pubDate>Thu, 08 Mar 2012 15:50:04 +0000</pubDate>
		<dc:creator>Christophe</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://coenraets.org/blog/?p=3313</guid>
		<description><![CDATA[Here is a mobile version of my Backbone.js Employee Directory application using jQuery Mobile as the UI toolkit. As described in my previous post, jQuery Mobile was (at least initially) intended as a full-stack framework as opposed to a pure UI toolkit like Twitter Bootstrap. As such, it overlaps with the Backbone.js infrastructure in some [...]
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Employee Directory Sample App with Backbone.js and jQuery Mobile http://coenraets.org/blog/?p=3313" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F03%2Femployee-directory-sample-app-with-backbone-js-and-jquery-mobile%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></description>
			<content:encoded><![CDATA[<p>Here is a mobile version of my Backbone.js Employee Directory application using jQuery Mobile as the UI toolkit.</p>
<p>As described in my <a href="http://coenraets.org/blog/2012/03/using-backbone-js-with-jquery-mobile/">previous post</a>, jQuery Mobile was (at least initially) intended as a full-stack framework as opposed to a pure UI toolkit like Twitter Bootstrap. As such, it overlaps with the Backbone.js infrastructure in some areas. In particular, the URL routing feature provided by both frameworks may clash. The approach used here is to disable the routing and navigation capabilities of jQuery Mobile, and essentially use it as a pure UI framework on top of Backbone.js. This approach and its caveats are described in my <a href="http://coenraets.org/blog/2012/03/using-backbone-js-with-jquery-mobile/">previous post</a>.</p>
<p><a href="http://coenraets.org/backbone/directory/jquerymobile"><img src="http://coenraets.org/blog/wp-content/uploads/2012/03/photo003.jpg" alt="" title="photo003" width="300" height="450" class="alignleft size-full wp-image-3337" /></a></p>
<p><a href="http://coenraets.org/backbone/directory/jquerymobile"><img src="http://coenraets.org/blog/wp-content/uploads/2012/03/photo001.jpg" alt="" title="photo001" width="300" height="450" class="alignnone size-full wp-image-3320" style="margin-top:-20px;" /></a><br />
<span id="more-3313"></span><br />
<a href="http://coenraets.org/backbone/directory/jquerymobile"><img src="http://coenraets.org/blog/wp-content/uploads/2012/03/photo002.jpg" alt="" title="photo002" width="300" height="450" class="alignleft size-full wp-image-3322"/></a></p>
<p><a href="http://coenraets.org/backbone/directory/jquerymobile"><img src="http://coenraets.org/blog/wp-content/uploads/2012/03/photo0041.jpg" alt="" title="photo004" width="300" height="450" class="alignnone size-full wp-image-3343" style="margin-top:-20px;" /></a></p>
<p>Click <a href="http://coenraets.org/backbone/directory/jquerymobile">here</a> to run the application.</p>
<p>A Backbone.js + Twitter Bootstrap version of this application is available <a href="http://coenraets.org/blog/2012/02/sample-app-with-backbone-js-and-twitter-bootstrap/">here</a>.</p>
<h5>Source Code</h5>
<p>I updated the <a href="https://github.com/ccoenraets/backbone-directory">backbone-directory</a> GitHub repository to include this version: It is available in the jquerymobile directory while the Twitter Bootstrap version is available in the web directory.</p>
<p>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal"  data-lang="en"  data-url="http://coenraets.org/blog/2012/03/employee-directory-sample-app-with-backbone-js-and-jquery-mobile/"></a>
<!-- End WP Socializer Plugin - Retweet Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - +1 Button -->
<g:plusone size="medium" href="http://coenraets.org/blog/2012/03/employee-directory-sample-app-with-backbone-js-and-jquery-mobile/" ></g:plusone>
<!-- End WP Socializer Plugin - +1 Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Facebook Button -->
<iframe src="http://www.facebook.com/plugins/like.php?&amp;href=http://coenraets.org/blog/2012/03/employee-directory-sample-app-with-backbone-js-and-jquery-mobile/&amp;layout=button_count&amp;show_faces=0&amp;width=80&amp;action=like&amp;font=arial&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:80px; height:21px;" allowTransparency="true"></iframe>
<!-- End WP Socializer Plugin - Facebook Button -->
</span>
</p>
<p>
<a href="https://twitter.com/ccoenraets" class="twitter-follow-button" data-show-count="false" data-lang="en" data-size="large">Follow @ccoenraets</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</p>
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Employee Directory Sample App with Backbone.js and jQuery Mobile http://coenraets.org/blog/?p=3313" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F03%2Femployee-directory-sample-app-with-backbone-js-and-jquery-mobile%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></content:encoded>
			<wfw:commentRss>http://coenraets.org/blog/2012/03/employee-directory-sample-app-with-backbone-js-and-jquery-mobile/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Using Backbone.js with jQuery Mobile</title>
		<link>http://coenraets.org/blog/2012/03/using-backbone-js-with-jquery-mobile/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=using-backbone-js-with-jquery-mobile</link>
		<comments>http://coenraets.org/blog/2012/03/using-backbone-js-with-jquery-mobile/#comments</comments>
		<pubDate>Mon, 05 Mar 2012 15:58:11 +0000</pubDate>
		<dc:creator>Christophe</dc:creator>
				<category><![CDATA[Backbone.js]]></category>
		<category><![CDATA[HTML 5]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[JQuery Mobile]]></category>
		<category><![CDATA[PhoneGap]]></category>

		<guid isPermaLink="false">http://coenraets.org/blog/?p=3215</guid>
		<description><![CDATA[Backbone.js is an architectural framework that helps you write well-structured Web applications. It is not, however, a user interface framework and it therefore doesn&#8217;t help you with the way your application looks. Backbone’s confined scope is a good thing: it&#8217;s lightweight, non-intrusive, not coupled to things you don&#8217;t need, and it lets you use the [...]
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Using Backbone.js with jQuery Mobile http://coenraets.org/blog/?p=3215" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F03%2Fusing-backbone-js-with-jquery-mobile%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></description>
			<content:encoded><![CDATA[<p>Backbone.js is an architectural framework that helps you write well-structured Web applications. It is not, however, a user interface framework and it therefore doesn&#8217;t help you with the way your application looks.</p>
<p>Backbone’s confined scope is a good thing: it&#8217;s lightweight, non-intrusive, not coupled to things you don&#8217;t need, and it lets you use the UI toolkit of your choice or simply roll your own styles and widgets. In my <a href="http://coenraets.org/blog/2012/02/sample-app-with-backbone-js-and-twitter-bootstrap/">previous post</a>, I demonstrated how to use Twitter Bootstrap on top of Backbone. </p>
<h5>Quest for a Mobile UI Toolkit</h5>
<p>After that post, I wanted to create a mobile version of the same application; a version that I could package with PhoneGap and that would look and behave like a native app. Twitter Bootstrap can probably be tweaked for that purpose as well, but I was looking for a UI toolkit dedicated to providing native looking controls and behaviors on mobile devices.<br />
<span id="more-3215"></span></p>
<h5>Another Way to Use jQuery Mobile</h5>
<p>jQuery Mobile (jQM) is one option that I’ve explored before (<a href="http://coenraets.org/blog/2011/10/sample-application-with-jquery-mobile-and-phonegap/">here</a> and <a href="http://coenraets.org/blog/2011/11/jquery-mobile-getting-started-application/">here</a>), but it fits more in the category of full-stack frameworks that tie together architectural structure and UI controls and behaviors. <a href="http://johnbender.us/">John Bender</a>, my colleague at Adobe and member of the jQuery Mobile team, recently pointed out to me that you can disable the routing and navigation capabilities of jQM, and essentially use it as a pure UI framework on top of other architectural frameworks like Backbone.js.</p>
<h5>Sample Application</h5>
<p>I ended up spending a decent amount of time trying different things to get the two frameworks to play well together without stepping on each other. To save you some headaches if you are trying to do the same, I put together a simple application with the basic setup to combine Backbone (for the application structure and “routing”) and jQuery Mobile (for its styles and widgets).</p>
<p>NOTE: Another approach would be to use jQM’s “routing” instead of Backbone’s. Ben Nolan has an example of this approach <a href="http://bennolan.com/2010/11/24/backbone-jquery-demo.html">here</a>. I prefer to use Backbone’s routing because I find it more flexible and less “page-centric”.</p>
<p>Here is the app:</p>
<p><iframe src="/backbone/jquerymobile" width="450" height="600" frameborder="0"></iframe></p>
<p>Click <a href="http://coenraets.org/backbone/jquerymobile">here</a> to run the application in a separate window. The source code is available in <a href="https://github.com/ccoenraets/backbone-jquerymobile">this GitHub repository</a>.</p>
<h5>How it works</h5>
<p>The key to this approach is to disable jQuery Mobile&#8217;s &#8220;routing&#8221;: In other words, you need to tell jQuery Mobile not to handle links, hash tag changes, and so on. I isolated that code in jqm-config.js:</p>
<pre class="brush: jscript; title: ; notranslate">
$(document).bind(&quot;mobileinit&quot;, function () {
    $.mobile.ajaxEnabled = false;
    $.mobile.linkBindingEnabled = false;
    $.mobile.hashListeningEnabled = false;
    $.mobile.pushStateEnabled = false;
});
</pre>
<p>If jQuery Mobile is not in charge of page navigation, you also have to manually remove the pages from the DOM when they are not used anymore. Here is one way to do it: </p>
<pre class="brush: jscript; title: ; notranslate">
$('div[data-role=&quot;page&quot;]').live('pagehide', function (event, ui) {
    $(event.currentTarget).remove();
});
</pre>
<p>With this configuration in place, you use Backbone&#8217;s routing as usual:</p>
<pre class="brush: jscript; title: ; notranslate">
var AppRouter = Backbone.Router.extend({

    routes:{
        &quot;&quot;:&quot;home&quot;,
        &quot;page1&quot;:&quot;page1&quot;,
        &quot;page2&quot;:&quot;page2&quot;
    },

    home:function () {
        this.changePage(new HomeView());
    },

    page1:function () {
        this.changePage(new Page1View());
    },

    page2:function () {
        this.changePage(new Page2View());
    },

    changePage:function (page) {
        $(page.el).attr('data-role', 'page');
        page.render();
        $('body').append($(page.el));
        $.mobile.changePage($(page.el), {changeHash:false});
    }

});
</pre>
<h5>Is this the right stack?</h5>
<p>I like the idea of a lightweight architectural framework combined with a UI toolkit. Backbone + Twitter Bootstrap felt right because the two frameworks have different areas of concern and complement each other very well. I was happy to see you could decouple jQM from its navigation infrastructure. However, that&#8217;s probably not the main &#8220;design center&#8221; at this point. I think it would be interesting for jQM to focus on that utilization scenario as well. At the end of the day, frameworks are often a matter of personal preferences, and not all applications are equal. So try it, see if it works for you, and share your experience. What UI toolkit are you using?</p>
<h5>Source Code</h5>
<p>The source code is available in <a href="https://github.com/ccoenraets/backbone-jquerymobile">this repository</a> on GitHub.</p>
<h5>A More Real-Life Application</h5>
<p>In my next post, I&#8217;ll share a Backbone.js + jQuery Mobile version of the Employee Directory application first explored with Backbone.js + Twitter Bootstrap.</p>
<p>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal"  data-lang="en"  data-url="http://coenraets.org/blog/2012/03/using-backbone-js-with-jquery-mobile/"></a>
<!-- End WP Socializer Plugin - Retweet Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - +1 Button -->
<g:plusone size="medium" href="http://coenraets.org/blog/2012/03/using-backbone-js-with-jquery-mobile/" ></g:plusone>
<!-- End WP Socializer Plugin - +1 Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Facebook Button -->
<iframe src="http://www.facebook.com/plugins/like.php?&amp;href=http://coenraets.org/blog/2012/03/using-backbone-js-with-jquery-mobile/&amp;layout=button_count&amp;show_faces=0&amp;width=80&amp;action=like&amp;font=arial&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:80px; height:21px;" allowTransparency="true"></iframe>
<!-- End WP Socializer Plugin - Facebook Button -->
</span>
</p>
<p>
<a href="https://twitter.com/ccoenraets" class="twitter-follow-button" data-show-count="false" data-lang="en" data-size="large">Follow @ccoenraets</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</p>
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Using Backbone.js with jQuery Mobile http://coenraets.org/blog/?p=3215" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F03%2Fusing-backbone-js-with-jquery-mobile%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></content:encoded>
			<wfw:commentRss>http://coenraets.org/blog/2012/03/using-backbone-js-with-jquery-mobile/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>Sample App with Backbone.js and Twitter Bootstrap</title>
		<link>http://coenraets.org/blog/2012/02/sample-app-with-backbone-js-and-twitter-bootstrap/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=sample-app-with-backbone-js-and-twitter-bootstrap</link>
		<comments>http://coenraets.org/blog/2012/02/sample-app-with-backbone-js-and-twitter-bootstrap/#comments</comments>
		<pubDate>Mon, 13 Feb 2012 18:51:18 +0000</pubDate>
		<dc:creator>Christophe</dc:creator>
				<category><![CDATA[Backbone.js]]></category>
		<category><![CDATA[HTML 5]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Twitter Bootstrap]]></category>

		<guid isPermaLink="false">http://coenraets.org/blog/?p=3125</guid>
		<description><![CDATA[Backbone.js is a lightweight JavaScript framework that provides the basic infrastructure (Model, Collection, View, and Router classes) to bring structure to your Web applications. Twitter Bootstrap is a UI toolkit that provides simple and flexible HTML, CSS, and Javascript to implement popular user interface components and interactions. In other words, Backbone.js and Twitter Bootstrap focus [...]
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Sample App with Backbone.js and Twitter Bootstrap http://coenraets.org/blog/?p=3125" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F02%2Fsample-app-with-backbone-js-and-twitter-bootstrap%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></description>
			<content:encoded><![CDATA[<p><a href="http://documentcloud.github.com/backbone/">Backbone.js</a> is a lightweight JavaScript framework that provides the basic infrastructure (Model, Collection, View, and Router classes) to bring structure to your Web applications. </p>
<p><a href="http://twitter.github.com/bootstrap/index.html">Twitter Bootstrap</a> is a UI toolkit that provides simple and flexible HTML, CSS, and Javascript to implement popular user interface components and interactions.   </p>
<p>In other words, Backbone.js and Twitter Bootstrap focus on different areas of your application: core architecture and user interface respectively. Because of their well-defined and non-overlapping scope, Backbone.js and Twitter Bootstrap work well together. In general, I find a lightweight architectural framework and a UI toolkit to be a powerful combination, and an interesting alternative to full-stack frameworks: it gives you the flexibility to choose the library you like (if any) in the respective areas of your application.</p>
<h4>The Sample Application</h4>
<p>To give this combination a try, I put together a new sample application that uses Backbone.js to organize the code, and Twitter Bootstrap to organize the UI. The application is an Employee Directory that allows you to look for employees by name, view the details of an employee, and navigate up and down the Org Chart by clicking the employee&#8217;s manager or any of his/her direct reports.</p>
<p>You can run the application <a href="http://coenraets.org/directory">here</a>.</p>
<p><a href="http://coenraets.org/blog/wp-content/uploads/2012/02/directory1.gif"><img src="http://coenraets.org/blog/wp-content/uploads/2012/02/directory1.gif" alt="" title="directory1" width="640" height="526" class="aligncenter size-full wp-image-3197" /></a></p>
<p><a href="http://coenraets.org/blog/wp-content/uploads/2012/02/directory2.gif"><img src="http://coenraets.org/blog/wp-content/uploads/2012/02/directory2.gif" alt="" title="directory2" width="640" height="526" class="aligncenter size-full wp-image-3200" /></a><br />
<span id="more-3125"></span><br />
Backbone Directory is a single page application: index.html is essentially empty. Views are injected into and removed from the DOM as needed. Even though it is a single page application, the Backbone.js Router makes it easy to keep the different states of the app &#8220;bookmarkable&#8221; and &#8220;deep-linkable&#8221;.</p>
<h4>Twitter Bootstrap highlights</h4>
<p>&#8220;Backbone Directory&#8221; uses a number of the Twitter Bootstrap styles, components, and interactions: the 12-column grid with nested columns, a &#8220;Navbar&#8221;, a &#8220;Search Form&#8221; with dropdown,  the dropdown plugin, the Glyphicons icons, Info and Warning alerts, a &#8220;Well&#8221;, etc.</p>
<h4>Backbone.js highlights</h4>
<p>If you are new to Backbone.js, you may want to start with the tutorial (<a href="http://coenraets.org/blog/2011/12/backbone-js-wine-cellar-tutorial-part-1-getting-started/">part 1</a>, <a href="http://coenraets.org/blog/2011/12/backbone-js-wine-cellar-tutorial-part-2-crud/">part 2</a>, <a href="http://coenraets.org/blog/2011/12/backbone-js-wine-cellar-tutorial-part-3-deep-linking-and-application-states/">part 3</a>, and <a href="http://coenraets.org/blog/2012/01/backbone-js-lessons-learned-and-improved-sample-app/">postface</a>) I blogged recently. &#8220;Backbone Directory&#8221; includes some interesting elements not covered in the tutorial:</p>
<ul>
<li><strong>One-to-Many association.</strong> A one-to-many (Manager-to-Employees) association is defined in the Employee model (model/employeemodel.js) as a collection of employees (the direct reports). That collection is lazily fetched in the render() function of EmployeeFullView (view/employeedetails.js).</li>
<li><strong>Composite View.</strong> EmployeeFullView (views/employeedetails.js) is an example of a composite view. Its render() function instantiates two subviews: EmployeeView and EmployeeListView (to display the employee&#8217;s direct reports).
</ul>
<h4>Source Code</h4>
<p>The source code is available in <a href="https://github.com/ccoenraets/backbone-directory">this repository</a> on GitHub.</p>
<p>Your feedback and comments are appreciated.</p>
<p>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal"  data-lang="en"  data-url="http://coenraets.org/blog/2012/02/sample-app-with-backbone-js-and-twitter-bootstrap/"></a>
<!-- End WP Socializer Plugin - Retweet Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - +1 Button -->
<g:plusone size="medium" href="http://coenraets.org/blog/2012/02/sample-app-with-backbone-js-and-twitter-bootstrap/" ></g:plusone>
<!-- End WP Socializer Plugin - +1 Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Facebook Button -->
<iframe src="http://www.facebook.com/plugins/like.php?&amp;href=http://coenraets.org/blog/2012/02/sample-app-with-backbone-js-and-twitter-bootstrap/&amp;layout=button_count&amp;show_faces=0&amp;width=80&amp;action=like&amp;font=arial&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:80px; height:21px;" allowTransparency="true"></iframe>
<!-- End WP Socializer Plugin - Facebook Button -->
</span>
</p>
<p>
<a href="https://twitter.com/ccoenraets" class="twitter-follow-button" data-show-count="false" data-lang="en" data-size="large">Follow @ccoenraets</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</p>
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Sample App with Backbone.js and Twitter Bootstrap http://coenraets.org/blog/?p=3125" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F02%2Fsample-app-with-backbone-js-and-twitter-bootstrap%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></content:encoded>
			<wfw:commentRss>http://coenraets.org/blog/2012/02/sample-app-with-backbone-js-and-twitter-bootstrap/feed/</wfw:commentRss>
		<slash:comments>48</slash:comments>
		</item>
		<item>
		<title>Sample Mobile App with Backbone.js, PhoneGap, and a Local Database</title>
		<link>http://coenraets.org/blog/2012/02/sample-mobile-app-with-backbone-js-phonegap-and-a-local-database/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=sample-mobile-app-with-backbone-js-phonegap-and-a-local-database</link>
		<comments>http://coenraets.org/blog/2012/02/sample-mobile-app-with-backbone-js-phonegap-and-a-local-database/#comments</comments>
		<pubDate>Tue, 07 Feb 2012 16:29:39 +0000</pubDate>
		<dc:creator>Christophe</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Backbone.js]]></category>
		<category><![CDATA[Frameworks]]></category>
		<category><![CDATA[HTML 5]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PhoneGap]]></category>

		<guid isPermaLink="false">http://coenraets.org/blog/?p=3044</guid>
		<description><![CDATA[In my previous post, I shared a simple Wine Cellar application built with Backbone.js and packaged as a mobile app with PhoneGap. That version of the application gets its data from a set of RESTful services, which means that you can only use it while online. In this post, we explore an offline version of [...]
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Sample Mobile App with Backbone.js, PhoneGap, and a Local Database http://coenraets.org/blog/?p=3044" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F02%2Fsample-mobile-app-with-backbone-js-phonegap-and-a-local-database%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></description>
			<content:encoded><![CDATA[<p>In my <a href="http://coenraets.org/blog/2012/02/sample-mobile-app-with-backbone-js-and-phonegap/">previous post</a>, I shared a simple Wine Cellar application built with <a href="http://documentcloud.github.com/backbone/">Backbone.js</a> and packaged as a mobile app with <a href="http://phonegap.com/">PhoneGap</a>. That version of the application gets its data from a set of RESTful services, which means that you can only use it while online.</p>
<p>In this post, we explore an offline version of the same application: this new version gets its data from your device&#8217;s local database using the PhoneGap SQL database API. </p>
<p>By default, Backbone.js Models access their data using RESTful services defined by the Models &#8220;url&#8221; and &#8220;urlRoot&#8221; attributes. But Backbone.js also makes it easy and elegant to change your Models data access mechanism: All you have to do is override the Backbone.sync method. From the Backbone.js <a href="http://documentcloud.github.com/backbone/#Sync">documentation</a>:</p>
<blockquote><p>Backbone.sync is the function that Backbone calls every time it attempts to read or save a model to the server. By default, it uses (jQuery/Zepto).ajax to make a RESTful JSON request. You can override it in order to use a different persistence strategy, such as WebSockets, XML transport, or Local Storage.</p></blockquote>
<p>The documentation includes a <a href="http://documentcloud.github.com/backbone/docs/backbone-localstorage.html">useful example</a> that shows how to replace the default Backbone.sync implementation with localStorage-based persistence where models are saved into JSON objects.  </p>
<p>In our application, we will replace the default Backbone.sync implementation with SQL-based persistence using the device&#8217;s local database.<br />
<span id="more-3044"></span><br />
First, we create a data access object that encapsulates the logic to read (find), create, update, and delete wines. This simple application is read-only and only needs to retrieve collections. So, I only implemented the findAll() method and stubbed the other methods (find, create, update, destroy). WineDAO also has a &#8220;populate&#8221; function to populate the wine table with sample data. </p>
<pre class="brush: jscript; title: ; notranslate">
window.WineDAO = function (db) {
    this.db = db;
};

_.extend(window.WineDAO.prototype, {

    findAll:function (callback) {
        this.db.transaction(
            function (tx) {
                var sql = &quot;SELECT * FROM wine ORDER BY name&quot;;
                tx.executeSql(sql, [], function (tx, results) {
                    var len = results.rows.length;
                    var wines = [];
                    for (var i = 0; i &lt; len; i++) {
                        wines[i] = results.rows.item(i);
                    }
                    callback(wines);
                });
            },
            function (tx, error) {
                alert(&quot;Transaction Error: &quot; + error);
            }
        );
    },

    create:function (model, callback) {
//        TODO: Implement
    },

    update:function (model, callback) {
//        TODO: Implement
    },

    destroy:function (model, callback) {
//        TODO: Implement
    },

    find:function (model, callback) {
//        TODO: Implement
    },

//  Populate Wine table with sample data
    populate:function (callback) {
        this.db.transaction(
            function (tx) {
                console.log('Dropping WINE table');
                tx.executeSql('DROP TABLE IF EXISTS wine');
                var sql =
                    &quot;CREATE TABLE IF NOT EXISTS wine ( &quot; +
                        &quot;id INTEGER PRIMARY KEY AUTOINCREMENT, &quot; +
                        &quot;name VARCHAR(50), &quot; +
                        &quot;year VARCHAR(50), &quot; +
                        &quot;grapes VARCHAR(50), &quot; +
                        &quot;country VARCHAR(50), &quot; +
                        &quot;region VARCHAR(50), &quot; +
                        &quot;description TEXT, &quot; +
                        &quot;picture VARCHAR(200))&quot;;
                console.log('Creating WINE table');
                tx.executeSql(sql);
                console.log('Inserting wines');
                tx.executeSql(&quot;INSERT INTO wine VALUES (1,'CHATEAU DE SAINT COSME','2009','Grenache / Syrah','France','Southern Rhone / Gigondas','The aromas of fruit and spice give one a hint of the light drinkability of this lovely wine, which makes an excellent complement to fish dishes.','saint_cosme.jpg')&quot;);
                tx.executeSql(&quot;INSERT INTO wine VALUES (2,'LAN RIOJA CRIANZA','2006','Tempranillo','Spain','Rioja','A resurgence of interest in boutique vineyards has opened the door for this excellent foray into the dessert wine market. Light and bouncy, with a hint of black truffle, this wine will not fail to tickle the taste buds.','lan_rioja.jpg')&quot;);
                tx.executeSql(&quot;INSERT INTO wine VALUES (3,'MARGERUM SYBARITE','2010','Sauvignon Blanc','USA','California Central Cosat','The cache of a fine Cabernet in ones wine cellar can now be replaced with a childishly playful wine bubbling over with tempting tastes of black cherry and licorice. This is a taste sure to transport you back in time.','margerum.jpg')&quot;);
                tx.executeSql(&quot;INSERT INTO wine VALUES (4,'OWEN ROE \&quot;EX UMBRIS\&quot;','2009','Syrah','USA','Washington','A one-two punch of black pepper and jalapeno will send your senses reeling, as the orange essence snaps you back to reality. Do not miss this award-winning taste sensation.','ex_umbris.jpg')&quot;);
                tx.executeSql(&quot;INSERT INTO wine VALUES (5,'REX HILL','2009','Pinot Noir','USA','Oregon','One cannot doubt that this will be the wine served at the Hollywood award shows, because it has undeniable star power. Be the first to catch the debut that everyone will be talking about tomorrow.','rex_hill.jpg')&quot;);
                tx.executeSql(&quot;INSERT INTO wine VALUES (6,'VITICCIO CLASSICO RISERVA','2007','Sangiovese Merlot','Italy','Tuscany','Though soft and rounded in texture, the body of this wine is full and rich and oh-so-appealing. This delivery is even more impressive when one takes note of the tender tannins that leave the taste buds wholly satisfied.','viticcio.jpg')&quot;);
                tx.executeSql(&quot;INSERT INTO wine VALUES (7,'CHATEAU LE DOYENNE','2005','Merlot','France','Bordeaux','Though dense and chewy, this wine does not overpower with its finely balanced depth and structure. It is a truly luxurious experience for the senses.','le_doyenne.jpg')&quot;);
                tx.executeSql(&quot;INSERT INTO wine VALUES (8,'DOMAINE DU BOUSCAT','2009','Merlot','France','Bordeaux','The light golden color of this wine belies the bright flavor it holds. A true summer wine, it begs for a picnic lunch in a sun-soaked vineyard.','bouscat.jpg')&quot;);
                tx.executeSql(&quot;INSERT INTO wine VALUES (9,'BLOCK NINE','2009','Pinot Noir','USA','California','With hints of ginger and spice, this wine makes an excellent complement to light appetizer and dessert fare for a holiday gathering.','block_nine.jpg')&quot;);
                tx.executeSql(&quot;INSERT INTO wine VALUES (10,'DOMAINE SERENE','2007','Pinot Noir','USA','Oregon','Though subtle in its complexities, this wine is sure to please a wide range of enthusiasts. Notes of pomegranate will delight as the nutty finish completes the picture of a fine sipping experience.','domaine_serene.jpg')&quot;);
                tx.executeSql(&quot;INSERT INTO wine VALUES (11,'BODEGA LURTON','2011','Pinot Gris','Argentina','Mendoza','Solid notes of black currant blended with a light citrus make this wine an easy pour for varied palates.','bodega_lurton.jpg')&quot;);
                tx.executeSql(&quot;INSERT INTO wine VALUES (12,'LES MORIZOTTES','2009','Chardonnay','France','Burgundy','Breaking the mold of the classics, this offering will surprise and undoubtedly get tongues wagging with the hints of coffee and tobacco in perfect alignment with more traditional notes. Breaking the mold of the classics, this offering will surprise and undoubtedly get tongues wagging with the hints of coffee and tobacco in perfect alignment with more traditional notes. Sure to please the late-night crowd with the slight jolt of adrenaline it brings.','morizottes.jpg')&quot;);
            },
            function (tx, error) {
                alert('Transaction error ' + error);
            },
            function (tx) {
                callback();
            }
        );
    }
});
</pre>
<p>I then added a &#8220;dao&#8221; attribute to the Models to specify which data object should be used to access their underlying data.</p>
<pre class="brush: jscript; title: ; notranslate">
window.Wine = Backbone.Model.extend({
	urlRoot: &quot;http://coenraets.org/backbone-cellar/part1/api/wines&quot;,
    dao: WineDAO
});

window.WineCollection = Backbone.Collection.extend({
	model: Wine,
	url: &quot;http://coenraets.org/backbone-cellar/part1/api/wines&quot;,
    dao: WineDAO
});
</pre>
<p>NOTE: The application doesn&#8217;t need the url and urlRoot attributes anymore, but you can imagine a more sophisticated version of the app that would work with the RESTful services while online and fall back to the local database while offline. </p>
<p>With that infrastructure in place, we can now override Backbone.sync as follows:</p>
<pre class="brush: jscript; title: ; notranslate">
Backbone.sync = function (method, model, options) {

    var dao = new model.dao(window.db);

    switch (method) {
        case &quot;read&quot;:
            if (model.id)
                dao.find(model, function (data) {
                    options.success(data);
                });
            else
                dao.findAll(function (data) {
                    options.success(data);
                });
            break;
        case &quot;create&quot;:
            dao.create(model, function (data) {
                options.success(data);
            });
            break;
        case &quot;update&quot;:
            dao.update(model, function (data) {
                options.success(data);
            });
            break;
        case &quot;delete&quot;:
            dao.destroy(model, function (data) {
                options.success(data);
            });
            break;
    }

};
</pre>
<p>And finally, we need to open the database (and populate it with sample data) at startup:</p>
<pre class="brush: jscript; title: ; notranslate">
window.startApp = function () {
    var self = this;
    window.db = window.openDatabase(&quot;WineCellar&quot;, &quot;1.0&quot;, &quot;WineCellar Demo DB&quot;, 200000);
    var wineDAO = new WineDAO(self.db);
    wineDAO.populate(function () {
        this.templateLoader.load(['wine-list', 'wine-details', 'wine-list-item'], function () {
            self.app = new AppRouter();
            Backbone.history.start();
        });
    })
}
</pre>
<h4>Download</h4>
<p>I added this version of the application to the backbone-cellar GitHub repository <a href="https://github.com/ccoenraets/backbone-cellar/tree/master/mobile/offline">here</a>. </p>
<p>NOTE: You will have to create a PhoneGap project (see <a href="http://phonegap.com/start">documentation</a> here) or use the <a href="https://build.phonegap.com/">hosted build service</a> to package the code as a native app for different mobile platforms.</p>
<p>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal"  data-lang="en"  data-url="http://coenraets.org/blog/2012/02/sample-mobile-app-with-backbone-js-phonegap-and-a-local-database/"></a>
<!-- End WP Socializer Plugin - Retweet Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - +1 Button -->
<g:plusone size="medium" href="http://coenraets.org/blog/2012/02/sample-mobile-app-with-backbone-js-phonegap-and-a-local-database/" ></g:plusone>
<!-- End WP Socializer Plugin - +1 Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Facebook Button -->
<iframe src="http://www.facebook.com/plugins/like.php?&amp;href=http://coenraets.org/blog/2012/02/sample-mobile-app-with-backbone-js-phonegap-and-a-local-database/&amp;layout=button_count&amp;show_faces=0&amp;width=80&amp;action=like&amp;font=arial&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:80px; height:21px;" allowTransparency="true"></iframe>
<!-- End WP Socializer Plugin - Facebook Button -->
</span>
</p>
<p>
<a href="https://twitter.com/ccoenraets" class="twitter-follow-button" data-show-count="false" data-lang="en" data-size="large">Follow @ccoenraets</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</p>
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Sample Mobile App with Backbone.js, PhoneGap, and a Local Database http://coenraets.org/blog/?p=3044" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F02%2Fsample-mobile-app-with-backbone-js-phonegap-and-a-local-database%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></content:encoded>
			<wfw:commentRss>http://coenraets.org/blog/2012/02/sample-mobile-app-with-backbone-js-phonegap-and-a-local-database/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Sample Mobile App with Backbone.js and PhoneGap</title>
		<link>http://coenraets.org/blog/2012/02/sample-mobile-app-with-backbone-js-and-phonegap/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=sample-mobile-app-with-backbone-js-and-phonegap</link>
		<comments>http://coenraets.org/blog/2012/02/sample-mobile-app-with-backbone-js-and-phonegap/#comments</comments>
		<pubDate>Mon, 06 Feb 2012 17:27:53 +0000</pubDate>
		<dc:creator>Christophe</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Backbone.js]]></category>
		<category><![CDATA[HTML 5]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[PhoneGap]]></category>

		<guid isPermaLink="false">http://coenraets.org/blog/?p=2987</guid>
		<description><![CDATA[I recently blogged a tutorial (part 1, part 2, part 3, and postface) that takes you through the process of building a CRUD application using HTML and the Backbone.js framework. The application used in this tutorial is a Wine Cellar management app, and I thought it would be fun to create a Mobile version using [...]
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Sample Mobile App with Backbone.js and PhoneGap http://coenraets.org/blog/?p=2987" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F02%2Fsample-mobile-app-with-backbone-js-and-phonegap%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></description>
			<content:encoded><![CDATA[<p>I recently blogged a tutorial (<a href="http://coenraets.org/blog/2011/12/backbone-js-wine-cellar-tutorial-part-1-getting-started/">part 1</a>, <a href="http://coenraets.org/blog/2011/12/backbone-js-wine-cellar-tutorial-part-2-crud/">part 2</a>, <a href="http://coenraets.org/blog/2011/12/backbone-js-wine-cellar-tutorial-part-3-deep-linking-and-application-states/">part 3</a>, and <a href="http://coenraets.org/blog/2012/01/backbone-js-lessons-learned-and-improved-sample-app/">postface</a>) that takes you through the process of building a CRUD application using HTML and the <a href="http://documentcloud.github.com/backbone/">Backbone.js</a> framework. The application used in this tutorial is a Wine Cellar management app, and I thought it would be fun to create a Mobile version using <a href="http://phonegap.com/">PhoneGap</a>.</p>
<p>PhoneGap &#8212; if you are not familiar with it &#8212; is an open source platform that allows you develop cross-platform Mobile applications using HTML and JavaScript. Specifically, it allows you to:</p>
<ol>
<li>Package an HTML application as a native app on all the key mobile platforms (iOS, Android, BlackBerry, Windows Phone, WebOS, Symbian, Bada).</li>
<li>Access your device capabilities (Camera, GPS, database, accelerometer, etc) using a cross-platform JavaScript API.</li>
</ol>
<p>Backbone.js is a great framework to give structure to your web application regardless of where it is running: in a traditional Web Browser, or as an app packaged with PhoneGap. </p>
<p>So, here is the app&#8230;<br />
<span id="more-2987"></span></p>
<p>
<img src="http://coenraets.org/blog/wp-content/uploads/2012/02/wine4.jpg" alt="" title="wine4" width="300" height="450" style="float: left;" /></p>
<p><img src="http://coenraets.org/blog/wp-content/uploads/2012/02/wine3.jpg" alt="" title="wine3" width="300" height="450" style="float: right;margin-top:-20px;"/>
</p>
<p style="clear: both;">&nbsp;</p>
<p>The UI is intentionally plain to keep the focus on the architectural framework. This is a simple &#8216;consumer&#8217; version of the application: It allows you to look for wines in your Wine Cellar. The tutorial application mentioned above is an &#8216;admin&#8217; version: You can create, update, delete wines in your Wine Cellar. Most of the code is shared between the Mobile &#8216;consumer&#8217; version and the browser-based &#8216;admin&#8217; version.</p>
<p>In this version, the application gets the data from RESTful services hosted on my server. In my next post, I will provide another version of the application that gets the data using a local database on your device.</p>
<h4>Download</h4>
<p>I added the source code for the mobile application to the <a href="https://github.com/ccoenraets/backbone-cellar">backbone-cellar GitHub repository</a>. You can download the zip file <a href="https://github.com/ccoenraets/backbone-cellar/zipball/master">here</a>. For your convenience, I created an <a href="https://github.com/ccoenraets/backbone-cellar/tree/master/mobile/ios">iOS directory</a> with the Xcode project and an <a href="https://github.com/ccoenraets/backbone-cellar/tree/master/mobile/android">Android directory</a> with the Eclipse project. The core application is in the www directory (<a href="https://github.com/ccoenraets/backbone-cellar/tree/master/mobile/ios/WineCellar/www">iOS</a> and <a href="https://github.com/ccoenraets/backbone-cellar/tree/master/mobile/android/BackboneCellarAndroid/assets/www">android</a>) and it is the same code for both iOS and Android.</p>
<p>If you want to run the application on your Android device without installing the project, you can also download the apk file available in the <a href="https://github.com/ccoenraets/backbone-cellar/tree/master/mobile/android/BackboneCellarAndroid/bin">bin directory</a>.</p>
<p>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal"  data-lang="en"  data-url="http://coenraets.org/blog/2012/02/sample-mobile-app-with-backbone-js-and-phonegap/"></a>
<!-- End WP Socializer Plugin - Retweet Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - +1 Button -->
<g:plusone size="medium" href="http://coenraets.org/blog/2012/02/sample-mobile-app-with-backbone-js-and-phonegap/" ></g:plusone>
<!-- End WP Socializer Plugin - +1 Button -->
</span>
	<span class="margin5">
<!-- Start WP Socializer Plugin - Facebook Button -->
<iframe src="http://www.facebook.com/plugins/like.php?&amp;href=http://coenraets.org/blog/2012/02/sample-mobile-app-with-backbone-js-and-phonegap/&amp;layout=button_count&amp;show_faces=0&amp;width=80&amp;action=like&amp;font=arial&amp;colorscheme=light&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:80px; height:21px;" allowTransparency="true"></iframe>
<!-- End WP Socializer Plugin - Facebook Button -->
</span>
</p>
<p>
<a href="https://twitter.com/ccoenraets" class="twitter-follow-button" data-show-count="false" data-lang="en" data-size="large">Follow @ccoenraets</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</p>
	
<!-- Start WP Socializer Plugin - Retweet Button -->
<a href="http://twitter.com/?status=RT @ Sample Mobile App with Backbone.js and PhoneGap http://coenraets.org/blog/?p=2987" target="_blank">Retweet this</a>
<!-- End WP Socializer Plugin - Retweet Button -->

	
	
<!-- Start WP Socializer Plugin - Facebook Button -->
<a href="https://www.facebook.com/sharer.php?u=http%3A%2F%2Fcoenraets.org%2Fblog%2F2012%2F02%2Fsample-mobile-app-with-backbone-js-and-phonegap%2F" target="_blank">Share on Facebook</a>
<!-- End WP Socializer Plugin - Facebook Button -->



Follow @ccoenraets
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
]]></content:encoded>
			<wfw:commentRss>http://coenraets.org/blog/2012/02/sample-mobile-app-with-backbone-js-and-phonegap/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
	</channel>
</rss>

