jQuery mobile page with knockout MVVM


In this example there are 4 page. The first is a form-serch page. The second fetch data from the search conditions on page definition you can see: jautocompose="false", jfselecting="selecting2". This tell to the framework does not create automaticaly a query, but the query is created in the event-handler specified by jfselecting="selecting2" attribute. The third is a detail page of the selected item (it go on db to read data). On this page you can see "data-jresturl='value'", this tell to the framework to send a rest call, it take the current item and then create a param link. On page definition you can see data-jparam01="*City". The current item is selected/saved when you apply "data-jsavecurritem='true'" on the template link. There is also an event-handler jfselecting="selecting3" where you can apply additionals contition. The fourth page is a edit/modify page (it not go on db to read data). On this page you can see "data-jresturl='none'", this tell to the framework does not sends a rest call, it take the current item. The current item is selected/saved when you put "data-jsavecurritem='true'". If you does not set "jautocompose" attribute, the default value is true. Navigation button have "data-jnavbaraction='action'" that tell the framework which command apply. In the query projection method "applyTempKo(mapKnock)", you have specified a callback function "mapKnock(data)" that it will create the viewmodel. On the main template "temp2" you can see some html attribute: jcustomaction, jdeleteaction. These tell the the plugin that there are some action that need to be registred. The tcustomCallBack, tdeleteCallBack actions will be specified by the plugin properties (tcustomCallBack, tdeleteCallBack) in the event-handler "jinitialize='init2'". They will be raised when you clik on the link.

@using JQueryLinq
@model Application1.CustomerViewModel2
<!DOCTYPE html>

<html>
<head>
<title>Pagina7</title>
<meta name="viewport" content="initial-scale=2, maximum-scale=4">
 ...
</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-transition="fade">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-jinitialize="init2" 
      data-jfselecting="selecting2" 
      data-jfdatabound="databound2">
  <div data-role="header">
    <h1>Page Title-2</h1>
    <div data-role="controlgroup" data-type="horizontal">
      <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">	
    <a data-rel="back" data-jbackcurrpage="true" 
      data-direction="reverse">Back</a><br /> 		
    <div>
    <table id="jtable2">
    <thead>
    <tr>
    <td colspan="4"> &nbsp; </td>
    <td data-sort="CustomerID"> CustomerID </td>
    <td data-sort="CompanyName"> CompanyName </td>
    <td data-sort="ContactName"> ContactName </td>
    <td data-sort="City"> City </td>
    <td data-sort="Country"> Country </td>
    </tr>
    </thead>
    <tbody id="c2" data-bind="template: {name: 'temp2', foreach: $data}"></tbody>
    </table>
    </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-jfselecting="selecting3" 
      data-jfdatabound="databound3">
  <div data-role="header">
    <h1>Page Title-4</h1>
  </div>
  <div data-role="content">	
    <a data-rel="back" data-jbackcurrpage="true" 
      data-direction="reverse">Back</a><br /> 		
    <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-jfselecting="selecting4" 
      data-jfdatabound="databound4">
  <div data-role="header">
    <h1>Page Title-4</h1>
  </div>
  <div data-role="content">	
    <a data-rel="back" data-jbackcurrpage="true" 
       data-direction="reverse">Back</a><br />     
    <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">
  <tr>
    <td><b data-jcustomaction="true">Custom</b></td>
    <td><b data-jdeleteaction="true">Delete</b></td>
    <td><a href="#page4" data-jsavecurritem="true">Edit</a></td>
    <td><a href="#page3" data-jsavecurritem="true">City</a></td>
    <td> ${CustomerID} </td>
    <td> ${CompanyName} </td>
    <td> ${ContactName} </td>
    <td> ${City} </td>
    <td> ${Country} </td>
  </tr>
</script>

<script id="tempmodify" type="text/html">
@using (Html.BeginForm()) {
  <div>
    <p><span data-bind="text: fullData"></span></p>
    <p><b>CustomerID: </b>@Html.KoTextBoxStrFor(model => model.CustomerID)</p>
    <p><b>CompanyName: </b>@Html.KoTextBoxStrFor(model => model.CompanyName)</p>
    <p><b>ContactName: </b>@Html.KoTextBoxStrFor(model => model.ContactName)</p>
    <p><b>City: </b>@Html.KoTextBoxStrFor(model => model.City)</p>
    <p><b>Country: </b>@Html.KoTextBoxStrFor(model => model.Country)</p>
    <button data-bind="click: updateItem">Update</button>&nbsp;
    <button data-bind="click: deleteItem">Delete</button>&nbsp;
  </div>
}  
</script>

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

</body>
</html>


$(document).ready(function () {
    $("body").mobileTemplate();
});

//
// This event is raised when the application start.
// In this case it regirter two callback.
//
function init2(context) {
    context.customCallBack = function (obj) {
        var item = obj.dataitemJs;
        alert("custom:" + item.CustomerID);
    };

    context.deleteCallBack = function (obj) {
        var item = obj.dataitemJs;
        alert("delete:" + item.CustomerID);
    };
}
//
// 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);
}
//
// This event is raised before the request will be send to the server.
// In this case "jautocompose='true'" (default value) then I can 
// add additional clause here.
//
function selecting3(eventargs) {
    var context = eventargs.toPage.context;
    //context.orderBy("ContactName");
}
//
//  The request will not be send to the server. 
//  The attribute "jresturl='none'". Will be showed data from previous page.
//
function selecting4(eventargs) {
    var context = eventargs.toPage.context;
}
//
//  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.fullData = ko.computed(function () {
        return this.CompanyName() + " - " + this.ContactName();
    }, this);

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

    this.deleteItem = function () {
        var itemreq = ko.toJSON(this);
        //if ($("#page4 form").valid() == false) {
        //    alert("Invalid form");
        //    return;
        //}
        $.ajax({ url: "/Mobile/DeleteViewModel1/", type: 'post',
            data: itemreq,
            contentType: 'application/json',
            error: function (request, state, error) {
                alert("Ajax error:" + error);
            },
            success: function (result) {
                alert(result);
            }
        });
    }.bind(this);
};
//
//  Databound event.
//
function databound2(event) {
    var itemKo = event.context.source;
    var itemJs = ko.toJS(itemKo);
}
//
//  Databound event.
//
function databound3(event) {
    var itemKo = event.context.source;
    var itemJs = ko.toJS(itemKo);
}
//
//  Databound event.
//
function databound4(event) {
    $.validator.unobtrusive.parse($("#page4"));
}


In this way you can create a MVC jquery mobile knockoutjs/MVVM grid with paging, filter and sort functions.
Alternatively you can also use PhoneGap to create your application (using jquery mobile with html5 and javascript).

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 Mar 18, 2012 at 9:41 AM by mastefano64, version 46

Comments

No comments yet.