Data Grid با قابلیت درج حذف ویرایش در MVC توسط Knockout.js ، Require.js و WEB API

در این مقاله نحوه پیاده سازی HTML Data Grid با قابلیت درج حذف ویرایش در MVC توسط Knockout.js ، Require.js و WEB API را خواهید آموخت. در این تمرین داده های دیتا گرید از بانک اطلاعاتی پر می شوند.

 Data Grid با قابلیت درج حذف ویرایش در MVC توسط Knockout.js ، Require.js و WEB API

عموما جداول Html در برنامه ها برای نمایش مجموعه ای از داده ها از یک منبع خارجی استفاده می شوند. حال چگونه می توان این جداول را بسط داده تا بتوانند پویا شوند و همچنین عملیات Crud را انجام دهند. در این مثال ما از کتابخانه Knockout.js برای ساخت مدل و واکشی داده ها و کتابخانه JQuery برای مدیریت فراخوانی سرویس های ساخته شده توسط WEB API استفاده می نماییم.

ویژوال استادیو را باز کرده و یک پروژه Empty از نوع MVC ایجاد می کنیم. نام آن را ‘MVC5_HTmlTableCRUD’ می گذاریم. در پوشه App_Data یک بانک اطلاعاتی با نام Application.mdf ایجاد کرده و یک جدول با نام EmployeeInfo مانند زیر در آن می سازیم.

CREATE TABLE [dbo].[EmployeeInfo] (
    [EmpNo]       INT          IDENTITY (1, 1) NOT NULL,
    [EmpName]     NVARCHAR (50) NOT NULL,
    [DeptName]    NVARCHAR (50) NOT NULL,
    [Designation] NVARCHAR (50) NOT NULL,
    [Salary]      DECIMAL (18) NOT NULL,
    PRIMARY KEY CLUSTERED ([EmpNo] ASC)
);

جدول را مانند زیر پر می نماییم :
 

INSERT INTO [dbo].[EmployeeInfo] ([EmpNo], [EmpName], [DeptName], [Designation], [Salary]) VALUES (1, N'بردیا', N'آی تی', N'برنامه نویس', CAST(500000 AS Decimal(18, 0)))
INSERT INTO [dbo].[EmployeeInfo] ([EmpNo], [EmpName], [DeptName], [Designation], [Salary]) VALUES (2, N'علی', N'آی تی', N'برنامه نویس', CAST(500000 AS Decimal(18, 0)))
INSERT INTO [dbo].[EmployeeInfo] ([EmpNo], [EmpName], [DeptName], [Designation], [Salary]) VALUES (3, N'بهار', N'آی تی', N'برنامه نویس', CAST(500000 AS Decimal(18, 0)))
INSERT INTO [dbo].[EmployeeInfo] ([EmpNo], [EmpName], [DeptName], [Designation], [Salary]) VALUES (7, N'نرگس', N'آی تی', N'برنامه نویس', CAST(500000 AS Decimal(18, 0)))

مدل را به پروژه اضافه می نماییم.

پروژه را Build کنید. (F5)

برای استفاده از WEBAPI باید یک کنترلر از نوع Web API Controller مانند زیر به پروژه اضافه کنیم :

مدل و Context را مانند زیر انتخاب می کنیم.

پس از اینکه روی Add کلیک کردیم یک کنترلر WEB API با قابلیت Read و Write خواهیم ساخت.

در پوشه کنترلر یک کنترلر جدید مانند زیر می سازیم.

این یک کنترلر با متد Index خواهد ساخت. روی این متد راست کلیک کرده ویک ویو برای آن می سازیم.

به دلیل آنکه می خواهیم عمیات Data Grid را روی جدول html پیاده سازی کنیم ما نیاز به رفرنس دادن jQuery, Knockout Bootstrap و Require.js و کتابخانه های جاوا اسکریپت داریم.برای این کار باید مانند زیر از گزینه NuGet Package Manager استفاده نماییم. برای این کار روی نام پروژه کلیک راست کرده و گزینه NuGet Package Manager را انتخاب می نماییم. در این پنجره کتابخانه های مورد نیاز را جستجو کرده و نصب می نماییم.

jQuery

BootStrap

Knockout

Require

چرا از Required.js استفاده می کنیم؟

1-هنگامی که می خواهیم قسمت script از المنت های رابط کاربری جدا باشند ما نیاز به استفاده از کتابخانه های پشتیبانی MVVM و MVC داریم. ما در این مقاله برای پیاده سازی MVVM نیاز به Knockout.js داریم.

2-هنگامی که ما می خواهیم از Knockout.js برای ویو استفاده کنیم نیازمند DOM برای بارگذاری شدن داریم.
 سپس بعد از آن می توانیم مدل Knockout و تابع ها و غیره را با استفاده از المنت های رابط کاربری فراخوانی کنیم. در این جا ما کدهایی برای ویو مدل knockout می نویسیم و همچنین کدهایی برای ویو آن. بنابراین یک راه خوب در اینجا استفاده از RequireJS برای Asynchronous Module Definition (AMD به جای Knockout.js می باشد.

Asynchronous Module Definition (AMD به جای استفاده از برنامه پیمانه (Application Modularity) می باشد. این باعث می شود تا ماژول های کمتری برای نگهداری بیشتر استفاده نماییم.

()require برای بارگذاری ماژول های تعریف شده توسط ()define استفاده می شود. برای اطلاعات بیشتر راجع به آن به www.requirejs.org مراجعه نمایید.

پس از آنکه کتابخانه های مورد نیاز به پروژه اضافه شدند فایل های ViewModel.js و main.js را اضافه نمایید.

در ViewModel.js ویو مدل Knockout را تعریف می کنیم.

کدهای زیر را برای ViewModel.js می نویسیم :
 

//The model definition for jQuery and knockout dependencies
define("jquery", function () {
    return $;
});
 
define("knockout", function () {
    return ko;
});
 
//The viewModel definition
define("viewModel", ['jquery',"knockout"], function ($,ko) {
 
    var viewModel = function () {
        var self = this;
        //Boolean flag to check wheather the current operation is for 
        //Edit and add  New Record
        var isNewRecord = false;
        self.Message = ko.observable();
 
        //Observable Array
        self.Employees = ko.observableArray([]);
 
        loadData();
 
        //Function to Load Data using WEB API
        function loadData() {
            $.ajax({
                url: "/api/EmployeeInfoAPI",
                type:"GET"
            }).done(function (resp) {
                self.Employees(resp);
            }).fail(function (err) {
                self.Message("Error " + err.status);
            });
        };
 
        //The Employee Object used for Add, Edit, Delete operations
        function Employee(eno, ename, dname, desig, sal) {
            return {
                EmpNo: ko.observable(eno),
                EmpName: ko.observable(ename),
                DeptName: ko.observable(dname),
                Designation: ko.observable(desig),
                Salary: ko.observable(sal)
            }
        };
 
        //Observables for Templates
        self.readonlyTemplate=ko.observable("readonlyTemplate"),
        self.editTemplate = ko.observable()
 
        //Function to set the Template      
        self.setCurrentTemplate = function (tmpl) {
            return tmpl === this.editTemplate() ? 'editTemplate' : this.readonlyTemplate();
        }.bind(self);
 
        //Function to cancel edit effect
        self.reset = function (t) {
            self.editTemplate("readonlyTemplate");
        };
 
        //Function to add a new Empty row in table
        self.addRecord = function () {
            self.Employees.push(new Employee(0, "", "", "", 0.0));
            isNewRecord = true; //Set the Check for the New Record
        };
 
        //Function to save record
        self.save = function (e) {
            var Emp = {}; 
            Emp.EmpNo=e.EmpNo;
            Emp.EmpName=e.EmpName;
            Emp.Salary=e.Salary;
            Emp.DeptName=e.DeptName;
            Emp.Designation = e.Designation;
 
            if (isNewRecord === false) {
 
                $.ajax({
                    type: "PUT",
                    url: "/api/EmployeeInfoAPI/" + e.EmpNo,
                    data: Emp})
                    .done(function (resp) {
                        self.Message("Record Updated Successfully ");
                        self.reset();
                    })
                    .fail(function (err) {
                        self.Message("Error Occures, Please Reload the Page and Try Again " + err.status);
                        self.reset();
                    });
            }
 
            if (isNewRecord === true) {
                isNewRecord = false;
                $.ajax({
                    type: "POST",
                    url: "/api/EmployeeInfoAPI",
                    data: Emp})
                    .done(function (resp) {
                        self.Message("Record Added Successfully ");
                        self.reset();
                        loadData();
                    }).fail(function (err) {
                        self.Message("Error Occures, Please Reload the Page and Try Again " + err.status);
                        self.reset();
                    });
            }
        };
 
        //Function to Delete the Record
        self.delete = function (d) {
              
            $.ajax({
                type: "DELETE",
                url: "/api/EmployeeInfoAPI/" + d.EmpNo})
                .done(function (resp) {
                    self.Message("Record Deleted Successfully " + resp.status);
                    self.reset();
                    loadData();
                })
                .fail(function (err) {
                    self.Message("Error Occures, Please Reload the Page and Try Again " + err.status);
                    self.reset();
                });
        };
 
 
    };
 
    return new viewModel();
});

main.js را باز کرده و کدهای جاوااسکریپت زیر را برای آن می نویسیم :

//Define module loading for jQuey, knockout and view Model
require(['jquery','knockout', 'viewModel'], function ($,ko, viewModel) {
    $(document).ready(function () {
 
        //Instantiate page view model
        ko.applyBindings(viewModel);
 
    });
});

The above code uses require () function to load necessary modules on the UI. Currently we have jQuery, Knockout libraries and viewModel created using knockout.

Step 8: Open Index.cshtml and add the following markup with HTML templates and Databinding:
<h1>HTML Table with Fundamental DataGrid Capabilities using Knockout.js DataBinding.</h1>
<h2 class="center-block">Performing Add, Update, and Delete Operations</h2>
 
  
<script src="~/Scripts/jquery-2.1.4.min.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
<script src="~/Scripts/knockout-3.3.0.js"></script>
 
<link href="~/Content/bootstrap.min.css" rel="stylesheet" />
 
 
 
<script src="~/Scripts/require.js"></script>
<script src="~/Scripts/ViewModel.js"></script>
<script src="~/Scripts/main.js"></script>
 
 
 
<script type="text/javascript" data-main="~/Scripts/main.js" src="~/Scripts/require.js"></script>
 
 
<!--html templates -->
<script type="text/template" id="readonlyTemplate">
    <tr>
        <td>
            <span data-bind="text:EmpNo"></span>
        </td>
        <td>
            <span data-bind="text:EmpName"></span>
        </td>
        <td>
            <span data-bind="text: DeptName"></span>
        </td>
        <td>
            <span data-bind="text: Designation"></span>
        </td>
        <td>
            <span data-bind="text: Salary"></span>
        </td>
        <td>
            <button class="btn btn-lg glyphicon glyphicon-edit"
                    title="Click here to edit record" data-bind="click: function () { $root.editTemplate($data);}" />
        </td>
        <td>
            <button class="btn btn-lg glyphicon glyphicon-trash"
                    title="Click here to delete record"
                    data-bind="click:function () { $root.delete($data); }"></button>
        </td>
    </tr>
</script>
 
<script type="text/html" id="editTemplate">
    <tr>
        <td>
            <input type="text" class="form-control" data-bind="value: $data.EmpNo" id="txteno" disabled="disabled" />
        </td>
        <td>
            <input type="text" class="form-control" data-bind="value: $data.EmpName" id="txtename" />
        </td>
        <td>
            <input type="text" class="form-control" data-bind="value: $data.DeptName" id="txtdname" />
        </td>
        <td>
            <input type="text" class="form-control" data-bind="value: $data.Designation" id="txtdesig" />
        </td>
        <td>
            <input type="text" class="form-control" data-bind="value: $data.Salary" id="txtsal" />
        </td>
        <td>
            <button class="btn btn-lg glyphicon glyphicon-floppy-save" title="Click here to save record"
                    data-bind="click:$root.save"></button>
        </td>
        <td>
            <button class="btn btn-lg glyphicon glyphicon-remove-circle"
                    title="Click here to cancel" data-bind="click:$root.reset"></button>
        </td>
    </tr>
</script>
<!--ends here-->
 
 
<button type="button" class="btn btn-lg glyphicon glyphicon-plus-sign"
        title="Click here to add record"
        data-bind="click:$root.addRecord"></button>
 
<table class="table table-bordered table-condensed table-striped">
    <thead>
        <tr>
            <th class="text-center">
                EmpNo
            </th>
            <th class="text-center">
                EmpName
            </th>
            <th class="text-center">
                DeptName
            </th>
            <th class="text-center">
                Desigation
            </th>
            <th class="text-center">
                Salary
            </th>
            <th class="text-center">
            </th>
            <th class="text-center">
            </th>
        </tr>
    </thead>
    <tbody data-bind="template: { name: setCurrentTemplate, foreach: Employees }"></tbody>
</table>
<hr />
<div>
    <span data-bind="value:Message"></span>
</div>

کدهای بالا شامل نمونه های html از readOnlyTemplate و editTemplate می باشد. استایل های پروژه هم از bootstrap می باشند.

فایل RouteConfig.cs را در پوشه App_Start باز کرده و کنترلر پیش فرض (default) را مانند زیر تغییر دهید:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Employee", action = "Index", id = UrlParameter.Optional }
    );
}

برنامه را اجرا کرده و خروجی مانند زیر خواهد شد :


با کلیک روی دکمه (+) در بالای جدول یک ردیف read-only به جدول اضافه می شود.

 

برای ویرایش این سطر روی دکمه ویرایش (ششمین ستون از چپ) کلیک کرده و حال می توانید آن ردیف خالی را وبرایش نمایید.

سپس با کلیک روی دکمه ذخیره (تصویر فلاپی) می توان داده ها را درج کرده و ذخیره نمود.

همچنین می توان ریدف ها را توسط دکمه حذف (تصویر سطل زباله) حذف نمود.

نتیجه گیری

کتابخانه Knockout.js همراه با JQuery ویژگی های خوبی را برای پیاده سازی برنامه های rich UX بر پایه MVVM توسط المنت های رابط کاربری html فراهم می کنند. همچنین توصیه می شود که از Asynchronous Module Definition (AMD) برای مدیریت ماژول استفاده نمایید. این می تواند توسط RequireJS پیاده سازی شود.

فایل های ضمیمه