Implementing CRUD operations (Create, Read, Update, Delete) using Knockout.js involves integrating observable data bindings.
Example Implementation
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Knockout.js CRUD Demo</title>
<!-- Link to Bootstrap CSS for styling -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<!-- Link to jQuery library -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<div class="container">
<div class="d-flex justify-content-between mt-3">
<h1>Product Management</h1>
<div>
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary" data-bind="click: showProductForm">
New Product
</button>
</div>
</div>
<hr />
<!-- Product Display area -->
<section class="">
<!-- Show message to suer -->
<div class="text-center" data-bind="if: products().length === 0">
<p class=" fs-5 fw-bold ">
Your product display here.
</p>
</div>
</section>
<!-- Table to display products -->
<div class="table-responsive" data-bind="if: products().length > 0">
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th>Product Name</th>
<th>Product Price</th>
<th>Actions</th>
</tr>
</thead>
<tbody data-bind="foreach: products">
<tr>
<th scope="col" data-bind="text: ($index() + 1)"></th>
<td><span data-bind="text: name"></span></td>
<td><span data-bind="text: price"></span></td>
<td class="d-flex">
<button class="btn btn-link text-primary"
data-bind="click: $parent.editProduct">Edit</button>
<button class="btn btn-link text-danger"
data-bind="click: $parent.deleteProduct">Delete</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel">New Product</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<!-- Form to add new products -->
<form class="row g-3" data-bind="submit: (isEditing() ? saveProduct : addProduct)">
<div class="col-12">
<label for="productName" class="fw-bold form-label">Product Name:</label>
<input type="text" class="form-control" placeholder="Product Name" id="productName"
data-bind="value: newProductName, valueUpdate: 'input'">
</div>
<div class="col-12">
<label for="productPrice" class="form-label fw-bold">Product Price:</label>
<input type="number" class="form-control" placeholder="Price" id="productPrice"
data-bind="value: newProductPrice, valueUpdate: 'input'">
</div>
<div class="col-12">
<button id="save-it" type="submit" class="btn btn-outline-primary px-4 rounded-pill">Add
Product</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Link to Knockout.js library for MVVM pattern support -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.1/knockout-latest.min.js"
integrity="sha512-vs7+jbztHoMto5Yd/yinM4/y2DOkPLt0fATcN+j+G4ANY2z4faIzZIOMkpBmWdcxt+596FemCh9M18NUJTZwvw=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout-validation/2.0.4/knockout.validation.min.js"
integrity="sha512-b99MDNv5TqiZtPKH2UeHzDAVydmgrOJEPtaPPEF8AgV86eYyqINFI/K7/7f0+R4WNTAVv8KvvpjwfOYHv5rd5g=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
crossorigin="anonymous"></script>
</html>
JavaScript
Write ViewModel code with form validation code,
<script type="text/javascript">
var modal = $('#exampleModal');
function UpdateModal(isEditing) {
modal.find('#exampleModalLabel').html(isEditing ? "Update Product" : "New Product");
modal.find('#save-it').html(isEditing ? "Save" : "Save");
modal.modal('show');
}
// Initialize Knockout validation
ko.validation.init({
registerExtenders: true, // Register custom validation rules
messagesOnModified: true, // Show validation messages as soon as a field is modified
insertMessages: true, // Insert validation messages next to the input elements
parseInputAttributes: true, // Parse HTML5 input attributes for validation rules
errorClass: 'text-danger fw-semibold', // CSS class for validation error messages
messageTemplate: null // Use default message template
}, true);
function Product(id, name, price) {
var self = this;
self.id = ko.observable(id);
self.name = ko.observable(name);
self.price = ko.observable(price);
}
function AppViewModel() {
var self = this;
// Observables for new product input fields
self.newProductName = ko.observable('').extend({
required: {
message: "Product name is required."
},
minLength: {
message: "Product name must be at least 2 characters long.",
params: 2
}
});
self.newProductPrice = ko.observable('').extend({
required: {
message: "Price is required."
},
min: {
message: "Price must be at least 1.",
params: 1
}
});
self.errors = ko.validation.group(self);
// Observable array to store the list of products
self.products = ko.observableArray([]);
// Observables for editing a product
self.isEditing = ko.observable(false);
self.currentProduct = ko.observable(null);
// Function to add a new product to the list
self.addProduct = function () {
if (self.errors().length === 0) {
self.products.push({
name: ko.observable(self.newProductName()), price: ko.observable(parseInt(self.newProductPrice())) }); self.newProductName(''); self.newProductPrice(''); // Hide modal using jQuery modal.modal('hide'); self.errors.showAllMessages(false); } else { self.errors.showAllMessages(); } }; // Function to start editing a product self.editProduct = function (product) { self.currentProduct(product); self.newProductName(product.name()); self.newProductPrice(product.price()); self.isEditing(true); UpdateModal(self.isEditing()); }; // Function to save changes to the edited product self.saveProduct = function () { if (self.errors().length === 0) { var product = self.currentProduct(); product.name(self.newProductName()); product.price(parseInt(self.newProductPrice())); self.isEditing(false); modal.modal('hide'); self.errors.showAllMessages(false); } else { self.errors.showAllMessages(); } }; // Function to delete a product from the list self.deleteProduct = function (product) { if (window.confirm("Are you sure, You want to delete this product.")) { self.products.remove(product); } }; self.showProductForm = function () { self.newProductName(''); self.newProductPrice(''); self.isEditing(false); UpdateModal(self.isEditing()); }; } // Apply Knockout.js bindings to the AppViewModel ko.applyBindings(new AppViewModel()); </script>
Explanation
HTML Structure:
- Two sections: one for displaying products list (
foreach
binding), another for adding/editing products (submit
,click
bindings). - Each product in the list has buttons for editing and deleting.
JavaScript :
- Product Model (
Product
): Defines aProduct
class with observable properties (id
,name
,price
). - ProductViewModel: Defines a view model with observable arrays (
products
), observable properties (productName
,productPrice
,currentProduct
), and functions for CRUD operations (saveProduct
,addProduct
,deleteProduct
,editProduct
,showProductForm
). - saveProduct: Handles update (PUT) operations for products.
- addProduct: Handles create (POST) operation for product
- deleteProduct: Sends a DELETE request to remove a product from the source.
- editProduct: Sets selected product for editing and populates form fields.
Read more
Getting Started with Templates in Knockout.js
Knockout.js: Building Dynamic Web Applications
Leave Comment