Saturday, 24 August 2013

Knockout: Built in bindings

Introduction:
The Built in bindings are built into knockout and facilitate binding to attribute for elements. We can use multiple bindings separated by commas.

The text binding:
It causes the associated DOM element to display text value.

The viewmodel
var viewModel = {
         message: "text binding message"
};

The html
The message is: <span data-bind="text: message"></span>

The output
The message is: text binding message

The html binding:
It causes the associated DOM element to display HTML text.

The viewmodel
var viewModel = {
         htmlMessage: "<p>html binding message</p>"
};

The html
The message is: <span data-bind="html: htmlMessage"></span>

The output
The message is: <p>html binding message</p>

The visible binding:
It causes the associated DOM element to become hidden or visible.

The viewmodel
var viewModel = {
         isShowMessage: true
};

The html
<div data-bind="visible: isShowMessage">
    This message will be shown only when "isShowMessage" is true.
</div>

The output
This message will be shown only when "isShowMessage" is true.

The css binding:
It adds or removes css classes to the associated DOM element.

The style
.profitWarning {
          color: Red;
 }

The viewmodel
var viewModel = {
         profit: 99
};

The html
<div data-bind="css: { profitWarning: profit < 100 }">
 Profit Information
</div>

The output
Profit Information

The style binding:
It adds or removes styles to the associated DOM element.

The viewmodel
var viewModel = {
         profit: 99
};

The html
<div data-bind="style: { color: profit < 100 ? 'red' : 'black' }">
 Profit Information
</div>

The output
Profit Information

The attr binding:
It sets the value of any attribute for the associated DOM element.

The viewmodel
var viewModel = {
     url: "https://www.google.co.in",
     details: "google web site"
};

The html
<a data-bind="attr: { href: url, title: details }">Google link </a>

The output

The click binding:
It adds a click event handler to the associated DOM element.

The viewmodel
var viewModel = {
     showAlert: function() {
           alert('hi');
      }
 };

The html
<button data-bind="click: showAlert">Click me</button>

The output
It will show a button and after clicking on this button it will show alert message.

The event binding:
It allows to add an event handler for a specified event to the associated DOM element. This can be used to bind to any event, such as click,  keypress, mouseover or mouseout.

The viewmodel
var viewModel = {
      showAlert: function() {
        alert('hi');
     }
 };

The html
<div data-bind="event: { mouseover: showAlert }">
 Mouse over me
</div>

The output
It will show message "Mouse over me" and on mouse over of this it will show alert .

The submit binding:
It adds an event handler so that the javascript function will be invoked when the associated DOM element is submitted. We typically use this on form elements.

The viewmodel
var viewModel = {
     showAlert: function() {
          alert('hi');
    }
};

The html
<form data-bind="submit: showAlert">
 <button type="submit">Submit</button>
</form>

The output
It will show submit button and on click on this it will show alert .

The enable binding:
It causes the associated DOM element to be enabled when the parameter value is true. This is useful with elements like input, select and textarea.

The viewmodel
var viewModel = {
        isEnabled: false
};

The html
<input type='text' data-bind="enable: isEnabled" />

The output
It will show textbox disabled as the isEnabled is false.

The disable binding:
It causes the associated DOM element to be disabled when the parameter value is true.This is useful with elements like input, select and textarea.

The viewmodel
var viewModel = {
      isDisabled: true
};

The html
<input type='text' data-bind="disable: isDisabled" />

The output
It will show textbox disabled as the isDisabled is true.

The value binding:
It is like text binding, but the only difference is that it is a two-way binding. This is useful with elements like input, select, textarea.
-> The associated element value will be updated when viewmodel property will change.
-> When the user edits the value in the associated element, it updates the value of view model property.

The viewmodel
var viewModel = {
        userName: ko.observable("Rakesh")
};

The html
Login name: <input data-bind="value: userName" />

The output
Login Name: Rakesh

The hasFocus binding:
It is a two-way binding.
-> The associated element will become focused or unfocused according to viewmodel property is true or false.
-> The viewmodel property will be set true or false when user manually focuses or unfocuses the associated element.

The viewmodel
var viewModel = {
      isFocus: true
};

The html
<input data-bind="hasFocus: isFocus" />

The output
It will display textbox with focus as viewmodel property "isFocus" is true.

The checked binding:
It links a checkable form control(a checkbox or a radio button) with a viewmodel property.

The viewmodel
var viewModel = {
      isChecked: true
};

The html
<input type="checkbox" data-bind="checked: isChecked" />

The output
It will display checkbox with checked as viewmodel property "isChecked" is true.

The options binding:
This binding is only used with dropdownlist. It is used to populate items in dropdownlist.

The viewmodel
var viewModel = {
       availableCountries : ko.observableArray(['France', 'Germany', 'Spain'])
};

The html
<select data-bind="options: availableCountries" size="5" multiple="true"></select>

The output
It will display dropdownlist with items.

The selectedOptions binding:
It controls the selected item in dropdownlist.

The viewmodel
var viewModel = {
       availableCountries : ko.observableArray(['France', 'Germany', 'Spain']),
       chosenCountries : ko.observableArray(['Germany'])
};

The html
<select data-bind="options: availableCountries, selectedOptions: chosenCountries" size="5" multiple="true"></select>

The output
It will display dropdownlist with selected item "Germany".

The uniqueName binding:
It sets the name attribute of associated element to some unique string value if this element doesn't have name attribute.

The foreach binding:
It duplicates a section for each entry in an array.
The viewmodel
var viewModel = {
     people: [
         { firstName: 'Bert', lastName: 'Bertington' },
         { firstName: 'Charles', lastName: 'Charlesforth' },
         { firstName: 'Denise', lastName: 'Dentiste' }
     ]
};

The html
<table>
  <thead>
   <tr>
     <th>First name</th><th>Last name</th>
  </tr>
  </thead>
  <tbody data-bind="foreach: people">
   <tr>
    <td data-bind="text: firstName"></td>
    <td data-bind="text: lastName"></td>
    </tr>
   </tbody>
</table>

The output
First name        Last name
Bert                 Bertington
Charles            Charlesforth
Denise             Dentiste

The if binding:
It causes a section to appear if a specified expression evaluates to true.

The viewmodel
var viewModel = {
       displayMessage : true
};

The html
<div data-bind="if: displayMessage">
Here is a message.
</div>

The output
It shows message "Here is a message" as the viewmodel property displayMessage is true.

The ifnot binding:
It is invert of if binding.

The viewmodel
var viewModel = {
       displayMessage : true
};

The html
<div data-bind="ifnot: !displayMessage">
Here is a message.
</div>

The output
It will show message "Here is a message" as the viewmodel property displayMessage is true.

The with binding:
It  creates a new binding context, so that descendant elements are bound in the context of a specified object.
The viewmodel
var viewModel = {
      person: {
           firstName: "Rakesh", lastName: "Nayak"
      }
};
The html
</div> <p data-bind="with: coords">
 First Name: <span data-bind="text: firstName"></span>,
 Last Name: <span data-bind="text: lastName">
</span> </p>

The output
First Name: Rakesh, Last Name: Nayak

Summary:
In this blog, I explained all built in bindings in knockout.





Tuesday, 20 August 2013

Knockout: Knockout and MVVM pattern

Introduction:
Knockout.js is a javascript library that helps us to create rich and responsive use interfaces with a clean data model.

Features:
1. Declarative Bindings - A simple way to bind a part of UI to data model.
2. Dependency Tracking - Automatically updates right part of UI when data model changes.
3. Templating - A simple way to make nested UIs as a function of view model data.
4. Trivially Extensible - With a just few line of code, we can implement custom behaviors as new declarative binding for easy reuse.

Benefits:
1. Free and open source.
2. Pure javascript - works with any client side or server side technology.
3. Small and lightweight - 40kb, around 30kb after gzipping.
4. It has no dependencies.
5. It works with any mainstream browser ((IE 6+, Firefox 2+, Chrome, Safari, others).

 Knockout.js simplifies javascript UIs by applying the MVVM pattern.

What is MVVM pattern?
MVVM was originally defined by Microsoft for use with Windows Presentation Foundation (WPF) and Silverlight.

In recent years, MVVM has been implemented in JavaScript in the form of structural frameworks such as KnockoutJS, Kendo MVVM and Knockback.js.

Model-View-View Model (MVVM) is a design pattern to clearly separate the development of user-interfaces from  the business logic and behavior in an application.


A Model:
It is a set of classes representing the data comes from services or database. We usually make ajax call to some server side code to read or write the data.

A ViewModel:
It is a pure-code representation of the data and operations on a UI

We can write a View-Model just like declaring any javascript object:
var myViewModel = {
    personName: 'Rakesh',
    personAge: 123
};


A View:
A visible, interactive UI representing the state of the view model. It displays information from the view model, sends commands to the view model (e.g., when the user clicks buttons), and updates whenever the state of the view model changes.

We can write it as HTML:
The name is <span data-bind="text: personName"></span>

It will display the output as:
The name is Rakesh

Activating Knockout:
Data-bind attribute - The data-bind attribute isn’t native to HTML, though it is perfectly OK (it’s strictly used in HTML 5, and causes no problems with HTML 4 )

We need to activate knockout to make data-bind attribute effect:
ko.applyBindings(myViewModel);


If you want to bind model to a part of view:
ko.applyBindings(myViewModel, document.getElementById('someElementId'));
ko.applyBindings(myViewModel, $('#someElementId')[0]);

Summary:
In this blog I explained the features and benefits of  knockout.js and how knockout.js facilitates MVVM pattern.


Monday, 22 October 2012

Knockout: Pagination using knockoutjs and ASP.NET Web API

Introduction

In this article, I am trying to implement HTML table as a grid with pagination using knockoutjs. This is the easiest way to get page wise data from server using ASP.NET Web API (or WCF service) and display them as a  grid.



Before we jump in to this article, let's have a look at the standard definition of Knockoutjs and ASP.NET Web API:

Knockoutjs is a JavaScript library that helps you to create rich, responsive displays and editor user interfaces with a clean underlying data model. Any time you have sections of UI that update dynamically (e.g., changing depending on user’s actions or when an external data source changes), KO can help you implement it more simply and maintainably.


ASP.NET Web API is a framework that makes it easy to build HTTP services that reach a broad range of clients, including browsers and mobile devices. ASP.NET Web API is an ideal platform for building RESTful applications on the .NET Framework.
----------------------------------------------------------------------------------------------------------

Define a PagedObservable

To get page wise data , create a javascript file pagedobservable.js and write the following codes:

(function (window, ko) {
    window.Utils = window.Utils || {};
    window.Utils.pagedObservable = function (options) {
        options = options || {};
        var _allData = ko.observableArray(), //the data collection to dispaly in grid
         
            _columns = ko.observableArray(options.columns || []), //the columns of grid
 
          _pageSize = ko.observable(options.pageSize || 10), //the size of the pages to display
          _pageSizes = ko.observable(options.pageSizes || []), //the size of the pages to display
          _pageIndex = ko.observable(1), //the index of the current page
          _pageCount = ko.observable(0), //the number of pages
         
           _totalRecords = ko.observable(0), //the number of records
         
           _sortName = ko.observable(options.sortName || ''), //the sort column name
         
           _sortOrder = ko.observable(options.sortOrder || 'asc'), //the sort order
         //move to the next page
       _nextPage = function () {
           if (_pageIndex() < _pageCount()) {
               _pageIndex(parseInt(_pageIndex()) + 1);
           }
       },
   //move to the previous page
       _previousPage = function () {
           if (_pageIndex() > 1) {
               _pageIndex(_pageIndex() - 1);
           }
       },
            //move to first page
            _firstPage = function () {
                if (_pageIndex() > 1) {
                    _pageIndex(1);
                }
            },
            //move to last page
            _lastPage = function () {
                if (_pageIndex() < _pageCount()) {
                    _pageIndex(_pageCount());
                }
            },
            //sort a column
            _sort = function (column) {
                _sortName(column.index);
                _sortOrder(_sortOrder() === 'asc' ? 'desc' : 'asc');
                _pageIndex(1);
                _loadFromServer();
            },
            //the message for record info
            _recordMessage = ko.computed(function () {
                if (_allData().length > 0) {
                    return 'Records ' + ((_pageIndex() - 1) * _pageSize() + 1) + ' - ' + (_pageIndex() < _pageCount() ? _pageIndex() * _pageSize() : _totalRecords()) + ' of ' + _totalRecords();
                }
                else {
                    return 'No records';
                }
            }),
            //the message for page info
            _pageMessage = ko.computed(function () {
                if (_allData().length > 0) {
                    return 'Page ' + _pageIndex() + ' of ' + _pageCount();
                }
                else {
                    return 'No pages';
                }
            }),
            //service url
            _serviceURL = ko.computed(function () {
                return options.serviceURL + (options.serviceURL.indexOf('?') != -1 ? "&" : "?") + "sidx=" + _sortName() + "&sord=" + _sortOrder() + "&page=" + _pageIndex() + "&rows=" + _pageSize();
            }, this),
           //load data from server
            _loadFromServer = function () {
                $.getJSON(_serviceURL(), function (data) {
                    if (data != null) {
                        _totalRecords(data.records);
                        _pageCount(data.total);
                        _allData(data.rows || []);
                    }
                    else {
                        _totalRecords(0);
                        _pageCount(0);
                        _allData([]);
                    }
                });
            };
        _pageIndex.subscribe(function () {
            _pageIndex() < 1 ? _pageIndex(1) :  _loadFromServer();
        });
        _pageSize.subscribe(function () {
            _pageIndex() != 1 ? _pageIndex(1) : _loadFromServer();
        });
        _loadFromServer();
        //public members
        this.columns = _columns;
        this.rows = _allData;
        this.totalRecords = _totalRecords;
        this.pageSize = _pageSize;
        this.pageSizes = _pageSizes;
        this.pageIndex = _pageIndex;
        this.pageCount = _pageCount;
        this.nextPage = _nextPage;
        this.previousPage = _previousPage;
        this.firstPage = _firstPage,
        this.lastPage = _lastPage,
        this.sortOrder = _sortOrder;
        this.sortName = _sortName;
        this.sort = _sort;
        this.recordMessage = _recordMessage;
        this.pageMessage = _pageMessage;
        this.load = _loadFromServer;
    };
}(window, ko));

The new instance exposes a number of properties to support paging:

  • columns– An observableArray instance which helps to display column information. 
  • rows– An observableArray instance exposing the page wise data.
  • totalRecords– An observable instance exposing the total no of records.
  • pageSize – An observable instance containing the number of items per page.
  • pageSizes – An observableArray instance exposing the page sizes user want to display on dropdownlist in grid. 
  • pageIndex – An observable instance containing the current page index.
  • pageCount – A computed observable that returns the number of pages.
  • rows – An observableArray instance that contains the current page data.
  • previousPage – A function that moves to the previous page.
  • nextPage – A function that moves to the next page.
  • firstPage – A function that moves to the first page.
  • lastPage – A function that moves to the last page.
  • recordMessage - It shows current record range information(ex: Records 1 - 10 of 14).
  • pageMessage - It shows current page index information(ex: Page 1 of 2).
  • load - It loads page wise data from server.
------------------------------------------------------------------------------------------------------------

Define a ViewModel

To make use of above PagedObservable, create an instance of Utils.pagedObservable as below:


var API_URL = "../api/contacts/";

var ViewModel = function () {

    this.pagedList = new Utils.pagedObservable({
        pageSize: 10,
        pageSizes: [5, 10, 15],
        sortName: 'Id',
        sortOrder: 'asc',
        columns: [{ name: 'ID', index: 'Id', sortable: true, width: '10%' },
                  { name: 'First Name', index: 'FirstName', sortable: true, width: '25%' },
                  { name: 'Last Name', index: 'LastName', sortable: true, width: '25%' },
                  { name: 'Phone', index: 'Phone', sortable: true, width: '25%' },
                  { name: '', index: '', sortable: false, width: '15%' }
        ],
        serviceURL: API_URL
    });

Let's explain the properties used in 
Utils.pagedObservable so that we can costomize according to our requirement:
  • pageSize: It is used to set default page size.
  • paeSizes: It is used to set page sizes which is displayed as dropdownlist in grid.
  • sortName: It is used to set default sort column name.
  • sortOrder: It is used to set sort order.
  • columns: It is used to set the columns name(which will be displayed in the header of grid), index(which is used to sort column), sortable(which is used to enable/disable sort functionality for a particular column) and width(which is used to set width of each column).
  • serviceURL: It is used to set the API controller url which is used to retrieve server data.
------------------------------------------------------------------------------------------------------------

Define a View

To bind the exposed properties to view, write some simple HTML:

<table class="table table-bordered table-condensed table-hover" data-bind="with: pagedList">
                <thead class="btn-primary">
                    <tr>
                        <!-- ko foreach: columns -->
                        <th data-bind="click: sortable ? $parent.sort : '', style: { width: width, cursor: sortable ? 'pointer' : '' }">
                            <span data-bind="text: name"></span>

                            <span class="icon-white icon-circle-arrow-down" data-bind="visible: $parent.sortOrder() === 'desc' && sortable && $parent.sortName() === index"></span>

                            <span class="icon-white icon-circle-arrow-up" data-bind="visible: $parent.sortOrder() === 'asc' && sortable && $parent.sortName() === index"></span>
                        </th>
                        <!-- /ko -->
                    </tr>
                </thead>
                <tbody data-bind="foreach: rows">
                    <tr>
                        <td>
                            <span data-bind="text: Id"></span>
                        </td>
                        <td>
                            <span data-bind="text: FirstName"></span>
                        </td>
                        <td>
                            <span data-bind="text: LastName"></span>
                        </td>
                        <td>
                            <span data-bind="text: Phone"></span>
                        </td>
                        <td>
                            <a href="#" data-bind="click: $root.editContact">Edit</a>

                            <a href="#" data-bind="click: $root.removeContact">Delete</a>

                        </td>
                    </tr>
                </tbody>
                <tfoot class="btn-primary">
                    <tr>
                        <td colspan="5">
                            <div class="row">
                                <div class="span9"></div>
                                <div class="span9" align="center">
                                    <span class="icon-white icon-fast-backward" data-bind="click: firstPage, style: { cursor: pageIndex() > 1 ? 'pointer' : '' }"></span>
                                    <span class="icon-white icon-backward" data-bind="click: previousPage, style: { cursor: pageIndex() > 1 ? 'pointer' : '' }"></span>
                                    <input type="text" style="width: 30px;" class="search-query" data-bind="value: pageIndex" />
                                    <span data-bind="text: pageMessage"></span>
                                    <select style="width: 60px;" class="search-query" data-bind="options: pageSizes, value: pageSize"></select>
                                    <span class="icon-white icon-forward" data-bind="click: nextPage, style: { cursor: pageIndex() < pageCount() ? 'pointer' : '' }"></span>
                                    <span class="icon-white icon-fast-forward" data-bind="click: lastPage, style: { cursor: pageIndex() < pageCount() ? 'pointer' : '' }"></span>
                                </div>
                                <div class="span9" align="right">
                                    <span data-bind="text: recordMessage"></span>
                                </div>
                            </div>
                        </td>
                    </tr>
                </tfoot>
            </table>

Let's explain three section of HTML Table so that it will be easy to customize:


1.Header:

<thead class="btn-primary">
                    <tr>
                        <!-- ko foreach: columns -->
                        <th data-bind="click: sortable ? $parent.sort : '', style: { width: width, cursor: sortable ? 'pointer' : '' }">
                            <span data-bind="text: name"></span>

                            <span class="icon-white icon-circle-arrow-down" data-bind="visible: $parent.sortOrder() === 'desc' && sortable && $parent.sortName() === index"></span>

                            <span class="icon-white icon-circle-arrow-up" data-bind="visible: $parent.sortOrder() === 'asc' && sortable && $parent.sortName() === index"></span>
                        </th>
                        <!-- /ko -->
                    </tr>
  </thead>

In this section I am using some knockout bindings to 

  • enable/disable column sorting
  • show header text 
according to the columns specified in paged List of ViewModel.


2.Body:
<tbody data-bind="foreach: rows">
                    <tr>
                        <td>
                            <span data-bind="text: Id"></span>
                        </td>
                        <td>
                            <span data-bind="text: FirstName"></span>
                        </td>
                        <td>
                            <span data-bind="text: LastName"></span>
                        </td>
                        <td>
                            <span data-bind="text: Phone"></span>
                        </td>
                        <td>
                            <a href="#" data-bind="click: $root.editContact">Edit</a>

                            <a href="#" data-bind="click: $root.removeContact">Delete</a>

                        </td>
                    </tr>
 </tbody>

In this section, rows are populating using knockout "foreach" binding..



3.Footer:
<tfoot class="btn-primary">
                    <tr>
                        <td colspan="5">
                            <div class="row">
                                <div class="span9"></div>
                                <div class="span9" align="center">
                                    <span class="icon-white icon-fast-backward" data-bind="click: firstPage, style: { cursor: pageIndex() > 1 ? 'pointer' : '' }"></span>
                                    <span class="icon-white icon-backward" data-bind="click: previousPage, style: { cursor: pageIndex() > 1 ? 'pointer' : '' }"></span>
                                    <input type="text" style="width: 30px;" class="search-query" data-bind="value: pageIndex" />
                                    <span data-bind="text: pageMessage"></span>
                                    <select style="width: 60px;" class="search-query" data-bind="options: pageSizes, value: pageSize"></select>
                                    <span class="icon-white icon-forward" data-bind="click: nextPage, style: { cursor: pageIndex() < pageCount() ? 'pointer' : '' }"></span>
                                    <span class="icon-white icon-fast-forward" data-bind="click: lastPage, style: { cursor: pageIndex() < pageCount() ? 'pointer' : '' }"></span>
                                </div>
                                <div class="span9" align="right">
                                    <span data-bind="text: recordMessage"></span>
                                </div>
                            </div>
                        </td>
                    </tr>
 </tfoot>

In this section I am using knockout bindings to 

  • enable/disable forward and backward option
  • show page size dropdownlist
  • show page index textbox
  • show page index message(ex: Page 1 of 2)
  • show record range message(ex: Records 1 - 10 of 14)
------------------------------------------------------------------------------------------------------------

Web API Controller



public class ContactsController : ApiController
    {
        IContactComponent contactComponent = new ContactComponent();

        // GET api/
        public dynamic GetContactList(string sidx, string sord, int page, int rows)
        {
            IQueryable contactQuery = contactComponent.GetAll();

            IList contactList;

            var totalRecords = contactQuery.Count();
            var pageIndex = page - 1;

            switch (sidx.ToLower())
            {
                case "id":
                    if (sord == "asc")
                    {
                        contactList = contactQuery.OrderBy(o => o.Id).Skip(pageIndex * rows).Take(rows).ToList();
                    }
                    else
                    {
                        contactList = contactQuery.OrderByDescending(o => o.Id).Skip(pageIndex * rows).Take(rows).ToList();
                    }
                    break;
                case "firstname":
                    if (sord == "asc")
                    {
                        contactList = contactQuery.OrderBy(o => o.FirstName).Skip(pageIndex * rows).Take(rows).ToList();
                    }
                    else
                    {
                        contactList = contactQuery.OrderByDescending(o => o.FirstName).Skip(pageIndex * rows).Take(rows).ToList();
                    }
                    break;
                case "lastname":
                    if (sord == "asc")
                    {
                        contactList = contactQuery.OrderBy(o => o.LastName).Skip(pageIndex * rows).Take(rows).ToList();
                    }
                    else
                    {
                        contactList = contactQuery.OrderByDescending(o => o.LastName).Skip(pageIndex * rows).Take(rows).ToList();
                    }
                    break;
                case "phone":
                    if (sord == "asc")
                    {
                        contactList = contactQuery.OrderBy(o => o.Phone).Skip(pageIndex * rows).Take(rows).ToList();
                    }
                    else
                    {
                        contactList = contactQuery.OrderByDescending(o => o.Phone).Skip(pageIndex * rows).Take(rows).ToList();
                    }
                    break;
                default:
                    contactList = contactQuery.OrderBy(o => o.Id).Skip(pageIndex * rows).Take(rows).ToList();
                    break;
            }


            var totalPages = (int)Math.Ceiling((float)totalRecords / (float)rows);
            return new
            {
                total = totalPages,
                records = totalRecords,
                rows = contactList
            };
        }
    }

I have created API controller named ContactsController.The method "GetContactList"  is used to retrieve page wise data.It takes minimum 4 parameters(sidx, sord, page, rows).It returns data in a particular format as below:

 return new
            {
                total = totalPages,
                records = totalRecords,
                rows = contactList
            };

------------------------------------------------------------------------------------------------------------
For sample project, download from link: KnockoutWithPagination.zip