Many WEB developers use jQuery and jQuery Mobile in their applications. Probably most of these developers would like to carry these applications on any popular mobile platforms such as Windows Phone, Android, iOS. In fact it is very easy and quick if you considerate some rules.
In previous blogs “Using PhoneGap in Windows Store Applications” and “Using PhoneGap in Windows Phone 8 Applications” we discussed how to start with PhoneGap and Windows Store / Windows Phone 8 applications.
In this blog we will continue the topic of PhoneGap / Apache Cordova considering real life examples and in particular how to convert your mobile WEB apps in hybrid* web applications.
* Hybrid apps: Hybrid apps are just web apps with native wrapper. Hybrid apps run in web view not in native browser. So you can download and install hybrid apps. There are many mobile development frameworks are available using them you can create hybrid apps. PhoneGap: is the most popular open-source mobile development framework for hybrid applications. Using PhoneGap you can create apps in HTML, CSS and JavaScript. PhoneGap also gives access to the native features from JavaScript.
In this article you will learn how to create a hybrid PhoneGap application for Windows Phone 8 based on jQuery Mobile Twitter WEB application. It is a simple WEB application that uses twitter API to search for tweets containing a specific string.
Twitter search WEB application
The source code of the twitter WEB app is available below.
1:<!DOCTYPEhtml>
2:
3:<html>
4:<head>
5:<metahttp-equiv="Content-Type"content="text/html; charset=UTF-8"/>
6:<metaname="format-detection"content="telephone=no"/>
7:<metaname="viewport"content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi"/>
8:<linkrel="stylesheet"type="text/css"href="css/index.css"/>
9:<linkrel="stylesheet"href="css/themes/default/jquery.mobile-1.2.0.css"/>
10:<scriptsrc="js/jquery.js"></script>1:
2:<script src="js/jquery.mobile-1.2.0.js">1:</script>
2:<title>Hello World</title>
3:</head>
4:<body>
5:
6:<script type="text/javascript" src="cordova-2.3.0.js">1:</script>
2:<script type="text/javascript" src="js/index.js">1:</script>
2:<script type="text/javascript">3:
4:
</script>1:
2:<!---- SEARCH PAGE ----->
3:<div data-role="page" id="page1">4:<div data-role="header">5:<h1>Twitter</h1>
6:</div><!-- /header -->
7:
8:<div data-role="content">9:<div data-role="fieldcontain">10:<input type="search" name="search" id="search" value="" style="-webkit-user-select: text;" />11:<input type="button" name="searchButt" id="searchButt" value="Search"/>12:</div>
13:<div data-role="content">14:<div id="twitList">15:
16:</div>
17:</div><!-- /content -->
18:<a href="#prefs" data-role="button" data-icon="gear" data-rel="dialog" data-transition="pop">Preferences</a>19:</div><!-- /content -->
20:
21:
22:</div><!-- /page -->
23:
24:
25:<!---- PREFERENCES ----->
26:<div data-role="page" id="prefs">27:<div data-role="header">28:<h1>Preferences</h1>
29:</div><!-- /header -->
30:
31:<div data-role="content">32:<div data-role="fieldcontain">33:
34:<label for="slider">Number of results:</label>35:
36:<input type="range" name="slider" id="slider" value="15" min="0" max="100" />37:<fieldset data-role="controlgroup">38:<legend>Result Type:</legend>
39:<input type="radio" name="radio-choice-1" id="radio-choice-1" value="mixed"checked="checked" />40:<label for="radio-choice-1">Mixed</label>41:
42:<input type="radio" name="radio-choice-1" id="radio-choice-2" value="recent" />43:<label for="radio-choice-2">Recent</label>44:
45:<input type="radio" name="radio-choice-1" id="radio-choice-3" value="popular" />46:<label for="radio-choice-3">Popular</label>47:</fieldset>
48:
49:</div>
50:</div><!-- /content -->
51:
52:</div><!-- /page -->
53:
54:</body>
55:</html>
56:
57:<script type="text/javascript">58: $(function () {59: $('#searchButt').click(function () {60: doSearch();
61: });
62: });
63:
64:function doSearch() {65: $.ajax({
66:
67:
68: url: "http://search.twitter.com/search.json?q=" + $('#search').val() + "&result_type=" + $('input:radio[name=radio-choice-1]:checked').val() + "&rpp=" + $('#slider').val(),69: dataType: 'jsonp',70: success: function (json_results) {71:// Remove any list - so the new one can be added.72: $('#twitList ul').remove();73:// Need to add UL on AJAX call or formatting of userlist is not displayed74: $('#twitList').append('<ul data-role="listview"></ul>');75: listItems = $('#twitList').find('ul');76: $.each(json_results.results, function (key) {77: html = '<img src="' + json_results.results[key].profile_image_url + '"/>';78: html += '<h3><a href="#">' + json_results.results[key].text + '</a></h3>';79: html += '<p>From: ' + json_results.results[key].from_user + ' Created: ' + json_results.results[key].created_at + '</p>';80: listItems.append('<li>' + html + '</li>');81: });
82:// Need to refresh list after AJAX call83: $('#twitList ul').listview();84: }
85: });
86: }
</script>
Before to start with PhoneGap / Cordova
If you have no experience with PhoneGap in general or/and with Cordova for Windows Phone 8 you could read the article “Using PhoneGap in Windows Phone 8 Applications”.
Some important thing that you need to know
- Your web application is encapsulated in a custom web view (named CordovaView in this case)
This is a custom view that can load a specified html file
1:<my:CordovaViewHorizontalAlignment="Stretch"
2:Margin="0,0,0,0"
3:Name="PGView"
4:VerticalAlignment="Stretch"/>
- Where is your html file?
If you are using a standalone Cordova WP8 template you could take a look at the CordovaView source code:
[your WP8 PhoneGap project path]\cordovalib\CordovaView.xaml.cs
There is set a default html file under www folder: www/index.html
1:protected Uri _startPageUri = null;
2:public Uri StartPageUri
3: {
4: get
5: {
6:if (_startPageUri == null)
7: {
8:// default
9:returnnew Uri(AppRoot + "www/index.html", UriKind.Relative);
10: }
11:else
12: {
13:return _startPageUri;
14: }
15: }
16: set
17: {
18:if (!this.IsBrowserInitialized)
19: {
20: _startPageUri = value;
21: }
22: }
23: }
If you have a different default html file: for example my-default.html you need to set it to StartpageUri property.
1:</Grid.RowDefinitions>
2:<my:CordovaViewHorizontalAlignment="Stretch"
3:Margin="0,0,0,0"
4:Name="PGView"
5:StartPageUri="www/my_default.html"
6:VerticalAlignment="Stretch"/>
You need to add jQuery, jQuery Mobile and Cordova css and scripts. In this example we just will “wrap” our WEB application without usage of specific Cordova API, but often you will need to use it.
1:<linkrel="stylesheet"type="text/css"href="css/index.css"/>
2:<linkrel="stylesheet"href="css/themes/default/jquery.mobile-1.2.0.css"/>
3:<scriptsrc="js/jquery.js"></script>1:
2:<script src="js/jquery.mobile-1.2.0.js">1:</script>
2:<script type="text/javascript" src="cordova-2.3.0.js">1:</script>
2:<script type="text/javascript" src="js/index.js">1:</script>
2:<script type="text/javascript">3: app.initialize();
</script>
PhoneGap WP8 template includes also “index.js” file where you could attach your code to “deviceready” event. This event is fired when the whole Cordova script is loaded.
The index.js file source
1:var app = {
2:// Application Constructor
3: initialize: function() {
4:this.bindEvents();
5: },
6:// Bind Event Listeners
7://
8:// Bind any events that are required on startup. Common events are:
9:// `load`, `deviceready`, `offline`, and `online`.
10: bindEvents: function() {
11: document.addEventListener('deviceready', this.onDeviceReady, false);
12: },
13:// deviceready Event Handler
14://
15:// The scope of `this` is the event. In order to call the `receivedEvent`
16:// function, we must explicity call `app.receivedEvent(...);`
17: onDeviceReady: function() {
18: app.receivedEvent('deviceready');
19:
20: },
21:// Update DOM on a Received Event
22: receivedEvent: function(id) {
23://add your code here
24: }
25: };
- Using jQuery Mobile and PhoneGap together
What is the correct way to use jQuery Mobile and PhoneGap together?
Both frameworks need to load before they can be used. There are several approaches to do this.
- You can use deferred feature of JQuery.
1:<!DOCTYPEhtml>
2:<html>
3:<head>
4:<metahttp-equiv="Content-Type"content="text/html; charset=UTF-8"/>
5:<metaname="format-detection"content="telephone=no"/>
6:<metaname="viewport"content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi"/>
7:<linkrel="stylesheet"type="text/css"href="css/bootstrap.css"/>
8:<title>InforMEA</title>
9:</head>
10:<body>
11:<scripttype="text/javascript"src="js/jquery-1.8.3.js"></script>1:
2:<script type="text/javascript">3:var dd = $.Deferred();4:var jqd = $.Deferred();5: $.when(dd, jqd).done(doInit);
6:
7: $(document).bind('mobileinit', function () {8: jqd.resolve();
9: });
10:
</script>1:
2:<script type="text/javascript" src="js/jquery.mobile-1.2.0.js">1:</script>
2:<script type="text/javascript" src="cordova-2.3.0.js">1:</script>
2:<script type="text/javascript">3: document.addEventListener('deviceready', deviceReady, false);4:function deviceReady() {5: dd.resolve();
6: }
7:
8:function doInit() {9: alert('Ready');10: }
11:
</script>
12:</body>
13:</html>
- Another approach is to add your code that handle both Cordova and jQuery Mobile after your html as in our sample application
Sample Application
Demo application contains only two pages – “search page” and “preferences page”. In the same file is included a jQuery function used to search for tweets via the twitter API.
Search page
1:<!---- SEARCH PAGE ----->
2:<divdata-role="page"id="page1">
3:<divdata-role="header">
4:<h1>Twitter</h1>
5:</div><!-- /header-->
6:
7:<divdata-role="content">
8:<divdata-role="fieldcontain">
9:<inputtype="search"name="search"id="search"value=""/>
10:<inputtype="button"name="searchButt"id="searchButt"value="Search"/>
11:</div>
12:<divdata-role="content">
13:<divid="twitList">
14:
15:</div>
16:</div><!-- /content-->
17:<ahref="#prefs"data-role="button"data-icon="gear"data-rel="dialog"data-transition="pop">Preferences</a>
18:</div><!-- /content-->
19:
20:
21:</div><!-- /page-->
Preferences page
1:<divdata-role="page"id="prefs">
2:<divdata-role="header">
3:<h1>Preferences</h1>
4:</div><!-- /header-->
5:
6:<divdata-role="content">
7:<divdata-role="fieldcontain">
8:
9:<labelfor="slider">Number of results:</label>
10:
11:<inputtype="range"name="slider"id="slider"value="15"min="0"max="100"/>
12:<fieldsetdata-role="controlgroup">
13:<legend>Result Type:</legend>
14:<inputtype="radio"name="radio-choice-1"id="radio-choice-1"value="mixed"checked="checked"/>
15:<labelfor="radio-choice-1">Mixed</label>
16:
17:<inputtype="radio"name="radio-choice-1"id="radio-choice-2"value="recent"/>
18:<labelfor="radio-choice-2">Recent</label>
19:
20:<inputtype="radio"name="radio-choice-1"id="radio-choice-3"value="popular"/>
21:<labelfor="radio-choice-3">Popular</label>
22:</fieldset>
23:
24:</div>
25:</div><!-- /content-->
26:
27:</div><!-- /page-->
Twitter search function:
1:<scripttype="text/javascript">1:
2: $(function () {3: $('#searchButt').click(function () {4: doSearch();
5: });
6: });
7:
8:function doSearch() {9: $.ajax({
10:
11:
12: url: "http://search.twitter.com/search.json?q=" + $('#search').val() + "&result_type=" + $('input:radio[name=radio-choice-1]:checked').val() + "&rpp=" + $('#slider').val(),13: dataType: 'jsonp',14: success: function (json_results) {15:// Remove any list - so the new one can be added.16: $('#twitList ul').remove();17:// Need to add UL on AJAX call or formatting of userlist is not displayed18: $('#twitList').append('<ul data-role="listview"></ul>');19: listItems = $('#twitList').find('ul');20: $.each(json_results.results, function (key) {21: html = '<img src="' + json_results.results[key].profile_image_url + '"/>';22: html += '<h3><a href="#">' + json_results.results[key].text + '</a></h3>';23: html += '<p>From: ' + json_results.results[key].from_user + ' Created: ' + json_results.results[key].created_at + '</p>';24: listItems.append('<li>' + html + '</li>');25: });
26:// Need to refresh list after AJAX call27: $('#twitList ul').listview();28: }
29: });
30: }
31:
</script>
The whole index.html page
1:<!DOCTYPEhtml>
2:<html>
3:<head>
4:<metahttp-equiv="Content-Type"content="text/html; charset=UTF-8"/>
5:<metaname="format-detection"content="telephone=no"/>
6:<metaname="viewport"content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi"/>
7:<linkrel="stylesheet"type="text/css"href="css/index.css"/>
8:<linkrel="stylesheet"href="css/themes/default/jquery.mobile-1.2.0.css"/>
9:<scriptsrc="http://debug.phonegap.com/target/target-script-min.js#devil-mike"></script>1:
2:<script src="js/jquery.js">1:</script>
2:<script src="js/jquery.mobile-1.2.0.js">1:</script>
2:<title>Hello World</title>
3:</head>
4:<body>
5:<script type="text/javascript" src="cordova-2.3.0.js">1:</script>
2:<script type="text/javascript" src="js/index.js">1:</script>
2:<script type="text/javascript">3: app.initialize();
4:
</script>1:
2:<script type="text/javascript">3:
4:
</script>1:
2:<!---- SEARCH PAGE ----->
3:<div data-role="page" id="page1">4:<div data-role="header">5:<h1>Twitter</h1>
6:</div><!-- /header -->
7:
8:<div data-role="content">9:<div data-role="fieldcontain">10:<input type="search" name="search" id="search" value="" />11:<input type="button" name="searchButt" id="searchButt" value="Search"/>12:</div>
13:<div data-role="content">14:<div id="twitList">15:
16:</div>
17:</div><!-- /content -->
18:<a href="#prefs" data-role="button" data-icon="gear" data-rel="dialog" data-transition="pop">Preferences</a>19:</div><!-- /content -->
20:
21:
22:</div><!-- /page -->
23:
24:
25:<!---- PREFERENCES ----->
26:<div data-role="page" id="prefs">27:<div data-role="header">28:<h1>Preferences</h1>
29:</div><!-- /header -->
30:
31:<div data-role="content">32:<div data-role="fieldcontain">33:
34:<label for="slider">Number of results:</label>35:
36:<input type="range" name="slider" id="slider" value="15" min="0" max="100" />37:<fieldset data-role="controlgroup">38:<legend>Result Type:</legend>
39:<input type="radio" name="radio-choice-1" id="radio-choice-1" value="mixed"checked="checked" />40:<label for="radio-choice-1">Mixed</label>41:
42:<input type="radio" name="radio-choice-1" id="radio-choice-2" value="recent" />43:<label for="radio-choice-2">Recent</label>44:
45:<input type="radio" name="radio-choice-1" id="radio-choice-3" value="popular" />46:<label for="radio-choice-3">Popular</label>47:</fieldset>
48:
49:</div>
50:</div><!-- /content -->
51:
52:</div><!-- /page -->
53:
54:</body>
55:</html>
56:
57:<script type="text/javascript">58: $(function () {59: $('#searchButt').click(function () {60: doSearch();
61: });
62: });
63:
64:function doSearch() {65: $.ajax({
66:
67:
68: url: "http://search.twitter.com/search.json?q=" + $('#search').val() + "&result_type=" + $('input:radio[name=radio-choice-1]:checked').val() + "&rpp=" + $('#slider').val(),69: dataType: 'jsonp',70: success: function (json_results) {71:// Remove any list - so the new one can be added.72: $('#twitList ul').remove();73:// Need to add UL on AJAX call or formatting of userlist is not displayed74: $('#twitList').append('<ul data-role="listview"></ul>');75: listItems = $('#twitList').find('ul');76: $.each(json_results.results, function (key) {77: html = '<img src="' + json_results.results[key].profile_image_url + '"/>';78: html += '<h3><a href="#">' + json_results.results[key].text + '</a></h3>';79: html += '<p>From: ' + json_results.results[key].from_user + ' Created: ' + json_results.results[key].created_at + '</p>';80: listItems.append('<li>' + html + '</li>');81: });
82:// Need to refresh list after AJAX call83: $('#twitList ul').listview();84: }
85: });
86: }
87:
</script>
Build and deploy your Windows Phone 8 application. It looks like a native WP app (if it is styled), but it is just a wrapped via Cordova mobile WEB application.
The app behavior also looks like a native app behavior.
You can use PhoneGap to package this application for different platforms.
This version of the application doesn’t use any of the PhoneGap native APIs (GPS, camera, accelerometer, notification, etc).
Demo source is available here.
You can find version of the same application built using plain jQuery & jQuery Mobile here.
Expect next blogs where you will learn more about Windows Store PhoneGap applications and Cordova extensions for Windows Phone.
Follow news from Infragistics for more information about new Infragistics events.
As always, you can follow us on Twitter @mihailmateev and @Infragistics and stay in touch on Facebook, Google+andLinkedIn!