Click here to Skip to main content
12,396,230 members (64,226 online)
Click here to Skip to main content
Add your own
alternative version

Stats

165.6K views
7.8K downloads
137 bookmarked
Posted

Knockout js for Beginners

, 12 Nov 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
This article will help beginners to understand how knockout will work with ASP.NET and communicate client-side server side and the usefulness of the responsive UI design.

Introduction

Knockout js (shortly called KO) is a very popular JavaScript library and increasing its popularity day by day. This library helps to create rich/responsive/interactive web applications. It works directly with the web application's underlying data model. Using KO with any web application is very simple, clean, and straightforward, it is very powerful in the context of dynamic UI creation.

Background

JavaScript plays a very important role for developing today's modern web applications. Once upon a time, we just wrote a few JavaScript lines for data validation from the client side. But day by day, JavaScript plays a vital role not only in data validation but also for creating responsive, robust web UI. For that reason, day by day, a new JavaScript framework/library comes into the market. We as smart developers, must adopt them and try to use them with our applications.

Benefits

If we use KO with web applications, we can get the following benefits:

  • Anytime we can connect UI elements with data model.
  • Easily create complex dynamic data model.
  • Automatically update UI when Data Model is changed, when UI is changed, then Data Model is changed automatically.
  • Support event-driven programming model.
  • Extend custom behavior very easily.
  • All main-stream browsers are supported (Internet Explorer, FireFox, Crome, Safari)

KO with MVVM

What is MVVM? Well, the full form of MVVM is Model View ViewModel. It is an architectural design pattern originated by Microsoft and mainly created for WPF/Silverlight applications. But we can use this Web application too with ASP.NET.

MVVM is a specific implementation targeted at UI development platform which supports event driven programming for WPF/Silverlight. It respects the programming principle "Separation of Concern". It completely separates GUI Rendering logic from Application Logic (Data Logic/Business Logic).

KO built on that architectural design pattern. So if you want to understand KO properly, then you should know about MVVM first.

MVVM Overview

The following diagram will show the style of MVVM:

MVVM has 3 parts:

  1. Model
  2. View
  3. ViewModel

Model: Responsible for holding Application data. When user enter data with UI elements, that data will store to a Model. So we can think like it is the data part of the pattern.

View: Responsible for presenting model data to the user. User elements are the part of the View. It provides structure/Layout of the User Interface and presents to the user.

ViewModel: Connector of Model and View. Main responsibility is to establish communication with View and Model. It holds data and function. Functions manipulate that data and it reflect to the UI. When data is changed, UI is changed, when UI is changed data is changed. ViewModel will do it for us with the help of databinding concept.

MVVM Benefits

Main benefits of MVVM are listed below:

  • Provide more flexibility of designer and developer works
  • Thorough unit testing
  • Provide flexibility to change user interface without having to re-factor other logic of the codebase
  • Provide more re-usability of UI component

Include KO Library to Web Application

CDN (Content Delivery Network) reference of Knockout js is available at Microsoft CDN repository. There are two versions available. One for compact (minified) another for debug. It is recommended to use the minified version. The URLs are as given below:

If your application is ASP.NET WebForm, then you can download js file from the above locations and store it to your web folder/subfolder and reference it to your page/master page.

<script src="~/Scripts/knockout-2.2.1.js" type="text/javascript"></script>
CDN Reference:
<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js" type="text/javascript"></script>  

If ASP.NET MVC application, you can take reference inside @section.

@section scripts {
    <script src="~/Scripts/knockout-2.2.1.js"></script>

Example 1

I will create a simple Employee information entry form. Form will accept some basic employee related data from the user and submit that data to the MVC Action with the help of AJAX. In View part, I will use various HTML elements so that I can show how all elements are worked with KO.

Implementation

Before I start showing code with KO, I will just mention one thing. In MVVM pattern, a personal choice is there. That is which come first, Model Or View. I am not going to discuss details on that. Just mention my personal preference and that is View. I always like to start View first, then for Model.

<form id="employeeForm" name="employeeForm" method="POST">
    <div id="form-root">
        <div>
            <label class="form-label">First Name:</label>
            <input type="text" id="txtFirstName" 
              name="txtFirstName" data-bind="value:Employee.FirstName" />
        </div>
         <div>
            <label class="form-label">Last Name:</label>
            <input type="text" id="txtLastName" 
              name="txtLastName" data-bind="value:Employee.LastName"  />
        </div>
        <div>
            <label class="form-label">Full Name:</label>
            <input type="text" id="txtFullName" name="txtFullName" 
              data-bind="value:Employee.FullName" readonly="readonly"  />
        </div>
        <div>
            <label class="form-label">Date Of Birth:</label>
            <input type="text" id="txtDateOfBirth" 
              name="dateOfBirth" data-bind="value:Employee.DateOfBirth"  />
        </div>
        <div>
            <label>Education:</label>
            <input type="checkbox" value="graduation" id="chkGraduation" 
              name="chkGraduation" data-bind="checked:Employee.EducationList" />Graduation
            <input type="checkbox" value="postGraduation" 
              id="chkPostGraduation" name="chkPostGraduation" 
              data-bind="checked:Employee.EducationList" />PostGraduation
        </div>
        <div>
            <label>Gender:</label>
            <input type="radio" id="rdoMale" name="gender" 
              value="0"  data-bind="checked:Employee.Gender" />Male
            <input type="radio" id="rdoFeMale" name="gender" 
              value="1" data-bind="checked:Employee.Gender"  />FeMale
        </div>
        <div>
            <label class="form-label">Department:</label>
            <select id="ddlDepartment" name="ddlDepartment" 
              data-bind="options:$root.Employee.DepartmentList, optionsValue:'Id', 
                optionsText:'Name', value:Employee.DepartmentId">
            </select>
        </div>
        <div>
            <input type="button" id="btnSubmit" 
              value="Submit" data-bind = "click: submit" />
             <input type="button" id="btnReset" 
               value="Reset" data-bind = "click: reset" />
        </div>
    </div>
</form>

View

First, I create a view. It looks like:

I bind all of view's html form element with my client side model with KO's data-bind attribute. Why I said Client Side Model? Because I have a Server Side Model too. Later, I talk about server side model. With Data-Bind attribute, KO connects UI element to the Model property. Now, explain UI elements connect to the Data Member of the Model one by one.

TextBox Binding

<input type="text" id="txtFirstName" 
name="txtFirstName" data-bind="value:Employee.FirstName" /> 

Html form element textbox binds with FirstName property of Employee Model. value is the KO key by which KO address textbox value property. But remember that value inside data-bind attribute is the KO's key not html native. When textbox value is updated, then FirstName property of the Employee model will be updated automatically and vice-versa. No custom code is needed for that.

CheckBox Binding

<input type="checkbox" value="graduation" 
  id="chkGraduation" name="chkGraduation" data-bind="checked:Employee.EducationList" />
<input type="checkbox" value="postGraduation" id="chkPostGraduation" 
  name="chkPostGraduation" data-bind="checked:Employee.EducationList" /> 

HTML form element checkbox (input checkbox) binds with EducationList string array of Employee Model. Here, both checkboxes bind with single model property. When user checked any/both checkbox, automatically the value of the checkboxes (1st checkbox value is "graduation" 2nd is "PostGraduation") will be added to the array. If un-checked, then the value will be removed from this array. In textbox, we use KO's key value, in checkbox, here we use another KO's key named checked. Again, checked in not any HTML native.

Radio-Button Binding

<input type="radio" id="rdoMale" name="gender" 
   value="0"  data-bind="checked:Employee.Gender" />
<input type="radio" id="rdoFeMale" name="gender" 
  value="1" data-bind="checked:Employee.Gender"  /> 

There are two radio buttons and grouped it with name property. If radio-buttons name is the same, then they work like group. If one radio is selected, then automatically others will be de-selected and vice-versa. Both radio buttons bind with single Employee Model property named Gender (number type). When Male will select, then this property will contain 0, if FeMale select, it will contain 1. Same way when Gender contains 0, then rdoMale radio will be selected, when Gender contains 1, then rdoFemale will be selected (checked).

Drop-Down/ComboBox/Select Binding

<select id="ddlDepartment" name="ddlDepartment" 
    data-bind="options:$root.Employee.DepartmentList, optionsValue:'Id', 
      optionsText:'Name', value:Employee.DepartmentId">
</select>  

Drop-down list binds with two Model properties. For its data source, it binds DepartmentList array of object of Employee Model. Object has 2 properties. Id and Name. Id Binds with value and Name binds with option text. Means, drop-down will show all Names of the objects. Drop-down selected value binds with DepartmentId property. When user selects any item from drop-down, the value of the item will store in DepartmentId. In the same way, if any value is assigned to the DepartmentId from the ViewModel, then drop-down will show that item which matches the value to the user.

Button/Command Button Binding

<input type="button" id="btnSubmit" 
value="Submit" data-bind = "click: submit" />  

Button binds with submit method of the ViewModel with click event. When user clicks Submit button, then submit method of the ViewModel will fire.

There are so many keywords that are used in data-binding in KO. All are KO's own keyword. It should be clear that those are not HTML native property.

Model

function Employee() {
    var that = this;
    that.FirstName = ko.observable("");
    that.LastName = ko.observable("");
    that.FullName = ko.computed(function () {
        return that.FirstName() + " " + that.LastName();
    });
    that.DateOfBirth = ko.observable("");
    that.EducationList = ko.observableArray();
    that.Gender = ko.observable("0");
    that.DepartmentList = ko.observableArray([{ Id: '0', Name: "CSE" }, { Id: '1', Name: "MBA" }]);
    that.DepartmentId = ko.observable("1");
} 

Employee is my Model. We can call it data model. In its every property, I initialize with ko.observable method. What does the actual ko.observable method return? It returns a function, which notify UI element for update. So in property of Employee like FirstName, LastName, etc., all are functions. If I want to assign any value to the FirstName, then code should be that.FirstName("Mr."), if read that property, then it should be that.FirstName() computed is another ko library function which is responsible for dependency tracking. Means, inside that function, I use FirstName and LastName property. When FirstName or LastName property is updated, then fullName is automatically updated.

KO has another important thing that is Observable Array. It is nothing but a collection of observable method. Each and every item of array will be observable. If I want to add value to that array, then I need to use:

that.DepartmentList.push({Id:3, Name:"BBA"}); 

If I want to remove last item of the array, then:

that.DepartmentList.pop(); 

It might be confused that push and pop are the JavaScript array native method. But here, use push and pop with observable array are not the native JavaScript method. It is actually KO's own implemented method. KO implements 6 methods for observable array.

  1. push
  2. pup
  3. unshift
  4. shift
  5. reverse
  6. sort

So be clear on that and do not confuse JavaScript array object's method with that.

ViewModel

function EmployeeVM() {
    var that = this;
    that.Employee = new Employee();
    that.reset = function () {
        that.Employee.FirstName("");
        that.Employee.LastName("");
        that.Employee.DateOfBirth("");
    };
    that.submit = function () {
        var json1 = ko.toJSON(that.Employee);
        $.ajax({
            url: '/Employee/SaveEmployee',
            type: 'POST',
            dataType: 'json',
            data: json1,
            contentType: 'application/json; charset=utf-8',
            success: function (data) {
                var message = data.Message;
            }
        });
    };
}; 

that.Employee holds the reference of Employee Model. Here, I implement 2 behaviors:

  1. reset: reset method will re-initialize the value of the model.
  2. submit: convert Employee model data to json string and send it to the server's SaveEmployee action method with the help of JQuery Ajax method.

Controller Action

[HttpPost]
public  ActionResult SaveEmployee(Employee emp)
{
    //EmployeeModel save code here
    return View("EmployeeInfo");
} 

This is the Employee controller action method. submit method of client side view model sends data to this action method. Actually, this action receives data from client view and process that data, then save that data to the database or throw exception if any invalid things happen.

Server Side Model

public class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }
    public Gender Gender { get; set; }
    public IList<string> EducationList { get; set; }
    public int DepartmentId { get; set; }
}
public enum Gender
{
    Male =0,
    FeMale = 1
} 

This is my server side Employee Model. Why is server side Model required? We see that from my client part we create an Employee ViewModel. But it should be kept in mind that in client side, we actually do not write any business logic. My application processes the business logic. Then where do I write that? For server side business logic handle, we create server side Model and we send that model to various layers like domain service layer/Repository layer, etc.

Example 2

I will create an Employee phone entry form. Employee could have multiple phone numbers with country code. First time view will show to add a single phone number entry option. If user wants, than if he presses Add button, then it will create another phone number entry option dynamically to the user interface. User can also remove any phone number entry option with press Remove button. So we understand that there dynamic UI rendering feature will come. Let's see how knockout will help in this area.

Implementation

View

First, I will complete view creation part. View looks like follows:

<form id="employeeForm" name="employeeForm" method="POST">
    <script id="PhoneTemplate" type="text/html">
        <div>
            <span>
                <label>Country Code:</label>
                <input type="text" id="txtCountryCode" data-bind="value:CountryCode" />
            </span>
            <span>
                <label>Phone Number:</label>
                <input type="text" id="txtPhoneNumber" data-bind="value:PhoneNumber" />
            </span>
            <input type="button" id="btnRemove" 
              value="Remove" data-bind="click: $parent.remove" />
        </div>
    </script>
    <div>
        <h2>Employee Phone Number</h2>
        <div data-bind="template:{name:'PhoneTemplate', foreach:PhoneList}, visible:isVisible">
        </div>
        <div>
            <input type="button" id="btnAdd" 
              value="Add Another" data-bind="click: add" />
        </div>
    </div>
</form> 

KO supports data template. It develops template engine for rendering template. This works with jQuery template too. I create a template named PhoneTemplate.

<script id="PhoneTemplate" type="text/html"> </script> 

I declare type="text/html" with script tag. That means I notify browser that it is text data and you will not execute it. Inside that script block, I create a div. Inside that div, I create 2 html textboxes, 2 labels and 1 button.

Outside the script block, I create another div and bind that div to my JavaScript viewmodel's array member PhoneList. KO has its own syntax to bind with template. Here, inside the data-bind attribute, I declare:

<div data-bind="template:{name:'PhoneTemplate', foreach:PhoneList}">
</div>  

It binds with a template named 'Phonetemplate' and uses foreach statement. That means render this referenced template equal to number of items found in PhoneList. If I dynamically add item to the PhoneList, then template will render again with new UI and same way if I remove item from this array automatically, UI element which renders when item is added will be removed.

Model

function Phone(code, number) {
    var self = this;
    self.CountryCode = ko.observable(code);
    self.PhoneNumber = ko.observable(number);
}  

Client side JavaScript Model has 2 properties:

  1. CountryCode
  2. PhoneNumber

Each property initializes with observable method so that it can work with KO. These two properties are used inside template.

ViewModel

function PhoneViewModel() {
    var self = this;
    self.PhoneList = ko.observableArray([new Phone("00", "00-00-00")]);
    self.remove = function () {
        self.PhoneList.remove(this);
    };
    self.add = function () {
        self.PhoneList.push(new Phone("01", "00-00-01"));
    };
}  

PhoneViewModel establishes connection between Phone View and Phone Model with the help of data-bind attribute. This ViewModel contains 2 methods:

  1. add
  2. remove

When user presses 'Add Another" button, it will create a new Phone Model with default value and add it to the PhoneList array. When PhoneList array is added new item, automatically UI creates a new div based on designed template.

When user presses "Remove" button, it will call remove method of ViewModel and remove current item from the PhoneList array. When an item is removed from array, then automatically UI is re-populated.

View & ViewModel Relation

When you start implementing MVVM to your web application, then one issue you might face is how many ViewModels are needed for a view for vice-versa. Means what type of relation is needed for view and model/ViewModel.

There are few opinions that exist in the market:

  • One-View to One-ViewModel
  • Many-Views to many-ViewModels
  • One-View to many-ViewModels

There is no hard and fixed rules here. Many people including me support Per View will have single ViewModel and per ViewModel will have single Model. It will helps us to keep code simpler and more manageable.

If there are re-usability concerns, then I think you can use many views with a single ViewModel.

Points of Interest

Knockout js such a library that we are using in our web application. So we need to enrich our knowledge about this library so that we can use it smoothly in any of our web projects.

I also like angular js very much. Many SPA (Single Page Application) use this library too. I will write my next article on that library.

Source Code

I upload a sample project with this article. That is an MVC project with ASP.NET 4.5 version with Visual Studio 2012. If you are a WebForm developer, then just download the code and add it to your WebForm project and customize it a little for the server side code. Replace a web method instead of ASP.NET Action method and it will work perfectly. Knock-out is not tightly coupled with any project template like MVC or WebForm or any other. In all places, you can work with it.

References

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

S. M. Ahasan Habib
Program Manager IXORA Solution Ltd.
Bangladesh Bangladesh
Mostly I work with MS technologies (ASP.NET MVC, WPF, C#, SQL Server, SSRS, SharePoint, Entity Framework, MSTest, Enterprise Library, MEF, WCF, WebAPI, MS Excel, IIS).
Non MS technologies which I love and use (Resharper, NHiberNet, JQuery, AngularJS, KnockoutJS, NodeJS, Python, MSpec, RihnoMock, Crystal Report, Subversion, Crome)

You may also be interested in...

Comments and Discussions

 
BugErr and Qusetions Pin
Stack Over10-Jul-16 5:42
memberStack Over10-Jul-16 5:42 
QuestionNice Article Pin
Santhakumar Munuswamy @ Chennai30-May-15 19:53
professionalSanthakumar Munuswamy @ Chennai30-May-15 19:53 
QuestionAwesome article Pin
Suparn B12-Jan-15 0:45
memberSuparn B12-Jan-15 0:45 
GeneralMy vote of 2 Pin
tenonic31-Oct-14 4:22
membertenonic31-Oct-14 4:22 
QuestionBinding Model Value to View Pin
a b28-Jul-14 23:25
membera b28-Jul-14 23:25 
QuestionClient side error Pin
sbrahul27-Jul-14 20:53
membersbrahul27-Jul-14 20:53 
AnswerRe: Client side error Pin
sbrahul27-Jul-14 21:00
membersbrahul27-Jul-14 21:00 
GeneralMy vote of 5 Pin
kentclark51916-Jul-14 8:25
memberkentclark51916-Jul-14 8:25 
GeneralMy vote of 5 Pin
mehedi195014-Jul-14 14:00
membermehedi195014-Jul-14 14:00 
GeneralMy vote of 4 Pin
Debabrata_Das13-Jul-14 7:43
professionalDebabrata_Das13-Jul-14 7:43 
GeneralMy vote of 2 Pin
KanupriyaGoel18-Jun-14 21:57
memberKanupriyaGoel18-Jun-14 21:57 
Questionnice exmplanation Pin
Borshon Aeolus Saydur5-Jun-14 0:17
memberBorshon Aeolus Saydur5-Jun-14 0:17 
AnswerRe: nice exmplanation Pin
S. M. Ahasan Habib5-Jun-14 1:08
memberS. M. Ahasan Habib5-Jun-14 1:08 
GeneralMy vote of 3 Pin
Member 1002269623-May-14 4:46
memberMember 1002269623-May-14 4:46 
QuestionGood Explanation Pin
ConnectingKamlesh6-May-14 23:40
professionalConnectingKamlesh6-May-14 23:40 
AnswerRe: Good Explanation Pin
S. M. Ahasan Habib7-May-14 1:05
memberS. M. Ahasan Habib7-May-14 1:05 
QuestionIt saved my day!! Pin
decentajay13-Apr-14 13:38
memberdecentajay13-Apr-14 13:38 
AnswerRe: It saved my day!! Pin
S. M. Ahasan Habib13-Apr-14 19:18
memberS. M. Ahasan Habib13-Apr-14 19:18 
QuestionVery Useful Pin
Vipin_Arora21-Mar-14 23:45
memberVipin_Arora21-Mar-14 23:45 
AnswerRe: Very Useful Pin
S. M. Ahasan Habib22-Mar-14 20:20
memberS. M. Ahasan Habib22-Mar-14 20:20 
GeneralRe: Very Useful Pin
Vipin_Arora23-Mar-14 5:43
memberVipin_Arora23-Mar-14 5:43 
GeneralRe: Very Useful Pin
S. M. Ahasan Habib23-Mar-14 8:15
memberS. M. Ahasan Habib23-Mar-14 8:15 
QuestionRe: Very Useful Pin
Vipin_Arora23-Mar-14 17:14
memberVipin_Arora23-Mar-14 17:14 
AnswerRe: Very Useful Pin
S. M. Ahasan Habib23-Mar-14 17:28
memberS. M. Ahasan Habib23-Mar-14 17:28 
Questionapplybinding method Pin
Member 103179135-Jan-14 18:11
memberMember 103179135-Jan-14 18:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.160721.1 | Last Updated 13 Nov 2013
Article Copyright 2013 by S. M. Ahasan Habib
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid