This project is read-only.

Create a PhoneGap application using html5/javascript

Will also be used mvc,ajax,jquery mobile and knockout.js

Using PhoneGap you can create a cross-platform application ( Windows Phone , Android, iOS, etc ). At base level will be used html5 and javascript; so it is supported from every platform. In this example will be used jquery mobile and the knockout.js MVVM library to create a mobile application (server side will be used mvc some action methods call that return data in a json format).

The data access is provided through the jquery "mobileTemplate" plugin using ajax/mvc rest call (the plugin wrap the "PagingBase / zPageGrid1.js" object). When you instantiates the plugin, it parse the DOM and it will create a datasource for every page (an html div with the attribute data-role=page), so you can create query and apply data-template. There are some attributes that you will put on the page div, so you can apply base functionalities in an unobtrusive mode. The plugin registers the "pagechange" of jquery mobile, so when you change page it will start some datasorce functionalities. When you instantiate the plugin; you must declare the base address of the ajax/rest calls.

Inside Windows Phone it will be used the WebBrowser control to host the html5/javascript code (for more detail see the PhoneGap web site). In this way you have an infrastructure that provides some base service (you must only concentrate on the business logic).

In this example there are 4 page. The first is a form-serch page. The second fetch data from the search conditions (it go on db to read data). The third is a detail page of the selected item (it go on db to read data). The fourth page is a edit/modify page (it not go on db to read data). In the download file you can find a Windows Phone 7.1 project that show this PhoneGap/javascript examples in action.

<!DOCTYPE html>

<html>
<head>
<title>PhoneGap</title>
<meta name="viewport" content="width=device-width,minimum-scale=1,maximum-scale=3">
<link href="master.css" rel="stylesheet" type="text/css" />
<link href="jquery.mobile-1.0.1.min.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="phonegap-1.4.1.js"></script>
<script type="text/javascript" src="scripts/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="scripts/knockout-2.0.0.js"></script>
<script type="text/javascript" src="scripts/knockout.mapping.js"></script> 
<script type="text/javascript" src="scripts/json2.js"></script>
<script type="text/javascript" src="scripts/zPageGrid1.js"></script>
<script type="text/javascript" src="scripts/zPageGrid3.js"></script>
<script type="text/javascript" src="scripts/zApplication1.js"></script>
<script type="text/javascript" src="scripts/jquery.mobile-1.0.1.min.js"></script>
</head>
<body>

<!-- page -->
<div id="page1" data-role="page">
   <div data-role="header">
     <h1>Page Title 1a</h1>
   </div>
   <div data-role="content">	
     <div>
     <table>    
     <tr>
        <td>City: </td>
        <td><input id="text1" type="text" /></td>
      </tr>  
      <tr>
        <td>Country: </td>
        <td><input id="text2" type="text" /></td>
      </tr> 
      </table>
      <br /><br />
      <a href="#page2" data-role="button">Find</a>
      <br /><br />
    </div>	
  </div>
  <div data-role="footer">
    <h4>Page Footer</h4>
  </div>
</div>
<!-- /page --> 

<!-- page -->
<div id="page2" data-role="page" 
      data-jresturl="GetData1" 
      data-jautocompose="false" 
      data-jenableko="true" 
      data-jpagesize="10"
      data-jcontainer="c2" 
      data-jfselecting="selecting2" 
      data-jrecreatestyle="true">
  <div data-role="header">
    <h1>Page Title-2</h1>
    <a data-rel="back" data-role="button" data-jbackcurrpage="true">Back</a>
    <div data-role="controlgroup" data-type="horizontal">
      <a id="buttonR" data-role="button" data-jnavbaraction="refresh">Refresh</a>
      <a id="buttonF" data-role="button" data-jnavbaraction="pageF">MoveF</a>
      <a id="buttonP" data-role="button" data-jnavbaraction="pageP">MoveP</a>
      <a id="buttonN" data-role="button" data-jnavbaraction="pageN">MoveN</a>
      <a id="buttonL" data-role="button" data-jnavbaraction="pageL">MoveL</a>
    </div>    
  </div>
  <div data-role="content">	
    <div id="jtable2">
      <ul id="c2" data-role="listview" data-bind="template: {name: 'temp2', foreach: $data}"></ul>
    </div>	
  </div>
  <div data-role="footer">
    <h4>Page Footer</h4>
  </div>
</div> 
<!-- /page -->

<!-- page -->
<div id="page3" data-role="page" 
      data-jresturl="GetData1" 
      data-jenableko="true" 
      data-jcontainer="c3" 
      data-jparam01="*City"
      data-jorderby="ContactName" 
      data-jrecreatestyle="true">
  <div data-role="header">
    <h1>Page Title-3</h1>
  </div>
  <div data-role="content">	
    <a data-rel="back" data-role="button" data-jbackcurrpage="true">Back</a>    
    <div id="c3" data-bind="template: {name: 'tempdetail', foreach: $data}"></div> 
  </div>
  <div data-role="footer">
     <h4>Page Footer</h4>
  </div>
</div>
<!-- /page -->

<!-- page -->
<div id="page4" data-role="page" 
      data-jresturl="none" 
      data-jenableko="true" 
      data-jcontainer="c4" 
      data-jrecreatestyle="true">
  <div data-role="header">
    <h1>Page Title-4</h1>
  </div>
  <div class="modifyfrm" data-role="content">	
    <a data-rel="back" data-role="button" data-jbackcurrpage="true">Back</a>       
    <div id="c4" data-bind="template: {name: 'tempmodify', foreach: $data}"></div> 
  </div>
  <div data-role="footer">
    <h4>Page Footer</h4>
  </div>
</div>
<!-- /page -->

<script id="temp2" type="text/html">
  <li>
    <div>
      <p><a href="#page4" data-role="button" data-jsavecurritem="true">Modify</a></p>
      <p><a href="#page3" data-role="button" data-jsavecurritem="true"> Find </a></p>
      <p><b>CustomerID: </b><span data-bind="text: CustomerID" /></span><p>
      <p><b>CompanyName: </b><span data-bind="text: CompanyName" /></span><p>
      <p><b>ContactName: </b><span data-bind="text: ContactName" /></span><p>
      <p><b>City: </b><span data-bind="text: City" /></span><p>
      <p><b>Country: </b><span data-bind="text: Country" /></span><p>
    </div>
  </li>
</script>

<script id="tempmodify" type="text/html">
  <div>
    <div data-role="fieldcontain">
      <fieldset data-role="controlgroup">
        <label for="CustomerID">CustomerID</label>
        <input id="CustomerID" type="text" data-bind="value: CustomerID" />
        <label for="CompanyName">CompanyName</label>
        <input id="CompanyName" type="text" data-bind="value: CompanyName" />
        <label for="ContactName">ContactName</label>
        <input id="ContactName" type="text" data-bind="value: ContactName" />
        <label for="City">City</label>
        <input id="City" type="text" data-bind="value: City" />
        <label for="Country">Country</label>
        <input id="Country" type="text" data-bind="value: Country" />
      </fieldset>
      <a data-role="button" data-bind="click: updateItem">Update</a>
      <a data-role="button" data-bind="click: deleteItem">Delete</a>
    </div>  
  </div>
</script>

<script id="tempdetail" type="text/html">
  <p>
    <b>CustomerID: </b><br>
    <span data-bind="text: CustomerID" /></span><br>
    <span data-bind="text: CompanyName" /></span><br>
    <span data-bind="text: ContactName" /></span><br>
    <span data-bind="text: City" /></span><br>
    <span data-bind="text: Country" /></span><br>
  </p>
</script>

</body>
</html>


At startup you must handling PhoneGap's deviceready event before doing something. As you can see in the below code it is applied the knockout.js MVVM pattern; the jquery mobile listview widget is populated through a knockout.js template. It is also used the "jrecreatestyle" attribute, this tell the plugin that it must recreate the page styles when it receives the response from the server (necessary in a dynamic context).
Inside the viewmodel you can see two command (updateItem, deleteItem) that it will be mapped through the knockout.js "click" binding. When you instantiate the plugin; it is declared the base address of the ajax/rest call. You must also set the jQuery.support.cors = true, $.mobile.allowCrossDomainPages = true property to allow cross-domain request.

var sdomain = "http://192.168.1.2/Mobile/";

window.addEventListener('load', function () {
    document.addEventListener('deviceready', function () {
        //console.log("PhoneGap is now loaded/1");
        jQuery.support.cors = true;
        $.mobile.allowCrossDomainPages = true;
        $("body").mobileTemplate({tdomain: sdomain});
        //console.log("PhoneGap is now loaded/2");
    }, false);
}, false);
//
// This event is raised before the request will be send to the server.
// In this case "jautocompose='false'" then the query will be defined here.
//
function selecting2(eventargs) {
    var context = eventargs.toPage.context;
    var swhere = "";
    var text1 = $("#text1").val();
    text1 = jQuery.trim(text1);
    if (text1 != "") {
        if (swhere == "")
            swhere = swhere + "param01=" + text1;
        else swhere = swhere + "&param01=" + text1;
    }
    var text2 = $("#text2").val();
    text2 = jQuery.trim(text2);
    if (text2 != "") {
        if (swhere == "")
            swhere = swhere + "param02=" + text2;
        else swhere = swhere + "&param02=" + text2;
    }
    context.pagingWithSize(10).where(swhere).orderBy(
       "Country,City desc").applyTempKo(mapKnock);
}
//
//  Callback function that create the viewmodel.
//
function mapKnock(data) {
    var viewmodel = ko.utils.arrayMap(data, function (item) {
        return new dataItem(item);
    });
    return ko.observableArray(viewmodel);
};
//
//  The viewmodel.
//
function dataItem(item) {
    var that = this;
    this.message = ko.observable("");
    this.CustomerID = ko.observable(item.CustomerID);
    this.CompanyName = ko.observable(item.CompanyName);
    this.ContactName = ko.observable(item.ContactName);
    this.City = ko.observable(item.City);
    this.Country = ko.observable(item.Country);

    this.updateItem = function () {
        var itemreq = ko.toJSON(this);
        //if ($("#page4 form").valid() == false) {
        //    alert("Invalid form");
        //    return;
        //}
        var surl = sdomain + "UpdateViewModel1/";
        $.ajax({ url: surl, type: 'post',
            data: itemreq,
            contentType: 'application/json',
            error: function (request, state, error) {
                navigator.notification.alert("Ajax error:" + error);
            },
            success: function (result) {
                navigator.notification.alert("Success:" + result);
            }
        });
    } .bind(this);

    this.deleteItem = function () {
        var itemreq = ko.toJSON(this);
        //if ($("#page4 form").valid() == false) {
        //    alert("Invalid form");
        //    return;
        //}
        var surl = sdomain + "DeleteViewModel1/";
        $.ajax({ url: surl, type: 'post',
            data: itemreq,
            contentType: 'application/json',
            error: function (request, state, error) {
                navigator.notification.alert("Ajax error:" + error);
            },
            success: function (result) {
                navigator.notification.alert("Success:" + result);
            }
        });
    } .bind(this);
};


This is the action method on the mvc/controller. As you can see the action method return data in a json format. Linq is not enabled; therefore you must set the mapping from the querystring patamiter to the entity field. You use the methods: AddWhereMapping(field, operator, param), AddWhereMappingStr(field, operator, param), AddWhereMappingInt(field, operator, param), AddWhereMappingDbl(field, operator, param), AddWhereMappingDec(field, operator, param), AddWhereMappingDat(field, operator, param), AddWhereMappingBol(field, operator, param). By default the AddWhereMapping(field, operator, param) method it create a string mapping.

public ActionResult GetData1(RequestRest rest)
{
    rest.Operator = WhereOperator.And;
    rest.AddWhereMapping("City", "=", "param01");
    rest.AddWhereMapping("Country", "=", "param02");
    rest.DefaultOrderBy("CustomerID");
    var query = Repository.GetRepository<Customer>().Query();
    var result = this.TryApplyView(rest, query.JQuery(rest));
    return Json(result, JsonRequestBehavior.AllowGet);
}



Classic page with knockout MVVM
jQuery mobile page with knockout MVVM
CRUD knockoutjs grid and MVVM pattern
jQuery mobile dynamic content with a knockoutjs template and MVC
Create a PhoneGap application using html5/javascript
How using RavenDB in a html5/javascript application
Knockoutjs and LINQ for JavaScript

Last edited May 22, 2012 at 10:00 AM by mastefano64, version 58

Comments

No comments yet.