آموزش پروژه محور Kendo CRUD

چاپ

آموزش پروژه محور Kendo CRUD

در این آموزش، قصد داریم تا نحوه انجام عملیات (CRUD (Create، Retrieve، Update، Delete را نمایش دهیم و علاوه بر آن نحوه مدیریت خطاهای سمت سرور را بررسی می کنیم و آن ها را در سرور نمایش می دهیم.

برای شروع، ترجیح می دهیم اطلاعات بیشتری از پایگاه داده واکشی کنیم. فیلدهای Title، BirthDate و City را به شیء Employee اضافه کنید. همچنین یک سازنده ایجاد کنید که یک شیء Data.Employee را به عنوان ورودی می گیرد و سپس آن را به خصوصیت های مدل نگاشت می کند.

این کار کدهای EmployeeController را خواناتر می کند.

حال شیء مدل Employee به صورت زیر در می آید:

شیء مدل Employee

public class Employee {

    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Title { get; set; }
    public DateTime BirthDate { get; set; }
    public string City { get; set; }

    public Employee(hello_kendo_ui.Data.Employee employee) {
        this.Id = employee.EmployeeID;
        this.FirstName = employee.FirstName;
        this.LastName = employee.LastName;
        this.Title = employee.Title;
        this.BirthDate = employee.BirthDate;
        this.City = employee.City;
    }
}

در این مرحله نیاز است تا context مربوط به LINQToSQL را تغییر دهید تا فیلد تاریخ تولد را یک فیلد non-nullable کنید. این کار باید در پایگاه داده نیز انجام شود.

متد Get کلاس EmployeesController حالا کمی متفاوت تر می شود.

متد Get کلاس EmoloyeesController

Code
// WebAPI will respond to an HTTP GET with this method
public Models.Response Get() {

    // the the take and skip parameters off of the incoming request
    int take = _request["take"] == null ? 10 : int.Parse(_request["take"]);
    int skip = _request["skip"] == null ? 0 : int.Parse(_request["skip"]);

    // get all of the records from the employees table in the
    // northwind database.  return them in a collection of user
    // defined model objects for easy serialization. skip and then
    // take the appropriate number of records for paging.
    var employees = (from e in _context.Employees
                     select new Models.Employee(e)).Skip(skip).Take(take).ToArray();

    // returns the generic response object which will contain the
    // employees array and the total count
    return new Models.Response(employees, _context.Employees.Count());
}

تغییر در پیکربندی گرید برای نمایش صفحه بندی

در نهایت باید تنظیماتی را به پیکر بندی Grid اضافه کنیم تا صفحه بندی سمت سرور تکمیل گردد.

تمام خصوصیت های مربوط به تنظیم پیکربندی سمت سرور و اتصال به پایگاه داده در آموزش اتصال به پایگاه داده توضیح داده شده است.

فقط به اختصار توضیح داده می شود که برای این امر باید خصوصیت schema را به dataSource اضافه کنیم و داخل بدنه آن گزینه های data و total را تنظیم کنیم.

پیکربندی Grid برای صفحه بندی سمت سرور


<script >

$(function () {
$("#employeesGrid").kendoGrid({
columns: [
{ field: "FirstName", title: "First Name" },
{ field:"LastName", title: "Last Name" }
],
dataSource: new kendo.data.DataSource({
transport: {
read: "api/employees",
},
pageSize:3,
serverPaging: true,
schema: {
data: "Data",
total: "Count"
}
}),
pageable: true
});
});
</script>

برنامه را اجرا کنید و نتیجه را به صورت شکل زیر ملاحظه فرمایید:

صفحه بندی سمت سرور در Kendo UI Grid


افزودن قابلیت حذف به Grid

برای انجام عملیات CRUD در grid، می بایست یک سری کد مربوط به UI اضافه کنید. Kendo UI گزینه های مختلفی را برای انجام چنین کاری در اختیار می گذارد. می توانید ردیف های grid را به صورت پاپ آپ یا inline ویرایش کنید. می توانید ویرایشگر سفارشی خودتان را داشته باشید.

برای این مثال، از ویرایش inlineی که Kendo UI grid در اختیار می گذارد استفاده می کنیم.

برای شروع، فایل Default.aspx را باز کنید. روی grid، نیاز است که خصوصیت editable:true را تنظیم فرمایید. تغییرات را ذخیره نمایید و صفحه را اجرا نمایید. متوجه خواهید شد که هنگام کلیک روی ردیف، آن ردیف قابل ویرایش می شود.

افزودن قابلیت حذف به Grid


editable: true

قبل از این که نتیجه را مشاهده فرمایید، برای اینکه ستون های اضافه شده در grid نمایش داده شود، تنظیمات پیکر بندی grid را مانند آن چه در زیر آمده است تغییر دهید.

پیکر بندی Grid برای نمایش ستون های Grid

 <script>
$(function () {
$("#employeesGrid").kendoGrid({
columns: [
{ field: "FirstName", title: "First Name" },
{ field:"LastName", title: "Last Name" },
"Address",
"City",
{ field: "BirthDate", title: "Birthday", format: "{0:MM/dd/yyyy}" }


],
dataSource: new kendo.data.DataSource({
transport: {
read: "api/employees",
},
pageSize:3,
serverPaging: true,
schema: {
data: "Data",
total: "Count"
},

}),
pageable: true,
editable:true
});
});
</script>

قابلیت ویرایش در grid

برای اینکه تجربه خوبی از ویرایش داشته باشد، از ستون command column در grid استفاده کنید.

در فایل Default.aspx، ستونی اضافه کنید که دستورات را مشخص می کند. در این مثال edit و destroy را مشخص کنید. عنوان آن را خالی بگذارید.

همچنین فرمت DateTime را که قرار است به عنوان تاریخ تولد سمت کاربر ارسال شود را تعیین فرمایید.

تعیین ستون Command


<script>
$(function () {
$("#employeesGrid").kendoGrid({
columns: [
{ field: "FirstName", title: "First Name" },
{ field:"LastName", title: "Last Name" },
"Address",
"City",
{ field: "BirthDate", title: "Birthday", format: "{0:MM/dd/yyyy}" },

{ command: ["edit", "destroy"], title: " " }

],
dataSource: new kendo.data.DataSource({
transport: {
read: "api/employees",
},
pageSize:3,
serverPaging: true,
schema: {
data: "Data",
total: "Count"
},

}),
pageable: true,
editable:true
});
});
</script>

با این کار دو دکمه Edit و Delete در ستون آخر grid ایجاد می شود. این ستون دارای عنوان نمی باشد. اکنون برنامه کاربردی دکمه هایی را نمایش می دهد، اما اگر روی آن ها کلیک کنید هیچ اتفاقی نمی افتد، چرا که grid انتظار دارد شما روی خانه های جدول کلیک کنید تا به حالت ویرایش برود.

مشخص کردن قابلیت ویرایش درون خطی

 
editable: "inline"

با این تغییر، وقتی روی دکمه کلیک می کنید، تمام ردیف به حالت ویرایش می رود. همچنین توجه فرمایید که grid به صورت خودکار، دکمه های Cancel و Update را نمایش می دهد. اگر روی دکمه Delete کلیک کنید، فرمانی مبنی بر اینکه آیا از حذف آیتم اطمینان دارید، به شما نمایش داده خواهد شد.

در هر حال توجه فرمایید که در حال حاضر grid فیلد BirthDate را در قالب textbox دارد و به شما اجازه می دهد که LastName را خالی کنید. این کار درست به نظر نمی رسد.

شما می توانید محدویت هایی را به ستون هایی که قصد ویرایش آن دارید اضافه کنید. این محدودیت ها را می توان با مشخص کردن مدلی در شمای DataSource به grid اضافه کرد.

شیء Model ساختار سمت کلاینتی را مشخص می کند که داده ها را از نظر نوع و صحت سنجی مشخص می کند.

مدلی در تعریف schema اضافه کنید که مشخص می کند id به فیلد Id پایگاه داده نگاشت شده است. سپس یک شیء fields ایجاد کنید. هر شیء داخل fields می تواند یک رشته ساده باشد، یا شیء ای باشد که اطلاعات بیشتری در مورد فیلد مدل فراهم می کند.

برای اینکه مشخص کنیم پر کردن این فیلد الزامی است می توانیم یک شیء validation به آن اضافه کنیم.

برای اینکه grid به شما یک DatePicker نمایش دهد تا با استفاده از آن مقدار ستون BirthDate را مشخص کنید، نوع این فیلد را date بگذارید.

مشخص کردن مدل برای DataSource

 
// the schema defines the schema of the JSON coming
// back from the server so the datasource can parse it
schema: {
// the array of repeating data elements (employees)
data: "Data",
// the total count of records in the whole dataset. used
// for paging.
total: "Count",
model: {
    id: "Id",
    fields:
{ FirstName: { editable: false }, LastName: { editable: true, nullable: false, validation: { required: true} }, Address: { editable: true, nullable: false, validation: { required: true} }, City: { editable: true, nullable: false, validation: { required: true} }, BirthDate: { editable: true, type: "date" }
} } }

در تعریف بالا، فیلد FirstName به عنوان فیلدی که ویرایش نمی شود مشخص گردیده است (در صورتی که این فیلد در پایگاه داده قابل ویرایش است.).

برنامه را اجرا کنید. می بینید که هنگامی که grid را به حالت ویرایش می برید یک Kendo UI DatePicker برای درج مقدار BirthDate به شما نمایش داده می شود. توجه فرمایید که مقدار ستون FirstName نیز قابل ویرایش نیست. به علاوه اگر بخواهید مقدار lastName را خالی بگذارید grid یک پیغام به صورت پاپ آپ به شما نمایش می دهد که نشان می دهد LastName نیاز است.

تعریف مدل سمت کلاینت در Kendo UI

اگر قصد داشته باشید که پیغام مربوطه را تغییر دهید، کافی است تعریف model را تغییر دهید و آن را به صورت زیر تعریف کنید:

صحت سنجی سفارشی شده

LastName: {
    editable: true,
    nullable: false,
    validation: {
        required: {
            message: "Please enter a last name for this employee"
        }
    }
}

تعیین عملیات سمت سرور در Grid

اکنون grid قبل ویرایش به نظر می رسد، اما در واقع این چنین نیست، زیرا در حال حاضر به سرور اتصالی ندارد. اولین گام برای انجام کار مشخص کردن اشیاء update و destroy می باشد. سروری که می خواهیم آن را صدا بزنیم کاملا RESTful می باشد. این بدان معناست که عملیات update در api/employees/id با POST انجام می شود. یک مثال برای انجام این کار عبارت است از https://beyamooz.com/api/employees/31. با این کار، مشخصات کارمند شماره 31 ویرایش می گردد.

متد delete نیز تقریباً مانند همین روش مدیریت می گردد، با این تفاوت که type با DELETE تنظیم می شود. همچنین از delete به عنوان نام شیء استفاده نمی کنیم زیرا واژه delete در جاوا اسکریپت یک کلیدواژه می باشد.

پارامتر Id را می توان با تنظیم قسمت transport مربوط به url بازیابی کنیم.

Kendo UI به صورت خودکار، آیتم مدل جاری را به این تابع ارسال می کند و سمت سرور این Id قابل بازیابی است.

افزودن Update و Create و Destroy به Transport

// the transport tells the datasource what endpoints
// to use for CRUD actions
transport: {
    read: "api/employees",
    update: {
        url: function (employee) {
            return "api/employees/" + employee.Id
        },
        type: "POST"
    },
    destroy: {
        url: function (employee) {
            return "api/employees/" + employee.Id
        },
        type: "DELETE"
    }
}
 

مدیریت عملیات CRUD سمت سرور

گام بعدی عبارت است از مدیریت درخواست های HTTP در کلاس EmployeeController. از آن جایی که این پروژه بر اساس پروژه Hello Service ایجاد گردیده است، بنابراین مدیریت درخواست هایی از نوع DELETE قبلاً آماده شده است. در این مثال تنها کافی است، متد مربوط به update را ایجاد نماییم. در این متد، قصد داریم اطلاعات کارمندان در پایگاه داده را بر اساس پارامتر id که برای سرور ارسال می شود ویرایش کنیم. بنابراین ابتدا id را دریافت و سپس فیلدها را یکی یکی برای به روز رسانی مشخص می کنیم.

به این دلیل که نمی توانیم به صحت سنجی که در سمت کلاینت اتفاق می افتد اتکا کنیم بایستی یک بار دیگر سمت سرور این عملیات را انجام دهیم تا این اطمینان حاصل شود که قبل از درج اطلاعات در پایگاه داده، داده ها از صحت لازم برخوردار هستند و یا داده ها خالی نیستند.

بعد از به روز رسانی فقط بایستی پیغام موفقیت را با "OK" یا 200 به grid ارسال کنیم تا آن را متوجه موفقیت آمیز بودن عملیات نماییم. می توانید با استفاده از شیء HttpResponseMessage این کار را انجام دهید. اگر به روز رسانی موفقیت آمیز بود، 200 را برگردانید. اگر به روز رسانی با مشکل مواجه شده بود، 500 را به همراه یک پیغام در بدنه پاسخ بفرستید و شرح خیلی مختصری بر آن چه اتفاق افتاده را به آگاهی grid برسانید.

افزودن Post به Transport

 
public HttpResponseMessage Post(int id) {
    // create a response message to send back
    var response = new HttpResponseMessage();
    try {
        // select the employee from the database where the id
        // matches the one passed in at api/employees/id
        var employeeToUpdate = (from e in _context.Employees
                                where e.EmployeeID == id
                                select e).FirstOrDefault();

        // if there was an employee returned from the database
        if (employeeToUpdate != null) {

            // update the employee object handling null values or empty strings
            employeeToUpdate.LastName = string.IsNullOrEmpty(_request["LastName"]) ? employeeToUpdate.LastName : _request["LastName"];
            employeeToUpdate.Address = string.IsNullOrEmpty(_request["Address"]) ? employeeToUpdate.Address : _request["Address"];
            employeeToUpdate.City = string.IsNullOrEmpty(_request["City"]) ? employeeToUpdate.City : _request["City"];
            employeeToUpdate.BirthDate = string.IsNullOrEmpty(_request["BirthDate"]) ? employeeToUpdate.BirthDate : Convert.ToDateTime(_request["BirthDate"]);

            // submit the changes to the database
            _context.SubmitChanges();

            // set the server response to OK
            response.StatusCode = HttpStatusCode.OK;
        } 
        else {
            // we couldn't find the employee with the passed in id
            // set the response status to error and return a message
            // with some more info.
            response.StatusCode = HttpStatusCode.InternalServerError;
            response.Content = new StringContent(string.Format("The employee with id {0} was not found in the database", id.ToString()));
        }
    } catch (Exception ex) {
        // something went wrong - possibly a database error. return a
        // 500 server error and send the details of the exception.
        response.StatusCode = HttpStatusCode.InternalServerError;
        response.Content = new StringContent(string.Format("The database updated failed: {0}", ex.Message));
    }

    // return the HTTP Response.
    return response;
}

متد بالا را با اجرای برنامه و ویرایش یکی از رکوردهای grid امتحان کنید. توجه فرمایید اگر هیچ تغییری رخ نداد، احتمالاً به این خاطر است که grid هیچ اطلاعاتی سمت سرور ارسال نمی کند. ابزار توسعه (F12) را باز کنید و به به سربرگ Network بروید. اگر از شما خواست که برای شروع Capture کردن درخواست ها، صفحه را refresh کنید، این کار را انجام دهید. grid را به حالت ویرایش ببرید و یک فیلد را تغییر دهید و سپس روی دکمه Update کلیک کنید.

باید خطایی در درخواست شبکه ای ببینید. اگر روی خطا کلیک کنید، متوجه خواهید شد که تبدیل BirthDate به مقدار صحیح DateTime امکان پذیر نبوده است. اگر پارامترهای درخواست را امتحان کنید، می بینید که فرمت عجیب و غریبی از تاریح سمت سرور ارسال گردیده است.

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

به این خاطر که تاریخی که برای جاوا اسکریپت (JavaScript) فرمت شده است، متفاوت است از فرمتی که برای کار با آن نیاز داریم. بنابراین باید فرمت صحیح تاریخ را ارسال کنیم. برای این کار، متد parameterMap را به transport در پایگاه داده اضافه می کنیم. parameterMap دو پارامتر می گیرد، یکی options و دیگری operations. مورد اول یعنی options به پارامترهایی گفته می شود که Kendo UI آن ها را ارسال می کند. operation یکی از چهار مورد read، update، create، یا destroy می باشد. شما همواره بایستی همواره وقتی تابع parameterMap را تعریف می کنید حداقل options را به عنوان پارامتر تعریف کنید. در این مثال برای عملیات update تاریخ را فرمت می کنیم و مقدار این پارامتر را در option تعییر می دهیم.

تغییر فرمت BirthDate در Parameter Map

// the transport tells the datasource what endpoints
// to use for CRUD actions
transport: {
    read: "api/employees",
    update: {
        url: function (employee) {
            return "api/employees/" + employee.Id
        },
        type: "POST"
    },
    destroy: {
        url: function (employee) {
            return "api/employees/" + employee.Id
        },
        type: "DELETE"
    },
    parameterMap: function (options, operation) {

        // if the current operation is an update
        if (operation === "update") {
            // create a new JavaScript date object based on the current
            // BirthDate parameter value
            var d = new Date(options.BirthDate);
            // overwrite the BirthDate value with a formatted value that WebAPI
            // will be able to convert
            options.BirthDate = kendo.toString(new Date(options.BirthDate), "MM/dd/yyyy");
        }

        // ALWAYS return options
        return options;
    }
}

اکنون باید update درست کار کند.


سایر روش های ایجاد خطا

بیش از یک راه برای نشان دادن خطا به Kendo UI وجود دارد. تنها کاری که شما باید انجام دهید این است که مقداری را برای فیلد errors در مدل برگردانید. به عبارتی شیء Models.Response را تغییر دهید، و یک فیلد Errors به آن اضافه کنید. به علاوه یک سازنده که فقط پارامتر error را دریافت می کند ایجاد کنید و مقدار خطا را تخصیص دهید و یک سازنده بدون پارامتر نیز ایجاد کنید.

افزودن فیلد Errors به شیء Response

 
public class Response {

    public Array Data { get; set; }
    public int Count { get; set; }
    public string Errors { get; set; }

    public Response(Array data, int count) {
        this.Data = data;
        this.Count = count;
    }

    public Response(string errors) {
        this.Errors = errors;
    }
}
 

در حالتی که به روز رسانی موفقیت آمیز بود، شیء Model.Response را خالی برگردانید و در حالتی که به روز رسانی با شکست مواجه گردید، مقدار Errors را پر کنید و شیء Model.Response را ارسال نمایید.

برگرداندن خصوصیت Errors هنگام وقوع خطا

 
public Models.Response Delete(int id) {

    try {
        // retrieve the employee to update from the database
        // based on the parameter passed in from api/employees/id
        var employeeToDelete = (from e in _context.Employees
                                where e.EmployeeID == id
                                select e).FirstOrDefault();

        // if a valid employee object was found by id
        if (employeeToDelete != null) {
            // mark the object for deletion
            _context.Employees.DeleteOnSubmit(employeeToDelete);
            // delete the object from the database
            _context.SubmitChanges();

            // return an empty Models.Response object (this returns a 200 OK)
            return new Models.Response();
        } 
else { // otherwise set the error field of a response object and return it. return new Models.Response(string.Format("The employee with id {0} was not found in the database", id.ToString())); } } catch (Exception ex) { // something went wrong. set the errors field of return new Models.Response(string.Format("There was an error updating employee with id {0}: {1}", id.ToString(), ex.Message)); } }

اکنون باید شمای شیء را تغییر دهید و خصوصیت Errors مربوط به شیء response را با errors پر نمایید. هنگامی که این فیلد مقدار می گیرد، به صورت خودکار رویداد error در DataSource صدا زده می شود. می توانید این رویداد را خودتان مدیریت کیند.

نگاشت فیلدهای خطا در Schema

 
// the schema defines the schema of the JSON coming
// back from the server so the datasource can parse it
schema: {
    // the array of repeating data elements (employees)
    data: "Data",
    // the total count of records in the whole dataset. used
    // for paging.
    total: "Count",
    model: {
        id: "Id",
        fields: {
            FirstName: { editable: false },
            LastName: { editable: true, nullable: false, validation: { required: true} };
            Address: { editable: true, nullable: false, validation: { required: true} },
            City: { editable: true, nullable: false, validation: { required: true} },
            BirthDate: { editable: true, type: "date" }
        }
    },
    // map the errors if there are any. this automatically raises the "error"
    // event
    errors: "Errors"
    }
}
error: function (e) {
    alert(e.errors);
}

نگاشت خطاها

در سناریوی بالا، وقتی می خواهید کارمند خاصی را از جدول Employees حذف کنید - مانند Nancy Davalio- خطایی که در تصویر زیر ملاحظه فرمایید از سمت سرور صادر می شود.

نمایش خطای حذف

هرگز چنین اطلاعات زیادی را از طرف پایگاه داده به کاربر نشان ندهید!!

همواره یک استراتژی برای هشدار دادن و خطاهای استاندارد مد نظر قرار دهید. یک راه خوب این است که خطا ها را log کنید و به کاربر اطلاعاتی را که نیاز دارد نمایش دهید. در مثال بالا delete به این خاطر با شکست مواجه شد که Nancy در جدول Orders دارای رکورد بود و به خاطر وجود ارتباط بین دو جدول عملیات delete موفقیت آمیز نبود.

اگر خطایی نمایش داده نشود، کاربر متوجه نخواهد شد که عملیات delete با شکست مواجه شده است، زیرا ردیف از grid حذف خواهد شد. این چیزی نیست که شما در نظر دارید. در هر حال DataSource باید روش مدیریت کار در این وضعیت را بداند. در حال حاضر شیء به صورت dirty ذخیره می شود و در اولین فرصت دوباره sync می شود. بهترین روش در این گونه حالات، برگرداندن تغییرات است. این کار را با مشخص کردن cancelChange در DataSource مشخص می کنیم.

لغو تغییرات هنگامی که درخواست با خطا مواجه می شود

 
error: function (e) {
    alert("The action failed. Please see the logs.");
    this.cancelChanges();
}

خطوط کد بالا تغییرات را در grid به عقب بر می گرداند و ردیف را دوباره نمایش می دهد.