Javascript to IQueryable and jQuery template (there is also support for jQuery mobile and Knockout).


Javascript to IQueryable is a framework that allows you to write a simple query in javascript client side and then execute it server side with EntityFramework or a linq provider that implement IQueryable. On the server is used "Dynamic Expressions and Queries in LINQ by Microsoft" to compose dynamically your query. So on the MVC controller you can put a generic entry point. If you require a client-side template is used jQuery template plugin. If you require a server-side template is used a MVC partial view. Server side there is also some MVC filter attribute that provide some security feature. On the server if you use a view that has no navigation property you should not be allowed to navigate through all tables. In the release 3.0 I' added the support for jquery mobile. In the release 4.0 I' added the support for knockout, so you can use in your pages the "MVVM" pattern. In this way, you can create a mvc ajax knockoutjs/mvvm grid with search and paging functions. In the release 5.0 I' added the support for mvc3 jquery validation. I have created some html helper that generate the html input field for knockoutjs "data-bind" attribute. In the release 6.0 I extended the support for jquery mobile. Now there is also a full crud knockoutjs grid support. Alternatively you can also use PhoneGap to create your application using jquery mobile framework. - Stefano Marchisio.

Single page apps using html5/javascript with mvc/ajax and knockout.js MVVM pattern.

At this link you can find an Italian article on www.aspitalia.com
Contenuto dinamico con Jquery mobile (Italian article - www.dotnettoscana.org)
Creare un widget Jquery mobile (Italian article - www.dotnettoscana.org)
Introduzione a ArcGIS API for JavaScript (Italian article - www.dotnettoscana.org)
Introduzione a SignalR (Italian article - www.dotnettoscana.org)

For example you can write a query as:
var text1 = $("#text1").val();
var text2 = $("#text2").val();
context.beginWhere("and");
if (text1!= "") {
    context.addWhereClause( "City"  , "=", text1);
}      
if (text2 != "") {
    context.addWhereClause("Country", "=", text2);
}
var r = context.endWhere();            
context.from("/Grid1/GetDataJson").where(r.value,r.param).
          orderBy("CustomerID").pagingWithSize(10).
             applyTempClient("templatename");  


on the Contoller an action method like this:
public ActionResult GetDataJson(LinqRequest linq)
{
    var query = Repository.GetRepository<Customer>().Query();
    var result = this.TryApplyView(linq, query.JQuery(linq));
    return Json(result,  JsonRequestBehavior.AllowGet);
}


and will send to the database the following command:
SELECT TOP (10) 
[Filter1].[CustomerID] AS [CustomerID], 
[Filter1].[CompanyName] AS [CompanyName], 
[Filter1].[ContactName] AS [ContactName], 
[Filter1].[ContactTitle] AS [ContactTitle], 
[Filter1].[Address] AS [Address], 
[Filter1].[City] AS [City], 
[Filter1].[Region] AS [Region], 
[Filter1].[PostalCode] AS [PostalCode], 
[Filter1].[Country] AS [Country], 
[Filter1].[Phone] AS [Phone], 
[Filter1].[Fax] AS [Fax]
FROM ( SELECT [Extent1].[CustomerID] AS [CustomerID], [Extent1].[CompanyName] AS [CompanyName], [Extent1].[ContactName] AS [ContactName], [Extent1].[ContactTitle] AS [ContactTitle], [Extent1].[Address] AS [Address], [Extent1].[City] AS [City], [Extent1].[Region] AS [Region], [Extent1].[PostalCode] AS [PostalCode], [Extent1].[Country] AS [Country], [Extent1].[Phone] AS [Phone], [Extent1].[Fax] AS [Fax], row_number() OVER (ORDER BY [Extent1].[CustomerID] ASC) AS [row_number]
	FROM [dbo].[Customers] AS [Extent1]
	WHERE (N'Madrid' = [Extent1].[City]) AND (N'Spain' = [Extent1].[Country])
)  AS [Filter1]
WHERE [Filter1].[row_number] > 0
ORDER BY [Filter1].[CustomerID] ASC


With this library you can create in the browser a simple query that it will be send on the server through an ajax call. When the ajax message arrive on the server there are some extension methods of IQueryable that create a dynamic query (build the expression tree and then execute it). I used at low-level the library Dynamic Expressions and Queries in LINQ by Microsoft. As you can see in the javascript code there is a method applyTempClient(), when the server send back the result in a json format the javascript code will apply a jquery template. There is another method applyTempServer(), in this case javascript code will apply a server template (will be returned a partialview of mvc in html format). The plugin also provides paging functionality. In this way you can create a jquery/javascript grid with dynamic data. Instead if you prefer to use knockoutjs you can apply a template by applyTempKo(callBackMapKnock, callBackTemplate) / showGridKo(data, callBackMapKnock, callBackTemplate) methods with crud support.

At javascript level (in the browser) there is also a method "continueWith(function)" as you can see it is passed as a parameter a javascript function that will be called when the plugin receives the first result. After this point it will not be called if you paging the result. There is also a "databound" event that will be raised every time the plugin receive data. There is also a "isloading" event that will be raised every time the plugin start a reqeust and when the plugin finish a request.

At javascript level there are two layer.

1) Inner layer is the file "zPageGrid1.js" that contains core funtionality (PagingBase object).

2) Outer layer is the file "zPageGrid2.js" that contains a jQuery plugin (for standard application) and wrap the "PagingBase" object.

3) Outer layer is the file "zPageGrid3.js" that contains a jQuery plugin (for jquery mobile application) and wrap the "PagingBase" object.

javascript-linq-jquery-mobile-knockoutjs-mvc.png

In this way you can reuse the core functionality in different plugin. At high level you can put the functionality that will be required from the enviroment where your code will be executed. For example in a standard browser application, you will have some navigation button and a search form. For example in a jquery mobile application you will create a datasource for every page (multipage application) that will be synchronized with the "pagechange" event of jquery mobile. In both cases it is possible to use a "MVVM" pattern by the knockuotjs library, So you will have a javascript object with bindable properties and methods.

The framework can create a knockoutjs grid using data returned from an ajax call, but you can also populate a grid using data that are already within the browser (localStorage, sessionStorage, application offline, indexDB ... etc). In this case you use only the projection method "showGridKo(data) ". Where data it is the collection that you already have. If you need to filter the data, you can use the library LINQ for JavaScript.

Below an example of client-template.

In this example you have three query that use search field "text2/City" , "text3/Country". It will be applied three different template "jtableTemplate1", "jtableTemplate2" ,"jtableTemplate3", The result will be paged with size 10. There is also a "continueWith" callback that it will be called the first time.

$(document).ready(function () {
    var settings = {
        tpane2: 'gpane2',
        tcontainer: "jpane",     
        ttype: "2",
    };
    $("#gpane2").gridTemplate(settings, searchfrm).
        bind('databound', function (event) {
           var plugin = event.context;
           if (plugin.getCurrentTemplate() == "jtableTemplate1")
               $(".identity").css("color","blue");
           if (plugin.getCurrentTemplate() == "jtableTemplate2")
               $(".identity").css("color","aqua");  
    });
});

function searchfrm(context) {
    context.clearSearch();

    var text1 = $("#text1").val();
    var text2 = $("#text2").val();
    var text3=  $("#text3").val();
    
    context.beginWhere("and");
    if (text2!= "") {
        context.addWhereClause( "City"  , "=", text2);
    }      
    if (text3 != "") {
        context.addWhereClause("Country", "=", text3);
    }
    var r = context.endWhere();   
         
    if (text1 == "1") {
        context.from("/Grid1/GetDataJson").
               where(r.value,r.param).
               orderBy("CustomerID").
               pagingWithSize(10).
               applyTempClient("jtableTemplate1").
               continueWith(continue1);  
    }
    if (text1 == "2") {
        context.from("/Grid1/GetDataJson").
               where(r.value,r.param).
               orderBy("CustomerID").
               pagingWithSize(10).
               applyTempClient("jtableTemplate2").
               continueWith(continue2);  
    }
    if (text1 == "3") {
        context.from("/Grid1/GetDataJson").
               where(r.value,r.param).
               orderBy("CustomerID").
               pagingWithSize(10).
               select("new(CustomerID,City,Country,Orders)").
               applyTempClient("jtableTemplate3");  
    }
}

function continue1(element) {
    alert("continue1"); 
}

function continue2(element) {
    alert("continue2"); 
}


Below an example of server-template.

In this example you have two query that use search field "text2/City" , "text3/Country". It will be applied two different server template "view1", "view2", The result will be paged with size 10. There is also a "continueWith" callback that it will be called the first time.

$(document).ready(function () {
    var settings = {
        tpane2: 'gpane2',
        tcontainer: "jlist",    
        ttype: "2",
    };
    $("#gpane2").gridTemplate(settings, searchfrm).
         bind('databound', function (obj, evt) {
            var plugin = obj.context;
            if (plugin.templatename == "view3")
                $(".identity").css("color","blue");
            if (plugin.templatename == "view4")
                $(".identity").css("color","aqua");  
         }
    );
});

function searchfrm(context) {
    context.clearSearch();

    var text1 = $("#text1").val();
    var text2 = $("#text2").val();
    var text3=  $("#text3").val();
    
    context.beginWhere("and");
    if (text2!= "") {
        context.addWhereClause( "City"  , "=", text2);
    }      
    if (text3 != "") {
        context.addWhereClause("Country", "=", text3);
    }
    var r = context.endWhere();   
         
    if (text1 == "1") {
        context.from("/Grid2/GetDataJson").
               where(r.value,r.param).
               orderBy("CustomerID").
               pagingWithSize(10).
               applyTempServer("view1").
               continueWith(continue1);  
    }
    if (text1 == "2") {
        context.from("/Grid2/GetDataJson").
               where(r.value,r.param).
               orderBy("CustomerID").
               pagingWithSize(10).
               applyTempServer("view1").
               continueWith(continue2);  
    }
}

function continue1(element) {
    alert("continue1"); 
}

function continue2(element) {
    alert("continue2"); 
}


Below an example of linq query disabled.

As you can see in this example linq is disabled therefore the query params will be passed to the server as querystring param. You must compose a base querystring then the "PagingBase" object will add some additional params as: pagecurr, pagesize. In this case you can apply a server side template or a client side template.

$(document).ready(function () {
    var settings = {
        tpane2: 'gpane2',
        tcontainer: "jlist",
        tlinqEnabled: false,      
        ttype: "2",
    };
    $("#gpane2").gridTemplate(settings, searchfrm).
         bind('databound', function (obj, evt) {
           //alert("databound");
         }
    );
});

function searchfrm(context) {
    context.clearSearch();

    var swhere = "";

    var text1 = $("#text1").val();
    text1 = jQuery.trim(text1);
    if (text1!= "") {
        if (swhere == "")
            swhere = swhere + "campo1=" + text1;
        else swhere = swhere + "&campo1=" + text1;
    }   
    var text2 = $("#text2").val(); 
    text2 = jQuery.trim(text2); 
    if (text2 != "") {
        if (swhere == "")
            swhere = swhere + "campo2=" + text2;
        else swhere = swhere + "&campo2=" + text2;
    } 

    context.from("/Grid3/GetDataJson").pagingWithSize(10).
           where(swhere).applyTempServer("view1");
}


If linq is enabled will be created a object request message that will be serialized on the ajax url:

var message = { groupresult: false, enablepaging: false, select: { value: 'x', param: 'y' },
order: { value: 'x', param: 'y' }, where: { value: 'x', param: 'y', ptype: 'z' }, skip: 0, take: 9999 }

If linq is not enabled will be created a ajax url like this:

url?currpage=n&currsize=n&param1=valore2&param2=valore2"&orderby=value

the reply message is the same for both request (linq enabled/ linq not enabled):

{ "total": n, "records": n, "rows":[{},{}, ... ,{}] }

If you has request a client side template, the rows property will be a json array. If you has request a server side template, the rows property will be a html/text string.

As you can see if you have query linq enabled you will have select,where,orderby clause in a string format, if you have not query linq enabled you will not have select,where,orderby clause in a string format but instead you will use some querystring paramiter.

Controller with linq enabled
public ActionResult GetDataJson(RequestLinq linq)
 {
     linq.SetSecurity(true, true, true);
     var query = Repository.GetRepository<Customer>().Query();
     object data = this.TryApplyView(linq, query.JQuery(linq));
     return Json(data, JsonRequestBehavior.AllowGet); }


Controller with linq not enabled
public ActionResult GetDataJson(RequestRest rest)
 {
     rest.Operator = WhereOperator.And;
     rest.AddWhereMapping("City", "=", "campo1");
     rest.AddWhereMapping("Country", "=", "campo2");
     var query = Repository.GetRepository<Customer>().Query();
     object result = this.TryApplyView(rest, query.JQuery(rest));
     return Json(result, JsonRequestBehavior.AllowGet);
 }


As you can see on the controller action you have two differents objects "RequestLinq" and "RequestRest" that map: linq enabled/linq not enabled request. Both this objects have a common interface IRequestQuery that will be used to call all query methods. So you can have only one family of IQueriable methods common for every use case. Server side there is also some MVC filter attribute that provide some security feature. On the server if you use a view that has no navigation property you should not be allowed to navigate through all tables. If linq is not enabled you will not have select,where,orderby clause in a string format but instead you will have some classic querystring paramiter.

Knockout extension

In the release 4.0 I' added the support for "Knockout". Knockoutjs is a JavaScript library that makes it easier to create rich, desktop-like user interfaces with JavaScript and HTML /HTML5.

You can create a CRUD knockoutjs grid using the MVVM pattern (this works with linq enabled and linq not enabled). The plugin has some properties/methods: tformViewModel, tcreateViewModel, tcustomCallBack, tdeleteCallBack, tinsertCallBack, tupdateCallBack, tcancelCallBack, koWhereViewModel(), koWhereObjectAnd(), koWhereObjectOr(), koWhereString(), applyTempKo(), showGridKo(). There are also some html attributes: data-jcustomaction, data-jdeleteaction, data-jcreateaction, data-jmodifyaction, data-jinsertaction, data-jupdateaction, data-jcancelaction, data- data-jdetailaction, jdetailtemplate ... etc.

Knockout can be used into two separate places. 1) First in the search form. 2) Second in the layout template (main template grid, popup detail, popup create, popup edit/modify). In the first place, you need to specify a callback function that creates the viewmodel object of the search form through the "tformViewModel" properties. Then you will use the methods koWhereObjectAnd(), koWhereObjectOr() , koWhereString() and koWhereViewModel() to create the param that you will put into the where clause "where(expression, param)". In the second place you need to call the "applyTempKo(callBackMapKnock, callBackTemplate)" method on the query projection. This tell the plugin that convert the result set in an observable array and then apply a knockout template. If you call it without param, it will create a viewmodel with only properties automaticaly (with no method/command). If you use a callback function "callBackMapKnock" as first parameter, it will create a custom viewmodel with properties and methods. If you use a callback function "callBackTemplate" as second parameter it will apply the specified template logic.

Important: If you use knockout as form search viewmodel, you must apply a "viewmodel" class to the form search.

       <div>
        <table class="viewmodel">    
        <tr>
          <td>City: </td>
          <td><input id="text1" data-bind="value: campo1" /></td>
        </tr>  
        <tr>
          <td>Country: </td>
          <td><input id="text2" data-bind="value: campo2" /></td>
        </tr> 
        </table>
        <br /><br />
        <input id="gbutton6" type="button" value="Find" />
      </div>


In addition to the main template, there are three popup templates (detail/create/edit). The detail popup template will be showed when you press a link where there is a "data-jdetailaction" attribute and it work for all type of template. The create popup template will be showed when you press a link where there is a "data-jcreateaction" attribute and it work only with knockout. In this popup you can put some edit field. In this case you need to specify a callback function "tcreateViewModel" that create an empty viewmodel. The edit popup template will be showed when you press a link where there is a "data-jmodifyaction" attribute and it work only with knockout. In this popup you can put some edit field. On the main template you can put two link with the "data-jcustomaction", "data-jdeleteaction" attribute. When the link is pressed it call the callback function "tcustomCallBack", "tdeleteCallBack" (it work only with knockout).

When you open a edit/modify popup, the code will do a backup of the viewmodel value (string, number, boolean). So when you change the value of a field and close the popup without "save" it will restore the original value. This works if you use the "tcancelCallBack" attribute (that close the popup automaticaly). There is also a property "enableBackup" that disable this function. If you use the "tupdateCallBack" callback you must call $(...).gridTemplate("closeModifyDialogFeilure") / $(...).gridTemplate("closeModifyDialogSuccess") in the ajax callback. So if there are some error from the server the popup will not close automaticaly. If you close with $(...).gridTemplate("closeModifyDialogFeilure") will be restored data. If you close with $(...).gridTemplate( "closeModifyDialogSuccess") will not be restored data. It work fine only for no complex type (string, number, boolean). For complex type you should disable this function. There are also $(...).gridTemplate("closeCreateDialogFeilure") / $(...).gridTemplate("closeCreateDialogSuccess") that work with create popup, but in this case there are not a backup of data. By this library you can build a mvc ajax application using javascript and knockout.js/mvvm, you can create a crud knockout.js grid with filter, paging and sort functions.- CRUD knockoutjs grid .

There is also support for mvc3 unobtrusive jquery validation.
To enable it, you must set the property "tknockoutValidation=true" of the plugin properties (by default it is disabled), In this way you will have a mvc3 form validation (this it will work with standard and mobile apps). Then you must create a template like this:

<script id="jmodifyTemplate" type="text/x-jquery-tmpl">
@using (Html.BeginForm()) {
<div>
  <p>
    <b>CustomerID: </b>@Html.KoTextBoxFor(model => model.CustomerID)<br>
    @Html.ValidationMessageFor(model => model.CustomerID)
  </p>
  <p>
    <b>CompanyName: </b>@Html.KoTextBoxFor(model => model.CompanyName)<br>
    @Html.ValidationMessageFor(model => model.CompanyName)
  </p>
  <p>
    <b>ContactName: </b>@Html.KoTextBoxFor(model => model.ContactName)<br>
    @Html.ValidationMessageFor(model => model.ContactName)
  </p>
  <p>
    <b>City: </b>@Html.KoTextBoxFor(model => model.City)
    @Html.ValidationMessageFor(model => model.City)
  </p>
  <p>
    <b>Country: </b>@Html.KoTextBoxFor(model => model.Country)
    @Html.ValidationMessageFor(model => model.Country)
  </p>
</div>
}
</script>


As you can see, there are some custom mvc html helper. By this mvc html helper you can create the html input field with the "data-bind=value x" attribute. There are 5 type of mvc html helper:

Html.KoTextBoxFor(), Create a base input field with default binding (By default the value it is set in a string format). If you want a typed value on the viewmodel you must use the mvc html helper below. Inside the javascript file, there are custom knockout binding.

Html.KoTextBoxStrFor(), Create an input field that use Knockout custom binding string
Html.KoTextBoxIntFor(), Create an input field that use Knockout custom binding integer
Html.KoTextBoxDecFor(), Create an input field that use Knockout custom binding decimal
Html.KoTextBoxBolFor(), Create an input field that use Knockout custom binding boolean

In this way the values ​​will be automatically typed.

In a silverlight application, if you want that a viewmodel comunicates with each other in a loosely coupled mode, you must use a messenger framework as: MVVM Light Toolkit Messenger. In a javascript application there is a library that provides similar functionality: amplify.js. Amplify.js provides methods to facilitate the publish and subscribe messaging pattern in your front-end application. So a viewmodel can send a message that will be received from each other viewmodel. This will be usefull if you use knockoutjs in your application. See examples in the "javascript-to-IQueryable-extra-beta-0.85.zip" extras file download.

Classic page with knockout MVVM

jQuery mobile extension

In the release 3.0 I' added the support for "jQuery mobile".

<!-- page -->
<div id="page2" data-role="page" 
         data-jresturl="GetData1" 
         data-jpagesize="10" 
         data-jtypetemplate="client" 
         data-jnametemplate="template2" 
         data-jcontainer="c2"   
         data-jparam01="#text1" 
         data-jparam02="#text2" 
         data-jparam03="#text3" 
         data-jfselecting="selecting1" 
         data-jfdatabound="databound1" 
         data-jtouchaction="true">

    <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">	
        <p>Page content-2 goes here (@DateTime.Now).</p>
        <a data-rel="back" data-jbackcurrpage="true" 
            data-direction="reverse">Back</a><br /> 		
        <div>
           <table id="jtable2">
          <thead>
             <tr>
                 <td> &nbsp; </td>
                 <td data-sort="CustomerID"> CustomerID </td>
                 <td data-sort="ContactName"> ContactName </td>
                 <td data-sort="ContactTitle"> ContactTitle </td>
                 <td data-sort="City"> City </td>
                <td data-sort="Country"> Country </td>
             </tr>
         </thead>
         <tbody id="c2"></tbody>
         </table>
      </div>	
  </div>

  <div data-role="footer">
      <h4>Page Footer</h4>
  </div>
</div>
<!-- /page -->


The jquery mobile extension provides a plugin "mobileTemplate/zPageGrid3.js" that wrap the "PagingBase / zPageGrid1.js" object, so you can reuse the base functionalities. 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.

There are 3 different operation modes that will be managed through "jautocompose" attribute.

1) If "jautocompose" attribute is set to "true" the plugin will create automaticaly a request that will be send to the server. Into the where clause will be used the params specified by the "jparam01 / jparam05" attribute.

2) If "jautocompose" attribute is set to "false" you must create a request in the event handler "jselecting", then the plugin will send it to the server. Into the event-handler you can use the methods like these: where(), oderby(), applyTempClient(), applyTempServer(), applyTempKo().

3) You may also use 1) 2) together. If "jautocompose" attribute is set to "true" the plugin will create automaticaly a request. Into the where clause will be used the params specified by the "jparam01/jparam05" attribute. If you declare also an event-handler function through "jselecting" attribute, you can intercept the call (before it will be send to the server) and apply additional clauses: oderby(), applyTempClient(), applyTempServer() applyTempKo(). In this way for example, I can manage the creation of a viewmodel through applyTempKo() method and apply the MVVM pattern.

If you use the event-handler "jselecting" you must not use the from() method. The url will be set through "jresturl" attribute. When the plugin will receive a response from the server, it will show the data through a template. For a client/server side template that do not use knockout you must set these attributes on a div page: "jenableko='false'", jtypetemplate=value, jnametemplate=value, jcontainer=value. For a knockout template you must set these attributes on a div page: jenableko="true", jtypetemplate=value, jcontainer=value. The "jnametemplate='value'" will not be set. The tempate name is inside the template definition.

Through the "jsavecurritem='true'" you can transfer data between page (frompage and topage). You must put the "jsavecurritem='true'" on a page navigation link , in this way when you click on a navigation link, before jquery mobile "changepage" is raised, the data of the current item will be saved. So when you arrive to the new page you can get the data of the frompage. It will be used if in the div page there is "jparam01='*City'" or if you request a template and as url attribute you have put "jresturl='none'". In this second way, you can create a detail page, that show data that was retrived from the server through the frompage (previous page).

By default the query process start when you arrive to a new page automatically. If you set "jautorun='false'" the query process does not start when you arrive to a new page automatically. To start the query you must have a link with the "jselectaction='true'". In this way you can have a page with search form and result div in the same page.

The param passed to the event-handler "jselecting" it is a object like this: eventargs(fromPage(id, context) , toPage(id, context), curritem). id: the id of the page. context: the datasource of the page. curritem: the current item selected in the previous page through the "jsavecurritem" attribute.

There is also support for mvc3 jquery validation. There are 5 type of mvc html helper: Html.KoTextBoxFor(), Html.KoTextBoxStrFor(), Html.KoTextBoxIntFor(), Html.KoTextBoxDecFor() and Html.KoTextBoxBolFor(). This will be useful to create a knockout html input field (with typed bindings).

If you create the page elements dynamically (ajax request / listview) jquery mobile may not apply the page style. To apply jquery mobile styles 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 style (for you). 3) Use the knockout.js custom binding "jqmRefreshList" (if you use a listview widget ). Populate a jQuery mobile listview widget dynamically with an ajax call - Example

//
// Knockout custom binding / template
//
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();
        }
    }
};

<ul id="c2" data-role="listview" data-bind="jqmRefreshList: {name: 'temp2', foreach: $data}"></ul>


When you navigate through pages in forward direction, the plugin create a new url every time and send a new query to the MVC action method. It will be used the datalink to the previous page (to create the url will be used the datalink specified by the attributes jparam01/jparam05). If you press the back button, the page is already in the DOM, then you must set the "jbackcurrpage=true" attribute on the link with "data-rel=back". In this way the plugin will not send a new query to the MVC action method. If you would like to refresh the data (when you press the back button), you can set the "jrefreshonback=true" attribute on the link with "data-rel=back". Important: in back direction the datalink between pages it will not work.

You can also use PhoneGap to create your application (using jquery mobile with html5 and javascript); so you will build a cross-platform application ( Windows Phone, Android, iOS, etc ). To create your apps you can also use the MVVM pattern through the knockout.js library (Mvc/Ajax/PhoneGap application example)

jQuery mobile page with knockout MVVM


Plugin reference: panel - properties - methods.

The plugin manage 3 panels: The search panel, the result panel and the is loading panel.
The "settings" variable specify the setting required by the plugin.

When you set the plugin in the $(document).ready event you write something like this:
$(document).ready(function () {
    var settings = {
        tpane2: 'gpane2',
        tcontainer: "jpane",
        tcacheEnabled: false,        
        ttype: "2",
    };
    $("#gpane2").gridTemplate(settings, searchfrm).
         bind('databound', function (obj, evt) {
            var plugin = obj.context;
            if (plugin.templatename == "jtableTemplate1")
                $(".identity").css("color","blue");
            if (plugin.templatename == "jtableTemplate2")
                $(".identity").css("color","aqua");  
         }
    );
});

function searchfrm(context) {
    context.clearSearch();

}


When you click on the search button the plugin call the "searchfrm(context)". This method have a parameter "context" this is an istance of the internal context. You will compose the query by calling (the method below) on this object. When the "searchfrm(context)" return the query start. Below a list of the context's method.


Plugin for standard application (zPageGrid2.js) properties/methods

tconvertJsonDate, Convert automatically the date when it will be returned from the server. Default value "false". If you set tconvertJsonDate="true" every datetime field it will be converted in the corresponding string format.

tdomain, The url of the base domain if you use a cross-domain request.

ttype, Type of the search form "popup/hide".

tpane1, Div name of the "search-form" panel (default value 'gpane1').

tpane2, Div name of the "result" panel (default value 'gpane2').

tpane3, Div name of the "isloading" panel (default value 'gpane3').

tsearch1, The name of the button that show the search/form (default value 'gbutton1').

tsearch2, The name of the button that start the search/call (default value 'gbutton6').

tpageF, The name of the "first" button (default value 'gbutton2').

tpageP, The name of the "prev" button (default value 'gbutton3').

tpageN, The name of the "next" button (default value 'gbutton4').

tpageL, The name of the "last" button (default value 'gbutton5').

tcontainer, Id of the client/server template container.

ttemplate, Default client template name.

tdetailPanel, Id of the popup "detail" 'div'.

tdetailContainer, Id of the popup "detail" template container.

tdlgdetail, Properties of the popup "detail".

tcreatePanel (only knockoutjs), Id of the popup "create" 'div'.

tcreateContainer (only knockoutjs), Id of the popup "create" template container.

tdlgcreate (only knockoutjs), Properties of the popup "create".

tmodifyPanel (only knockoutjs), Id of the popup "modify" 'div'.

tmodifyContainer (only knockoutjs), Id of the popup "modify" template container.

tdlgmodify (only knockoutjs), Properties of the popup "modify".

tformViewModel (only knockoutjs), A function that create the search viewmodel object.

tcreateViewModel (only knockoutjs), A function that create the create viewmodel object.

tenableBackup (only knockoutjs), Boolean value. Enable/Disable backup funtionality. In a popup modify dialog if you change a value of a field and then close the popup without press "update", original values will be replaced.

tknockoutValidation (only knockoutjs), Boolean value. Enable/Disable support for mvc3 jquery validation.

tcustomCallBack (only knockoutjs), The function callback of the "data-jcustomaction" attribute. It must put on the main template.

tdeleteCallBack (only knockoutjs), The function callback of the "data-jdeleteaction" attribute. It must put on the main template.

tinsertCallBack (only knockoutjs), The function callback of the "data-jinsertaction" attribute. It must put on the modify template (tcreatePanel/tcreateConteiner).

tcancelCallBack (only knockoutjs), The function callback of the "data-jcancelaction" attribute. It must put on the modify template (tcreatePanel/tcreateConteiner).

tupdateCallBack (only knockoutjs), The function callback of the "data-jupdateaction" attribute. It must put on the modify template (tmodifyPanel/tmodifyConteiner).

tcancelCallBack (only knockoutjs), The function callback of the "data-jcancelaction" attribute. It must put on the modify template (tmodifyPanel/tmodifyConteiner).

turlpath, Default url rest path that is used by an ajax request.

tlinqEnabled, Enable/disable linq query.

tcacheEnabled, Enable/disable cache and prefetch.

tpage, Default page number.

getObjectInstance(), Get an instance of the plugin ( $(...).gridTemplate("getObjectInstance") )

getSource(), Get the current data source( $(...).gridTemplate("getSource") )

closeCreateDialogSuccess(),Close the create popup. ( $(...).gridTemplate("closeCreateDialogSuccess") ).

closeCreateDialogFeilure(),Close the create popup. ( $(...).gridTemplate("closeCreateDialogFeilure") ).

closeModifyDialogSuccess(), Close the edit/modify popup. When you open a edit/modify popup, the code do a backup of the viewmodel value (string, number, boolean). So when you change the value of a field and close the popup without "save" it will restore the original value. ( $(...).gridTemplate("closeModifyDialogSuccess") ).

closeModifyDialogFeilure(), Close the edit/modify popup applay the backup/restore behaviour. When you open a edit/modify popup, the code do a backup of the viewmodel value (string, number, boolean). So when you change the value of a field and close the popup without "save" it will restore the original value. ( $(...).gridTemplate("closeModifyDialogFeilure") ).

Event: "databound", Raise when databound.

Event: "isloading", Raise when isloading.

Event: "popupdialog", Raise when popup dialog is open.

Event: "popupcreate", Raise when popup create is open.

Event: "popupmodify", Raise when popup modify is open.


Plugin for jquery mobile (zPageGrid3.js) properties/methods

tdomain, The url of the base domain if you use a cross-domain request.

data-jbackcurrpage, (true/false) This property must be set on a link with "data-rel=back", this tell to the plugin that when you press a back button, it must not send a query. In this way the querys will be sent only in forward direction (not in back direction).

data-jrefreshonback, (true/false) This property must be set on a link with "data-rel=back" and work with the "jbackcurrpage" attribute. The property tell to the plugin that when you press a back button, it must refresh the datasource. It will be used the url stored into the datasource. It will not be created a new datasource (as in forward direction).

data-jrecreatestyle, Specify if recreate style is enabled. If "jrecreatestyle" attribute is set to "true" the plugin will recreate the page style. It will be called the "$('.ui-page-active').page('destroy').page()" method. Alternatively you can use a knockout.js custom binding "jqmRefreshList" (if you use a listview).

data-jhidebeforeajaxcall, Specify if the container panel must be hidden before an ajax call.

data-jautocompose, Specify if autocompose is enabled. If "jautocompose" attribute is set to "true" the plugin will create automaticaly a request that will be send to the server. If "jautocompose" attribute is set to "false" you must create a request in the event handler "jselecting", then the plugin will send it to the server.

data-jresturl, Specify the url.

data-jpagesize, Specify the pagesize. Optional, if not specified pagesize=9999 and paging is not enabled.

data-jenableko, Specify if knockout is enabled (true/false). If you specify "jenableko='false'" you must set "jtypetemplate='value'", "jnametemplate='value'", "jcontainer='value'". If you specify "jenableko='true'" you must set "jtypetemplate='client'", "jcontainer='value'", the "jnametemplate='value'" will not be set, the tempate name is inside the template definition.

data-jtypetemplate, Specify the template type (client jquery template/ server partialview). For knockout template you must set: "jenableko='true'" and "jtypetemplate='client'".

data-jnametemplate, Specify the template name. Used for client/server side template. For knockout template you must not use this attribute, the tempate name is inside the template definition.

data-jcontainer, This is the container of the template.

data-jparam01/data-jparam05, Specify searchs param. There are 3 type of param. 1) value: data-jparam01="germany", absolute value. 2) text: data-jparam01="#filed1" you reference an input tag of name "filed1". 3) data: data-jparam01="*country", you reference the field "country" of the previous page. In this case in the previous page you must have a link with this attribute data-jsavecurritem="true", this tell the plugin to transfer data from origin to destination page. In this way you create a data link between frompage and topage.

data-jorderby, Specify an order by clause.

data-jsavecurritem="true", It must put on a button/link template. It tell the framework that must pass the current dataitem to the next page. It saves the current item and then pass it through "changePage" event of jquery mobile.

data-jautorun="false", Default behavior is that a query/search begin when you go on a new page. On the page definitions tell the framework do not start query/search automaticaly when you go on a new page. The search start when you press a button/link where there is an attribute data-jselectaction="true".

data-jselectaction="true", It must put on a button/link searchform. The plugin register it and when you press the button/link a new query/search start. This overrides the default behavior where a new query/search start when you go on a new page.

data-jnavbaraction='action', The param action can be "refresh,pageF,pageP,pageN,pageL". It tells the plugin that this is a navigation button of the current datasource.

data-initialize, Specify initialize event. Raised when the datasource was created.

data-jfselecting, Specify selecting event. Raised before a request will be send to rhe server.

data-jfdatabound, Specify databound event.

data-jfisloading, Specify isloading event.


PagingBase object (zPageGrid1.js) properties/methods

convertJsonDate, Convert automatically the date when it will be returned from the server. Default value "false". If you set tconvertJsonDate="true" every datetime field it will be converted in the corresponding string format.

mainpane, Div name of the "result" panel (same value as plugin 'tpane2').

container, Id of the client/server template container.

template, Default client template name.

tdomain, The url of the base domain if you use a cross-domain request.

urlpath, Default url rest path that is used by an ajax request.

cacheEnabled, Enable/disable cache and prefetch.

linqEnabled, Enable/disable linq query.

detailPanel, Id of the popup "detail" 'div'.

detailContainer, Id of the popup "detail" template container.

dlgdetail, Properties of the popup "detail".

createPanel (only knockoutjs), Id of the popup "create" 'div'.

createContainer (only knockoutjs), Id of the popup "create" template container.

dlgcreate (only knockoutjs), Properties of the popup "create".

modifyPanel (only knockoutjs), Id of the popup "modify" 'div'.

modifyContainer (only knockoutjs), Id of the popup "modify" template container.

dlgmodify (only knockoutjs), Properties of the popup "modify".

formviewmodel (only knockoutjs), A function that create the search viewmodel object.

createViewModel (only knockoutjs), A function that create the create viewmodel object.

enableBackup (only knockoutjs), Boolean value. Enable/Disable backup funtionality. In a popup modify dialog if you change a value of a field and then close the popup without press "update", original values will be replaced.

knockoutValidation (only knockoutjs), Boolean value. Enable/Disable support for mvc3 jquery validation.

customCallBack (only knockoutjs), The function callback of the "data-jcustomaction" attribute. It must put on the main template.

deleteCallBack (only knockoutjs), The function callback of the "data-jdeleteaction" attribute. It must put on the main template.

insertCallBack (only knockoutjs), The function callback of the "data-jinsertaction" attribute. It must put on the create template (tcreatePanel/tcreateConteiner).

cancelCallBack (only knockoutjs), The function callback of the "data-jcancelaction" attribute. It must put on the create template (tcreatePanel/tcreateConteiner).

updateCallBack (only knockoutjs), The function callback of the "data-jupdateaction" attribute. It must put on the modify template (tmodifyPanel/tmodifyConteiner).

cancelCallBack (only knockoutjs), The function callback of the "data-jcancelaction" attribute. It must put on the modify template (tmodifyPanel/tmodifyConteiner).

closeCreateDialogSuccess(),Close the create popup. ( $(...).gridTemplate("closeCreateDialogSuccess") ).

closeCreateDialogFeilure(),Close the create popup. ( $(...).gridTemplate("closeCreateDialogFeilure") ).

closeModifyDialogSuccess(), Close the edit/modify popup. When you open a edit/modify popup, the code do a backup of the viewmodel value (string, number, boolean). So when you change the value of a field and close the popup without "save" it will restore the original value. ( $(...).gridTemplate("closeModifyDialogSuccess") ).

closeModifyDialogFeilure(), Close the edit/modify popup applay the backup/restore behaviour. When you open a edit/modify popup, the code do a backup of the viewmodel value (string, number, boolean). So when you change the value of a field and close the popup without "save" it will restore the original value. ( $(...).gridTemplate("closeModifyDialogFeilure") ).

koWhereObjectAnd() (only knockoutjs), Create a "where" clause with "AND" of the current viewmodel "formviewmodel". Used if linq is enabled. For complex clause you must create it manually. The object returned will be passed to the "where(expression, param)" method. It will return an object.

koWhereObjectStrAnd() (only knockoutjs), Create a "where" clause with "AND" of the current viewmodel "formviewmodel" of type String. Used if linq is enabled. For complex clause you must create it manually. The object returned will be passed to the "where(expression, param, type)" method. It will return an object. There are some other method like this (koWhereObjectIntAnd, koWhereObjectDblAnd, koWhereObjectDecAnd, koWhereObjectDatAnd, koWhereObjectBolAnd)

koWhereObjectOr() (only knockoutjs), Create a "where" clause with "OR" of the current viewmodel "formviewmodel". Used if linq is enabled. For complex clause you must create it manually. The object returned will be passed to the "where(expression, param)" method. It will return an object.

koWhereObjectStrOr() (only knockoutjs), Create a "where" clause with "OR" of the current viewmodel "formviewmodel" of type String. Used if linq is enabled. For complex clause you must create it manually. The object returned will be passed to the "where(expression, param, type)" method. It will return an object. There are some other method like this (koWhereObjectIntOr, koWhereObjectDblOr, koWhereObjectDecOr, koWhereObjectDatOr, koWhereObjectBolOr)

koWhereString() (only knockoutjs), Create a "where" clause of the current viewmodel "formviewmodel". Used if linq is not enabled. For complex clause you must create it manually. It will return a querystring.

koWhereViewModel() (only knockoutjs), Return the form search viewmodel object.

beginWhere(), If linq is enabled: begin a where clause.

addWhereClause(), If linq is enabled: add a where clause ( ex. addWhereClause("CustomerID","=","value") ).

addWhereClauseStr(), If linq is enabled: add a where clause of type String ( ex. addWhereClauseStr("CustomerID","=","value") ). There are some other method like this ( addWhereClauseInt() ,addWhereClauseDbl(),addWhereClauseDec(), addWhereClauseDat(), addWhereClauseBol() )

endWhere(), If linq is enabled: close and return a where clause. It will be passed in the "where(expression, param)".

source, The value of the current datasource (if client-template: is an json array/if server-template is a htmltext/if knockout: is an observable array).

getCurrentTemplate(), Return the current client side template name.

clearSearch(), Reset the object for a new search.

hasWaitingRequest(), Return 'true' if there are waiting request.

loadData(), Start a request, create a message and send it to the server.

refresh(), Refresh the current page number.

goPage(page), Go to page number.

pageF(), Go to first page.

pageP(), Go to prev page.

pageN(), Go to next page.

pageL(), Go to last page.

from(resturl, group), Used to create a query, specify the source data as a rest url.

pagingWithSize(value), Set the paging size result. This method tell the plugin that you want a paging result set.

where(expression, param, ptype), Set the where clause. If linq enabled expression and param are required (values returned from "endWhere()", "koWhereObjectAnd()", "koWhereObjectOr()"). The argument ptype is an optional array, if specified set the type/value of the param array ( where(r.value,r.param,r.ptype) ). If linq is not enabled you must set only expression as string ("manual string" or "koWhereString()").

orderBy(expression), Set the orderby clause (ex. "orderBy('Country asc, City desc')"). Param is optional.

select(expression), Set the select clause (ex. "select('new(CustomerID, Country)')"). Param is optional.

take(value), Specifiy the take value. If you use this method the paging function will be disabled. This is the opposite of pagingWithSize(value).

skip(value), Specifiy the skip value. If you use this method the paging function will be disabled. This is the opposite of pagingWithSize(value).

applyTempClient(value), Apply a client side templete (jquery template). If you do not specify the param, will be used the default template "template". If you specify a "string" will be a template-name. If you specify a "function" will be a call back function, inside of which, there is the logic that applies the template.

applyTempServer(value), Apply a server side templete (mvc partialview). If you do not specify the param, will be used the default mvc action view. If you specify a "string" will be a template-name. If you specify a "function" will be a call back function, inside of which, there is the logic that applies the template.

applyTempKo(callBackMapKnock,callBackTemplate), Request a knockout-template. Params are optionals. Param "callBackMapKnock" is the callback function that trasform the array returned from the server to observable, applying a custom logic (by default will be converted to observable). Param "callBackTemplate" is the callback function inside of which, there is the logic that applies the template.

showGridKo(data,callBackMapKnock,callBackTemplate), Request a knockout-template. data is an array of json data. callBackMapKnock,callBackTemplate params are optionals. Param "callBackMapKnock" is the callback function that trasform the array returned from the server to observable, applying a custom logic (by default will be converted to observable). Param "callBackTemplate" is the callback function inside of which, there is the logic that applies the template. This is useful if you works with in memory data, you can filter it using linq.js - LINQ for JavaScript.

continueWith(callBackfunc), Set the callback function that will be called the first time.

exec(), Call the "loadData()" and return "this".

Event: "databound", Raise when databound.

Event: "isloading", Raise when isloading.

Event: "popupdialog", Raise when popup dialog is open.

Event: "popupcreate", Raise when popup create is open.

Event: "popupmodify", Raise when popup modify is open.


Callback arguments (customCallBack,deleteCallBack,insertCallBack,updateCallBack,cancelCallBack)

element (only knockoutjs), The html element.

context (only knockoutjs), The PagingBase object.

dataitemJs (only knockoutjs), The viemodel object (javascript).

dataitemKo (only knockoutjs), The viemodel object (observable).

removeCurrent() (only knockoutjs), Remove current object from the observable array.

currentIndex() (only knockoutjs), The index of the current object.

getArrayJs() (only knockoutjs), Return an array in "javascript" format.

getArrayKo() (only knockoutjs), Return an array in "observable" format.


HTML 5 "data-" attribute

html5 / data-jdetailaction, Must put it on a link. It tell the plugin that the link show a detail popup. It must put on the main template.

html5 / data-jdetailtemplate, Must put it on a link. Name of the popup detail template. It must put on the main template.

html5 / data-jmodifyaction, Must put it on a link. It tell the plugin that the link show a edit popup. It must put on the main template.

html5 / data-jcustomaction, Must put it on a button/link. It call the "tcustomCallBack" callback. It must put on the main template.

html5 / data-jdeleteaction, Must put it on a button/link. It call the "tdeleteCallBack" callback. It must put on the main template.

html5 / data-jinsertaction, Must put it on a button/link. It call the "tinsertCallBack" callback. It must put on the modify template (tcreatePanel/tcreateConteiner).

html5 / data-jcancelaction, Must put it on a button/link. It call the "tcancelCallBack" callback. It must put on the modify template (tcreatePanel/tcreateConteiner).

html5 / data-jupdateaction, Must put it on a button/link. It call the "tupdateCallBack" callback. It must put on the modify template (tmodifyPanel/tmodifyConteiner).

html5 / data-jcancelaction, Must put it on a button/link. It call the "tcancelCallBack" callback. It must put on the modify template (tmodifyPanel/tmodifyConteiner).


With javascript to iqueryable framework, you can build a javascript / html5 / css3 application that implement the MVVM pattern for standard and mobile scenarios. Youtube video


http://jquery.com/
http://jquerymobile.com/
http://knockoutjs.com/
http://blog.stevensanderson.com/
http://www.knockmeout.net/
http://www.asp.net/mvc
http://www.torinotechnologiesgroup.it
http://www.dotnettoscana.org
My windows phone marketplace
Linkedin / Stefano Marchisio
Blog

Video
ASP.NET MVC With KnockoutJS (by Brandon Satrom)
Helping you build dynamic JavaScript UI with MVVM and ASP.NET (by Steve Sanderson)
Building HTML5 and JavaScript Apps with MVVM and Knockout (by John Papa)


Last edited Jul 31 at 8:19 AM by mastefano64, version 272