Introduction
Knockoutjs is at the center of attention for a quite long time. It is a client-side data-binding JavaScript library which help us to create clean data models and responsible for events and data-binding. knockoutjs reflect changes on views real-time and update only changed areas.
Using knockoutjs you can build sleek and responsive web applications with today's popular technologies.
For this demonstration, let's create ASP.NET MVC web application called student-register and use knockoutjs for client-side data binding.
We are using ASP.NET Web API service to talk to the server side persistent data store.
Using the code
Let's begin with ASP.NET MVC (5) application with Web API and create LocalDB database for data store.
Then Let's create Entity Data Model for our database and expose data via Web API service.
Finally, we'll consume Web service from client side and use knockoutjs to bind data to our views.
We are using integrated bootstrap styles here to achieve clean, simple UIs
Project Setup
Create ASP.NET MVC Application with Web API.
Add new ASP.NET web application under .NET framework 4.5. On second page, tick Web API to add core reference for Web API services.
Add KnockoutJS libraries to project.
Using nuget package manager, add knockoutJS libraries to the project.
Create Application Database.
Create database file under app_data folder and with visual studio provided tools, create a table called students. please refer to screenshot for data-table fields.
Add Entity Data Model
Add a new folder called 'EntityDataModel' to the solution and add ADO.NET Entity Data Model called StudentModel.edmx to that folder.
Select existing Student database and select students table. for the Entitiy Data Model. EF model should look like this after generated.
Create Web API controller
Right click on controllers folder and click add --> Controller.
Select Web API 2 Controller with read/write actions template and click add.
Name this controller as StudentController.cs
Now we have project setup for the solution. In next steps, we will do the implementation to achieve required functionality.
Implementation
Lets add new class called 'StudentRepository' under 'Models' folder which enable us to access EF data models more easily.
using KnockoutMVC.EntityDataModel;
using System.Collections.Generic;
using System.Linq;
namespace KnockoutMVC.Models
{
public class StudentRepository
{
private static StudentDatabaseEntities _studentDb;
private static StudentDatabaseEntities StudentDb
{
get { return _studentDb ?? (_studentDb = new StudentDatabaseEntities()); }
}
public static IEnumerable<Student> GetStudents()
{
var query = from students in StudentDb.Students select students;
return query.ToList();
}
public static void InsertStudent(Student student)
{
StudentDb.Students.Add(student);
StudentDb.SaveChanges();
}
public static void DeleteStudent(int studentId)
{
var deleteItem = StudentDb.Students.FirstOrDefault(c => c.Id == studentId);
if (deleteItem != null)
{
StudentDb.Students.Remove(deleteItem);
StudentDb.SaveChanges();
}
}
}
}
Next, Let's update our Web API controller to do basic read/ add/ delete operations on students table with help of StudentRepository class.
using KnockoutMVC.EntityDataModel;
using KnockoutMVC.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace KnockoutMVC.Controllers
{
public class StudentController : ApiController
{
public IEnumerable<Student> Get()
{
return StudentRepository.GetStudents();
}
public Student Get(int id)
{
return StudentRepository.GetStudents().FirstOrDefault(s=>s.Id == id);
}
public HttpResponseMessage Post(Student student)
{
StudentRepository.InsertStudent(student);
var response = Request.CreateResponse(HttpStatusCode.Created, student);
string url = Url.Link("DefaultApi", new {student.Id});
response.Headers.Location = new Uri(url);
return response;
}
public HttpResponseMessage Delete(int id)
{
StudentRepository.DeleteStudent(id);
var response = Request.CreateResponse(HttpStatusCode.OK, id);
return response;
}
}
}
Create MVC views
Lets create two partial views for display registered student list and register new student.
later we can add these two partial views to Index view which will display student list and provide functionality to register new student as well.
Register Student partial view
add new partial view called _RegisterStudent.cshtml. You may add this under 'Home' views folder since this is not shared among controllers
_RegisterStudent.cshtml
<form role="form">
<div class="form-group">
<label for="inpFirstName">First Name</label>
<input id="inpFirstName" type="text" class="form-control" data-bind="value: FirstName" />
</div>
<div class="form-group">
<label for="inpLastName">Last Name</label>
<input id="inpLastName" type="text" class="form-control" data-bind="value: LastName" />
</div>
<div class="form-group">
<label for="inpAge">Age</label>
<input id="inpAge" type="text" class="form-control" data-bind="value: Age" />
</div>
<div class="form-group">
<label for="inpGender">Gender</label>
<select id="inpGender" class="form-control" data-bind="options: genders, value: Gender"></select>
</div>
<div class="form-group">
<label for="txtDescription">Description</label>
<input id="txtDescription" class="form-control" data-bind="value: Description"/>
</div>
</form>
<input type="button" id="btnAddStudent" class="btn btn-primary" value="Add Student" data-bind="click: addStudent" />
We're using bootstrap styles here for styling form elements (role="form", class="form-group", class="form-control", class="btn btn-primary" )
Also we add knockout data binding attributes (data-bind=""). this bind knockout view model to html element property which is specified in binding expression.
For example [data-bind="value: FirstName"] says bind 'FirstName' property from view model to value of this element.
if you look at button's binding expression, it's look like [data-bind="click: addStudent"]. this states bind click event of the button to addStudent function of the view model.
We will specify view-model when creating our view-model java-script file.
Listing Partial view
Lets create our listing partial view.
Add another partial view called _StudentsList.cshtml with following markup
_StudentsList.cshtml
<table class="table table-striped table-bordered table-condensed">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Age</th>
<th>Gender</th>
<th>Description</th>
<th>Action</th>
</tr>
</thead>
<tbody data-bind="foreach: students">
<tr>
<td data-bind="text: Id"></td>
<td data-bind="text: FullName"></td>
<td data-bind="text: Age"></td>
<td data-bind="text: Gender"></td>
<td data-bind="text: Description"></td>
<td><input type="button" class="btn btn-danger btn-xs" value=" [x] delete " data-bind="click: $parent.removeStudent" /></td>
</tr>
</tbody>
</table>
<br />
<input type="button" class="btn btn-default" id="btnGetStudents" value="Refresh" data-bind="click: getStudents" />
In tbody tag we are using [data-bind="foreach: students"] binding expression. as name implies it's an iterative expression.
We have collection of student objects called students in our view model and expression will create table row for each of this student object.
also button's binding have [ data-bind="click: $parent.removeStudent"] expression. which state removeStudent function is reside at parent which outside the view-model context. you will get better idea how this scope works when comparing this with the actual view-model.
It's time change 'Index' view under 'Home' view folder and integrate our partial views.
Update Index view
add this markup to 'Index' view by replacing it's current markup.
We'll create 'KnockoutModels/StudentRegisterViewModel.js' in next step, for now just add the reference.
@{
ViewBag.Title = "Student Register";
}
<script src="~/Scripts/knockout-3.1.0.js"></script>
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/KnockoutModels/StudentRegisterViewModel.js"></script>
<div class="page-header">
<h2 class="text-center">Student Registration</h2>
</div>
<div class="row">
<div class="col-md-4">
<div class="panel panel-info">
<div class="panel-heading">
<h2 class="panel-title">Register new student</h2>
</div>
<div class="panel-body" data-bind="with: addStudentViewModel">
@Html.Partial("_RegisterStudent")
</div>
</div>
</div>
<div class="col-md-8">
<div class="panel panel-primary">
<div class="panel-heading">
<h2 class="panel-title">Registerd Students</h2>
</div>
<div class="panel-body" data-bind="with: studentListViewModel">
@Html.Partial("_StudentsList")
</div>
</div>
</div>
</div>
by stating [data-bind="with: addStudentViewModel"] expression, we are saying to use 'addStudentViewModel' sub view-model for the partial view. so Index view-model contain two sub view-models for partial views.
Lets create javascript viewmodel file for the index view.
Create ViewModel
Create folder named 'KnockoutModels' and add javascript file named 'StudentRegisterViewModel.js'
Add this code to 'StudentRegisterViewModel.js' file. please go through this code carefully. this contain all the view logic and necessary Web API calls.
var studentRegisterViewModel;
function Student(id, firstName, lastName, age, description, gender) {
var self = this;
self.Id = ko.observable(id);
self.FirstName = ko.observable(firstName);
self.LastName = ko.observable(lastName);
self.FullName = ko.computed(function() {
return self.FirstName() + " " + self.LastName();
}, self);
self.Age = ko.observable(age);
self.Description = ko.observable(description);
self.Gender = ko.observable(gender);
self.genders = [
"Male",
"Female",
"Other"
];
self.addStudent = function () {
var dataObject = ko.toJSON(this);
delete dataObject.FullName;
$.ajax({
url: '/api/student',
type: 'post',
data: dataObject,
contentType: 'application/json',
success: function (data) {
studentRegisterViewModel.studentListViewModel.students.push(new Student(data.Id, data.FirstName, data.LastName, data.Age, data.Description, data.Gender));
self.Id(null);
self.FirstName('');
self.LastName('');
self.Age('');
self.Description('');
}
});
};
}
function StudentList() {
var self = this;
self.students = ko.observableArray([]);
self.getStudents = function () {
self.students.removeAll();
$.getJSON('/api/student', function (data) {
$.each(data, function (key, value) {
self.students.push(new Student(value.Id, value.FirstName, value.LastName, value.Age, value.Description, value.Gender));
});
});
};
self.removeStudent = function (student) {
$.ajax({
url: '/api/student/' + student.Id(),
type: 'delete',
contentType: 'application/json',
success: function () {
self.students.remove(student);
}
});
};
}
studentRegisterViewModel = { addStudentViewModel: new Student(), studentListViewModel: new StudentList() };
$(document).ready(function () {
ko.applyBindings(studentRegisterViewModel);
studentRegisterViewModel.studentListViewModel.getStudents();
});
So That's it.
Run the application and see. you should have something like this.
Hope this quick guide will help you to get a jump start on knockoutJS. you can find more info and attractive tutorials on knockoutjs at http://learn.knockoutjs.com
History
Initial Post: 5th May 2014