Monday, July 11, 2011

Knockout.js Sample

According to Knockout ,KO is a nice javascript library that helps to create rich UI with a clean underlying data model. we can create a view data model and bind that model to our UI. so any time that a section of UI changes, the view model will be changed automatically and reverse. knockout is promoting Model-View-ViewModel (MVVM) design pattern like in silverlight. When using KO, the view is simply your HTML document with declarative bindings to link it to the view model. Alternatively, we can use templates that generate HTML using data from your view model.
I am trying to give it a shot and create a sample application using knockout.js
//------------------------------------------------------
PersonHandler.ashx
//------------------------------------------------------
<%@ WebHandler Language="C#" Class="PersonHandler" %>

using System;
using System.Web;
using System.Collections.Generic;
using System.Runtime.Serialization.Json;
using System.IO;

public class Person
{
    public string Name { get; set; }
    public string Phone { get; set; }
}

public class PersonHandler : IHttpHandler {
    
    public void ProcessRequest (HttpContext context) {

        //If the method is POST
        //string name = context.Request.Form["name"];
        
        //If the method is GET
        //string name = context.Request.QueryString["name"];

        if (context.Request.RequestType == "GET")
        {
            if (context.Request.QueryString["pwd"] != "123")
            {
                context.Response.ContentType = "application/json";
                context.Response.Write("");
                context.Response.End();
            }

            List<Person> list = new List<Person>();
            list.Add(new Person() { Name = "aaa", Phone = "1111" });
            list.Add(new Person() { Name = "bbb", Phone = "222" });
            list.Add(new Person() { Name = "cccc", Phone = "3333" });

            string str = "";
            DataContractJsonSerializer serilizer = new DataContractJsonSerializer(typeof(List<Person>));
            using (MemoryStream ms = new MemoryStream())
            {
                serilizer.WriteObject(ms, list);
                ms.Position = 0;
                StreamReader sr = new StreamReader(ms);
                str = sr.ReadToEnd();
                sr.Dispose();
            }

            context.Response.ContentType = "application/json";
            
            context.Response.Write(str);
            context.Response.End();
        }
        else
        {
            //RequestType is POST
            string json = context.Request.Form["persons"];

            DataContractJsonSerializer serilizer = new DataContractJsonSerializer(typeof(List<Person>));
            System.Text.UTF8Encoding en = new System.Text.UTF8Encoding();
            int count = 0;
            using (MemoryStream ms = new MemoryStream(en.GetBytes(json)))
            {
                List<Person> list = (List<Person>) serilizer.ReadObject(ms);
                count = list.Count;
            }
            
            context.Response.ContentType = "application/json";
            context.Response.Write("\"" + count.ToString() + " saved successfully\"");
            context.Response.End();
        }
    }
 
    public bool IsReusable {
        get {
            return false;
        }
    }

}
//----------------------------------------------------------------------
lookup.html page using jquery ajax to call the server http handler.
//----------------------------------------------------------------------
<!DOCTYPE html>
<html>
<head>
    <title></title>    
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.1.min.js" type="text/javascript"></script>
    <script src="http://ajax.aspnetcdn.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js" type="text/javascript"></script>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/knockout/1.2.1/knockout-min.js" type="text/javascript"></script>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js" type="text/javascript"></script>

    <script src="https://raw.github.com/riklomas/quicksearch/master/jquery.quicksearch.js" type="text/javascript"></script>

    <script type="text/javascript">

        var viewModel;

        $(document).ready(function () {

            viewModel = {
                // persons: new ko.observableArray([{ Name: ko.observable("aaaaa"), Phone: ko.observable("111111") },
                //                                  { Name: ko.observable("bbbb"), Phone: ko.observable("222222") }
                //                                ]),
                //                
                persons: new ko.observableArray([]),


                addPerson: function () {
                    if (this.itemToAdd().Name().length == 0 || this.itemToAdd().Phone().length == 0) {
                        alert("required missing!");
                        return;
                    }
                    for (var x in this.persons()) {
                        if (this.persons()[x].Name() == this.itemToAdd().Name() && this.persons()[x].Phone() == this.itemToAdd().Phone()) {
                            alert("name must be unique!");
                            return;
                        }
                    }
                    this.persons.push({ Name: new ko.observable(this.itemToAdd().Name()), Phone: new ko.observable(this.itemToAdd().Phone()) });
                    this.itemToAdd().Name("");
                    this.itemToAdd().Phone("");
                },

                itemToAdd: new ko.observable({ Name: ko.observable(""), Phone: ko.observable("") }),

                removePerson: function (person) { if (window.confirm("Are you sure to delete?") == true) { this.persons.remove(person); } },

                updatePerson: function (person) {
                    //notify the subscribers to update
                    //this.persons.valueHasMutated();
                    //ko.applyBindings(viewModel, document.getElementById('lst'));
                },

                load: function () {
                    var pwd = $("#txtPwd").val();
                    $.ajax({
                        type: "GET",
                        url: "PersonHandler.ashx?pwd=" + pwd,
                        //contentType: "application/json",
                        //dataType: "json",
                        //data: "{'pwd':'" + pwd + "'}",
                        success: function (result) {
                            //var json = eval('(' + result.d + ')');
                            if (result == "") {
                                viewModel.isAuthenticated(false);
                                viewModel.persons([]);
                                return;
                            }
                            
                            viewModel.isAuthenticated(true);
                            viewModel.persons([]);

                            for (var i = 0; i < result.length; i++) {
                                viewModel.persons.push({ Name: new ko.observable(result[i].Name), Phone: new ko.observable(result[i].Phone) });
                            }
                            //ToDo QuickSearch
                            $('#txtSearch').quicksearch('#tbl tbody tr');

                        },
                        error: function (errMsg) {
                            alert(errMsg);
                        }
                    });

                },

                save: function () {
                    var jsonVal = ko.toJSON(viewModel.persons());

                    $.ajax({
                        type: "POST",
                        url: "PersonHandler.ashx",
                        //contentType: "application/json",
                        dataType: "json",
                        data: { "persons": jsonVal },
                        success: function (result) {
                            alert(result);
                        },
                        error: function (errMsg) {
                            alert(errMsg);
                        }
                    });

                },

                isAuthenticated: new ko.observable(false),

                editRowId: new ko.observable(""),

                getUniqueName: function (item) { return item.Name() + item.Phone(); },

                beginEdit: function (obj) {
                    this.editRowId(obj.Name() + obj.Phone());
                },

                cancelEdit: function (obj) {
                    this.editRowId("");
                }
            };

            ko.applyBindings(viewModel);
        });
    </script>
      
    <script type="text/html" id="myTemplate">
        <tr>
            {{if $item.editRowId() == $item.getUniqueName($item.data) }}
                <td><input type="text" data-bind="value: Name"/></td>
                <td><input type="text" data-bind="value: Phone"/></td>
                <td><a href="#" data-bind="click: function() { viewModel.updatePerson($item.data) }">Update</a></td> 
                <td><a href="#" data-bind="click: function() { viewModel.cancelEdit(this) }">Cancel</a></td>    
           {{else}}
               <td><span data-bind="text: Name"></td>
                <td><span data-bind="text: Phone"></span></td>
                <td><a href="#" data-bind="click: function() { viewModel.beginEdit(this) }">Edit</a></td>
                <td><a href="#" data-bind="click: function() { viewModel.removePerson($item.data) }">Delete</a></td> 
           {{/if}}
         </tr>    
     </script>

</head>
<body>
    Password:&nbsp;&nbsp;<input type="password" id="txtPwd" />&nbsp;&nbsp;
    <input type="button" value="Load Data" data-bind="click: load" />
    <br />
    <br />
    <div data-bind="visible: isAuthenticated">
        <fieldset style="width:300px">
            <legend>Add Names</legend>
            <label for="txtName">Name</label>
            <input id="txtName" type="text" data-bind="value: viewModel.itemToAdd().Name"  />
            <br />
            <label for="txtPhone">Phone</label>
            <input type="text" id="txtPhone" data-bind="value: viewModel.itemToAdd().Phone"  />
            <br />
            <input id="btnAdd" type="button" value="Add To List" data-bind="click: addPerson" />
        </fieldset>
    </div>
    <div data-bind="visible: persons().length > 0">
        <br />
<!--        Existing Names:<br />
        <select id="lst" name="lst" multiple="multiple" data-bind="options: persons, optionsText: function(item){ return item.fname() + ' ' + item.lname()}, value: 'fname'">
        </select>-->
        <br />
        Search:&nbsp;<input type="text" id="txtSearch" />
        <br />
        <table id="tbl" width="450" cellspacing="1" cellpadding="3" data-bind="visible: persons().length > 0">
            <thead>
            <tr style="Color:#ffffff;background-color:#344262">
                <th>Name</th>
                <th>Phone</th>
                <th>&nbsp;</th>
                <th>&nbsp;</th>
            </tr>
            </thead>
            <tbody data-bind="template: {name:'myTemplate', foreach: persons, templateOptions: { editRowId : editRowId, getUniqueName: getUniqueName} }" ></tbody>
        </table>
        <br />
        <input type="button" id="btnSave" value="Save list" data-bind="click: save, enable: persons().length > 0" />
    </div>
</body>
</html>