چهار عمل اصلی (CRUD) با استفاده از KnockOut.js در MVC

Knockout.Js یک فرم ورک از جاوااسکریپت است که امکان اینکه به صورت اسکریپتی الگوی MVVM را پیاده سازی کنیم .با کمک Knockout.Js مکانیزم data-binding به راحتی پیاده سازی خواهد شد .کاری که Knockout.Js انجام می دهد این است که عناصر DOM را به data-model و اشیاء data-model را به عناصر HTML مقید می کند .

 چهار عمل اصلی (CRUD) با استفاده از KnockOut.js در MVC

Knockout.Js یک فرم ورک از جاوااسکریپت است که امکان اینکه به صورت اسکریپتی الگوی MVVM را پیاده سازی کنیم .با کمک Knockout.Js مکانیزم data-binding به راحتی پیاده سازی خواهد شد .کاری که Knockout.Js انجام می دهد این است که عناصر DOM را به data-model و اشیاء data-model را به عناصر HTML مقید می کند .

با کمک این فرم ورک اگر تغییری در عناصر صفحه اعمال کنید تغییرات به مواردی که به این عناصر Bind شده اند هم اعمال می گردد. در این مقاله قصد داریم از این فرم ورک به همراه MVC استفاده کنیم .

در داخل پایگاه داده خود یک دیتابیس ساده به نام Sample ایجاد می کنیم .در داخل این دیتابیس یک جدول به نام TblProductList ایجاد می کنیم .شکل این جدول در زیر آمده است .

یک پروژه MVC از نوع Empty ایجاد کنید

با استفاده از NuGet اقدام به نصب Entity Framework 6 و Knockout می کنیم .همچنین در داخل پروژه خود باید jquery.js را هم داشته باشید .

بر روی پوشه مدل کلیک راست کرده و گزینه ADO.NET Entity data Model را بزنید .نام مدل خود را ProductDataContext.edmx بگذارید

بعد از این پنجره گزینه Generate from Database را کلیک کنید .و سپس رشته اتصال خود را به همان دیتابیس sample که ساخته بودید تنظیم کنید .

نهایتا مدل دیتابیس شما در داخل برنامه ساخته خواهد شد که به صورت زیر است

یک فولدر جدید به نام interface ایجاد کنید و در داخل آن اینترفیس زیر را اضافه کنید

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

یک فولدر جدید به نام Repositories در داخل برنامه ایجاد کرده و یک کلاس به نام  ProductRepository.cs در آن ایجاد کنید در داخل این کلاس متدهای مربوط به  چهار عمل اصلی حذف اضافه ایجاد و ویرایش را می نویسیم

public class ProductRepository : IProductRepository
   {
       ProductDBEntities ProductDB = new ProductDBEntities();

       public IEnumerable<TblProductList> GetAll()
       {
           // TO DO : Code to get the list of all the records in database
           return ProductDB.TblProductLists;
       }

       public TblProductList Get(int id)
       {
           // TO DO : Code to find a record in database
           return ProductDB.TblProductLists.Find(id);
       }

       public TblProductList Add(TblProductList item)
       {
           if (item == null)
           {
               throw new ArgumentNullException("item");
           }

           // TO DO : Code to save record into database
           ProductDB.TblProductLists.Add(item);
           ProductDB.SaveChanges();
           return item;
       }

       public bool Update(TblProductList item)
       {
           if (item == null)
           {
               throw new ArgumentNullException("item");
           }

           // TO DO : Code to update record into database

           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)
       {
           // TO DO : Code to remove the records from database

           TblProductList products = ProductDB.TblProductLists.Find(id);
           ProductDB.TblProductLists.Remove(products);
           ProductDB.SaveChanges();

           return true;
       }
   }

در مرحله بعد بر روی کنترلر کلیک راست کرده و گزینه Add Controller را بزنید. نام این کنترلر را ProductController می گذاریم .در داخل کنترلر کدهای زیر را می نویسیم .

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);

        }
    }

بر روی اکشن Products کلیک راست کرده و گزینه AddView را می زنیم .در داخل view کدهای زیر را می نویسیم

@{
    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>

یک فایل جاوااسکریپت به نام KnockoutDemo.js ایجاد می کنیم و چهار عمل اصلی یا همان CRUD را داخل آن می نویسیم

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: 'Product/GetAllProducts',
        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: '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 !!');
        }
    }
    // Delete product details
    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);
            });
        }
    }

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

    // Update product details
    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); //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);

در داخل Layout هم صفحه ما شبیه کدهای زیر خواهد بود

<!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>

بعد از این صفحه استایل CustomStyle.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: '';
              }

حال در داخل Rout.Config.cs مسیر پیش فرض را تغییر می دهیم .

routes.MapRoute(
              name: "Default",
              url: "{controller}/{action}/{id}",
              defaults: new { controller = "Product", action = "Products", id = UrlParameter.Optional }
           );

بعد از اجرا و وارد کردن چند دیتای ساده شکل زیر را خواهیم دید

فایل های ضمیمه
دانلود نسخه ی PDF این مطلب