jQuery mobile dynamic content with a knockoutjs template and MVC

Populate a listview widget dynamically with an ajax call

If you create the page elements dynamically (ajax request / listview) jquery mobile may not apply the page styles. In the jquery mobile plugin there are two event: "pagecreate" and "pageinit". In the jquery mobile
documentation you can see:

"pagecreate": Triggered when the page has been created in the DOM (via ajax or other) but before all widgets have had an opportunity to enhance the contained markup. This event is most useful for user's
wishing to create their own custom widgets for child markup enhancement as the jquery mobile widgets do.

"pageinit": Triggered on the page being initialized, after initialization occurs. We recommend binding to this event instead of DOM ready() because this will work regardless of whether the page is loaded directly or if
the content is pulled into another page as part of the Ajax navigation system.

These events will be raised before the "pagebeforechange/pagechange" events. If you send an ajax request in the "pagechange" event and then apply a knockoutjs template, the page style has already been applied (before the ajax call). So you can see unformatted html. To apply a jquery mobile styles/theme to a knockoutjs template loaded dynamically (forcing jQuery Mobile to re-evaluate styles/theme on dynamically inserted content), you have three way to follow. 1) You must call, in the databound event, the $('.ui-page-active' ).page( 'destroy' ).page() method. This tell to the jquery mobile plugin that it must recreate the page styles. 2) Use the "jrecreatestyle" attribute, this tell the plugin that it must recreate the page styles for you. 3) Use the custom binding "jqmRefreshList" (if you use a listview widget).

If you set a breakpoint in the "pageinit" you can see that the "pageinit" will be reraised. In this way the
jquery mobile application it will apply style to the new content / widget.

Contenuto dinamico con Jquery mobile (Italian article - www.dotnettoscana.org)
Creare un widget Jquery mobile (Italian article - www.dotnettoscana.org)

<!DOCTYPE html>

<html>
<head>
<title>Pagina13</title>
<meta content="width=device-width,minimum-scale=1,maximum-scale=3" name="viewport">.
  ...
</head>
<body>

<!-- page -->
<div id="page1" data-role="page">
  <div data-role="header">
    <h1>Page Title 1a</h1>
  </div>
  <div data-role="content">	
    <p>Page content-1 goes here (@DateTime.Now).</p>
    <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-jfselecting="selecting2" 
      data-jrecreatestyle="false">
  <div data-role="header">
    <h1>Page Title-2</h1>
    <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="jqmRefreshList: {name: 'temp2', foreach: $data}"></ul>
    </div>	
  </div>
  <div data-role="footer">
    <h4>Page Footer</h4>
  </div>
</div>
<!-- /page -->

<script id="temp2" type="text/html">
   <li>
     <div><span data-bind="text: CustomerID" /></span></div>
     <ul data-role="listview">          
       <li>
         <p><br>
         <a data-rel="back" data-jbackcurrpage="true">Back</a><br>
         <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></p>
       </li>
     </ul>
  </li>
</script>

</body>
</html>

In this example there are 2 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. In the knockout template has been used the custom binding jqmRefreshList this tell to the plugin that it must recreate the page styles (if you use a listview widget).

$(document).ready(function () {
    $("body").mobileTemplate();
});
//
// 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);
};

Below the custom binding "jqmRefreshList" that will be used if you request a knockout template that contain a listview widget. As you can see, the custom binding call the method "$(element).listview('refresh')" that recreate the page styles after the ajax call has returned data.

ko.bindingHandlers.jqmRefreshList = {
    init: ko.bindingHandlers.template.init,
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, context) {
        ko.bindingHandlers.template.update(element, valueAccessor, 
                allBindingsAccessor, viewModel, context);
        try {
            $(element).listview('refresh');
        } catch (e) {
            $(element).listview();
        }
    }
};


In this way you can build dynamic pages with knockoutjs and the MVVM pattern. 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 Jul 9, 2012 at 9:15 PM by mastefano64, version 25

Comments

No comments yet.