Introduction
As you know, Knockout is a JavaScript library that allows you to bind html elements to any data model. You can also use Knockout for Insert
, Update
, Delete
and Retrieve
operations. This tip will demonstrate how to use Knockout with MVC4 for CRUD (Create
, Read
, Update
, Delete
) operations. Many experienced developers will find this tip very basic, but since the tip is written from the perspective of beginners, I've tried to keep things simple.
Background
If you don't have basic knowledge of Knockout JS, kindly refer to this excellent article.
Using the Code
Let's begin !!!
Create 'TblProductList
' table with this schema.
Create a new project in ASP.NET MVC 4 and name it as you prefer and select empty project template.
Install Entity Framework 6, Jquery and Knockout in your project using NuGet Package Manager.
You can also download jquery.js and Knockout.js from their official website and paste it in 'Scripts' folder of your project.
Right Click on Model folder and add a new ADO.NET Entity Data Model. Name it as 'ProductDataContext.edmx'.
Choose 'Generate from Database' and configure the connection string as per your SQL server.
After generating the model, you will get the entity of TblProductList
.
Create new folder 'Interface' in root directory. Add a new class 'IProductRepository.cs'.
interface IProductRepository
{
IEnumerable<TblProductList> GetAll();
TblProductList Get(int id);
TblProductList Add(TblProductList item);
bool Update(TblProductList item);
bool Delete(int id);
}
Create new folder 'Repositories' in root directory. Add a new class 'ProductRepository.cs'.
Implement the methods to Create
, Read
, Update
, Delete
using Entity Framework.
public class ProductRepository : IProductRepository
{
ProductDBEntities ProductDB = new ProductDBEntities();
public IEnumerable<TblProductList> GetAll()
{
return ProductDB.TblProductLists;
}
public TblProductList Get(int id)
{
return ProductDB.TblProductLists.Find(id);
}
public TblProductList Add(TblProductList item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
ProductDB.TblProductLists.Add(item);
ProductDB.SaveChanges();
return item;
}
public bool Update(TblProductList item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
var products = ProductDB.TblProductLists.Single(a => a.Id == item.Id);
products.Name = item.Name;
products.Category = item.Category;
products.Price = item.Price;
ProductDB.SaveChanges();
return true;
}
public bool Delete(int id)
{
TblProductList products = ProductDB.TblProductLists.Find(id);
ProductDB.TblProductLists.Remove(products);
ProductDB.SaveChanges();
return true;
}
}
Right click on Controllers folder and add new Controller 'ProductController.cs':
public class ProductController : Controller
{
static readonly IProductRepository repository = new ProductRepository();
public ActionResult Products()
{
return View();
}
public JsonResult GetAllProducts()
{
return Json(repository.GetAll(), JsonRequestBehavior.AllowGet);
}
public JsonResult AddProduct(TblProductList item)
{
item = repository.Add(item);
return Json(item, JsonRequestBehavior.AllowGet);
}
public JsonResult EditProduct(int id, TblProductList product)
{
product.Id = id;
if (repository.Update(product))
{
return Json(repository.GetAll(), JsonRequestBehavior.AllowGet);
}
return Json(null);
}
public JsonResult DeleteProduct(int id)
{
if (repository.Delete(id))
{
return Json(new { Status = true }, JsonRequestBehavior.AllowGet);
}
return Json(new { Status = false }, JsonRequestBehavior.AllowGet);
}
}
Right click on ActionResult Products()
and add a view 'Products.cshtml'.
@{
ViewBag.Title = "Products List";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@section scripts {
<link href="~/Content/CustomStyle.css" rel="stylesheet" />
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script src="~/Scripts/knockout-2.2.0.js"></script>
<script src="~/Scripts/knockout-2.2.0.debug.js"></script>
<script src="~/Scripts/KnockoutDemo.js"></script>
}
<div id="body">
<h2>Knockout CRUD Operations with MVC4</h2>
<h3>List of Products</h3>
<table id="products1" data-bind="visible: Products().length > 0">
<thead>
<tr>
<th style="display: none;">ID</th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Actions</th>
</tr>
</thead>
<tbody data-bind="foreach: Products">
<tr>
<td data-bind="visible:false , text: Id "></td>
<td data-bind="text: Name"></td>
<td data-bind="text: Category"></td>
<td data-bind="text: formatCurrency(Price)"></td>
<td>
<button data-bind="click: $root.edit">Edit</button>
<button data-bind="click: $root.delete">Delete</button>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="6">
<hr />
</td>
</tr>
<tr>
<td>Total :</td>
<td></td>
<td data-bind="text: formatCurrency($root.Total())"></td>
<td></td>
</tr>
</tfoot>
</table>
<br />
<div style="border-top: solid 2px #282828; width: 430px; height: 10px"> </div>
<div data-bind="if: Product">
<div>
<h2>Update Product</h2>
</div>
<div>
<label for="productId" data-bind="visible: false">ID</label>
<label data-bind="text: Product().Id, visible: false"></label>
</div>
<div>
<label for="name">Name</label>
<input data-bind="value: Product().Name" type="text" title="Name" />
</div>
<div>
<label for="category">Category</label>
<input data-bind="value: Product().Category" type="text" title="Category" />
</div>
<div>
<label for="price">Price</label>
<input data-bind="value: Product().Price" type="text" title="Price" />
</div>
<br />
<div>
<button data-bind="click: $root.update">Update</button>
<button data-bind="click: $root.cancel">Cancel</button>
</div>
</div>
<div data-bind="ifnot: Product()">
<div>
<h2>Add New Product</h2>
</div>
<div>
<label for="name">Name</label>
<input data-bind="value: $root.Name" type="text" title="Name" />
</div>
<div>
<label for="category">Category</label>
<input data-bind="value: $root.Category" type="text" title="Category" />
</div>
<div>
<label for="price">Price</label>
<input data-bind="value: $root.Price" type="text" title="Price" />
</div>
<br />
<div>
<button data-bind="click: $root.create">Save</button>
<button data-bind="click: $root.reset">Reset</button>
</div>
</div>
</div>
Create a new JavaScript file 'KnockoutDemo.js' in Scripts folder to implement CRUD operations using Knockout code.
function formatCurrency(value) {
return "₹ " + value.toFixed(2);
}
function ProductViewModel() {
var self = this;
self.Id = ko.observable("");
self.Name = ko.observable("");
self.Price = ko.observable("");
self.Category = ko.observable("");
var Product = {
Id: self.Id,
Name: self.Name,
Price: self.Price,
Category: self.Category
};
self.Product = ko.observable();
self.Products = ko.observableArray();
$.ajax({
url: 'Product/GetAllProducts',
cache: false,
type: 'GET',
contentType: 'application/json; charset=utf-8',
data: {},
success: function (data) {
self.Products(data);
}
});
self.Total = ko.computed(function () {
var sum = 0;
var arr = self.Products();
for (var i = 0; i < arr.length; i++) {
sum += arr[i].Price;
}
return sum;
});
self.create = function () {
if (Product.Name() != "" &&
Product.Price() != "" && Product.Category() != "") {
$.ajax({
url: 'Product/AddProduct',
cache: false,
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: ko.toJSON(Product),
success: function (data) {
self.Products.push(data);
self.Name("");
self.Price("");
self.Category("");
}
}).fail(
function (xhr, textStatus, err) {
alert(err);
});
}
else {
alert('Please Enter All the Values !!');
}
}
self.delete = function (Product) {
if (confirm('Are you sure to Delete "' + Product.Name + '" product ??')) {
var id = Product.Id;
$.ajax({
url: 'Product/DeleteProduct/' + id,
cache: false,
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: id,
success: function (data) {
self.Products.remove(Product);
}
}).fail(
function (xhr, textStatus, err) {
self.status(err);
});
}
}
self.edit = function (Product) {
self.Product(Product);
}
self.update = function () {
var Product = self.Product();
$.ajax({
url: 'Product/EditProduct',
cache: false,
type: 'PUT',
contentType: 'application/json; charset=utf-8',
data: ko.toJSON(Product),
success: function (data) {
self.Products.removeAll();
self.Products(data);
self.Product(null);
alert("Record Updated Successfully");
}
})
.fail(
function (xhr, textStatus, err) {
alert(err);
});
}
self.reset = function () {
self.Name("");
self.Price("");
self.Category("");
}
self.cancel = function () {
self.Product(null);
}
}
var viewModel = new ProductViewModel();
ko.applyBindings(viewModel);
Add a Layout view '_Layout.cshtml'.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<meta name="viewport" content="width=device-width" />
</head>
<body>
<div id="body">
@RenderSection("featured", required: false)
<section class="content-wrapper main-content clear-fix">
@RenderBody()
</section>
</div>
@RenderSection("scripts", required: false)
</body>
</html>
Add stylesheet 'CustomStyle.css' to improve the look and feel of the page.
body {
margin: 20px;
font-family: "Arial", "Helventica", sans-serif;
}
label {
width: 80px;
display: inline-block;
}
button {
display: inline-block;
outline: none;
cursor: pointer;
text-align: center;
text-decoration: none;
padding: .4em 1.1em .4em;
color: #fef4e9;
border: solid 1px #006fb9;
background: #1276bb;
}
button:hover {
text-decoration: none;
background: #282828;
border: solid 1px #000;
}
table {
padding-top: 1em;
}
thead, tfoot {
font-weight: 600;
}
th, td {
padding: .1em .5em;
text-align: left;
}
td li, td ul {
margin: 0;
padding: 0;
}
td li {
display: inline;
}
td li::after {
content: ',';
}
td li:last-child::after {
content: '';
}
Now, change the default controller and action in Route.Config.cs.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Product", action = "Products", id = UrlParameter.Optional }
);
Hit Ctrl+F5.
That's it !!!
Congratulations !!! Now you have successfully implemented CRUD operations in ASP.NET MVC 4 using Knockout.js.
Please comment for any queries.
Source Code
I have uploaded a sample project with SQL scripts, in case you need them. Don't forget to change the server name in Web.Config.
HAPPY CODING! :)
Reference