Web API CRUD Operations with Knockout

07 sep. 2022
Advanced
36,6K Views
8 min read     Source Code

In previous article, I have explained Knockout CRUD Operations using MVC4. In this article, I will demonstrate, how to use Knockout with Web API for CRUD (Create, Read, Update, Delete) Operations.

Step 1: Define Model

Product.cs

public class Product
{
 public int Id { get; set; }
 public string Name { get; set; }
 public string Category { get; set; }
 public decimal Price { get; set; }
}

IProductRepository.cs

interface IProductRepository
{
 IEnumerable<Product> GetAll();
 Product Get(int id);
 Product Add(Product item);
 bool Update(Product item);
 bool Delete(int id);
}

ProductRepository.cs

public class ProductRepository : IProductRepository
{
 private List<Product> products = new List<Product>();
 private int _nextId = 1;

 public ProductRepository()
 {
 // Add products for the Demonstration
 Add(new Product { Name = "Computer", Category = "Electronics", Price = 23.54M });
 Add(new Product { Name = "Laptop", Category = "Electronics", Price = 33.75M });
 Add(new Product { Name = "iPhone4", Category = "Phone", Price = 16.99M });
 }

 public IEnumerable<Product> GetAll()
 {
 // TO DO : Code to get the list of all the records in database
 return products;

 }

 public Product Get(int id)
 {
 // TO DO : Code to find a record in database
 return products.Find(p => p.Id == id);

 }

 public Product Add(Product item)
 {
 if (item == null)
 {
 throw new ArgumentNullException("item");
 }
 
 // TO DO : Code to save record into database
 item.Id = _nextId++;
 products.Add(item);

 return item;
 }
 public bool Update(Product item)
 {
 if (item == null)
 {
 throw new ArgumentNullException("item");
 }
 
 // TO DO : Code to update record into database
 int index = products.FindIndex(p => p.Id == item.Id);
 if (index == -1)
 {
 return false;
 }
 products.RemoveAt(index);
 products.Insert(index,item);

 return true;
 }

 public bool Delete(int id)
 {
 // TO DO : Code to remove the records from database
 products.RemoveAll(p => p.Id == id);

 return true;
 }
}

Step 2: Define Controller

HomeController.cs

public class HomeController : Controller
{
 public ActionResult Product()
 {
 return View();
 }
}

ProductController.cs for Web API

public class ProductController : ApiController
{
 static readonly IProductRepository repository = new ProductRepository();

 public IEnumerable GetAllProducts()
 {
 return repository.GetAll();
 }

 public Product PostProduct(Product item)
 {
 return repository.Add(item);
 }

 public IEnumerable PutProduct(int id, Product product)
 {
 product.Id = id;
 if (repository.Update(product))
 {
 return repository.GetAll();
 }
 else
 {
 return null;
 }
 }

 public bool DeleteProduct(int id)
 {
 if (repository.Delete(id))
 {
 return true;
 }
 else
 {
 return false;
 }

 }
}

Step 3: Define View

Product.cshtml

@{
 ViewBag.Title = "Products' List";
}

@section scripts {
 <style type="text/css">
 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: '';
 }
 </style>
 <script src="~/Scripts/knockout-2.2.0.js"></script>
 <script src="@Url.Content("~/Scripts/knockout-2.2.0.debug.js")" type="text/javascript"></script>
 <script type="text/javascript">

 function formatCurrency(value) {
 return "$" + value.toFixed(2);
 }

 function ProductViewModel() {

 //Make the self as 'this' reference
 var self = this;
 //Declare observable which will be bind with UI 
 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(); // Contains the list of products

 // Initialize the view-model
 $.ajax({
 url: 'api/product',
 cache: false,
 type: 'GET',
 contentType: 'application/json; charset=utf-8',
 data: {},
 success: function (data) {
 self.Products(data); //Put the response in ObservableArray
 }
 });

 // Calculate Total of Price After Initialization
 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;
 });

 //Add New Item
 self.create = function () {
 if (Product.Name() != "" && Product.Price() != "" && Product.Category() != "") {
 $.ajax({
 url: 'api/product',
 cache: false,
 type: 'POST',
 contentType: 'application/json; charset=utf-8',
 data: ko.toJSON(Product),
 success: function (data) {
 // alert('added');
 self.Products.push(data);
 self.Name("");
 self.Price("");
 self.Category("");

 }
 }).fail(
 function (xhr, textStatus, err) {
 alert(err);
 });

 }
 else {
 alert('Please Enter All the Values !!');
 }

 }
 // Delete product details
 self.delete = function (Product) {
 if (confirm('Are you sure to Delete "' + Product.Name + '" product ??')) {
 var id = Product.Id;
 $.ajax({
 url: 'api/product/' + id,
 cache: false,
 type: 'DELETE',
 contentType: 'application/json; charset=utf-8',
 data: {},
 success: function (data) {
 self.Products.remove(Product);

 }
 }).fail(
 function (xhr, textStatus, err) {
 alert(err);
 });
 }
 }

 // Edit product details
 self.edit = function (Product) {
 self.Product(Product);

 }

 // Update product details
 self.update = function () {
 var Product = self.Product();
 var id = Product.Id;

 $.ajax({
 url: 'api/product/' + id,
 cache: false,
 type: 'PUT',
 contentType: 'application/json; charset=utf-8',
 data: ko.toJSON(Product),
 success: function (data) {
 self.Products.removeAll();
 self.Products(data); //Put the response in ObservableArray
 self.Product(null);
 alert("Record Updated Successfully");
 
 }
 })
 .fail(
 function (xhr, textStatus, err) {
 alert(err);
 });
 }
 
 // Reset product details
 self.reset = function () {
 self.Name("");
 self.Price("");
 self.Category("");
 }

 // Cancel product details
 self.cancel = function () {
 self.Product(null);

 }
 }
 var viewModel = new ProductViewModel();
 ko.applyBindings(viewModel);

 </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>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="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></td>
 <td></td>
 <td>Total :</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>

Step 4: Run Application - Retrieve Operation

Step 4: Insert Operation

Step 4: Update Operation

Step 4: Delete Operation

What do you think?

I hope you will enjoy the tips while working with Knockout and Web API. I would like to have feedback from my blog readers. Your valuable feedback, question, or comments about this article are always welcome.

Share Article
About Author
Shailendra Chauhan (Microsoft MVP, Founder & CEO at Scholarhat by DotNetTricks)

Shailendra Chauhan is the Founder and CEO at ScholarHat by DotNetTricks which is a brand when it comes to e-Learning. He provides training and consultation over an array of technologies like Cloud, .NET, Angular, React, Node, Microservices, Containers and Mobile Apps development. He has been awarded Microsoft MVP 8th time in a row (2016-2023). He has changed many lives with his writings and unique training programs. He has a number of most sought-after books to his name which has helped job aspirants in cracking tough interviews with ease.
Learn to Crack Your Technical Interview

Accept cookies & close this