عنوان:

‫صفحه بندی، مرتب سازی و جستجوی پویای اطلاعات به کمک Kendo UI Grid


نویسنده: وحید نصیری
تاریخ: ۱۳۹۳/۰۸/۱۶ ۱۶:۳۰
آدرس: www.dntips.ir
پس از آشنایی مقدماتی با Kendo UI DataSource، اکنون می‌خواهیم از آن جهت صفحه بندی، مرتب سازی و جستجوی پویای سمت سرور استفاده کنیم. در مثال قبلی، هر چند صفحه بندی فعال بود، اما پس از دریافت تمام اطلاعات، این اعمال در سمت کاربر انجام و مدیریت می‌شد.



مدل برنامه

در اینجا قصد داریم لیستی را با ساختار کلاس Product در اختیار Kendo UI گرید قرار دهیم:
namespace KendoUI03.Models
{
    public class Product
    {
        public int Id { set; get; }
        public string Name { set; get; }
        public decimal Price { set; get; }
        public bool IsAvailable { set; get; }
    }
}


پیشنیاز تامین داده مخصوص Kendo UI Grid

برای ارائه اطلاعات مخصوص Kendo UI Grid، ابتدا باید درنظر داشت که این گرید، درخواست‌های صفحه بندی خود را با فرمت ذیل ارسال می‌کند. همانطور که مشاهده می‌کنید، صرفا یک کوئری استرینگ با فرمت JSON را دریافت خواهیم کرد:
 /api/products?{"take":10,"skip":0,"page":1,"pageSize":10,"sort":[{"field":"Id","dir":"desc"}]}
سپس این گرید نیاز به سه فیلد، در خروجی JSON نهایی خواهد داشت:
{
"Data":
[
{"Id":1500,"Name":"نام 1500","Price":2499.0,"IsAvailable":false},
{"Id":1499,"Name":"نام 1499","Price":2498.0,"IsAvailable":true}
],
"Total":1500,
"Aggregates":null
}
فیلد Data که رکوردهای گرید را تامین می‌کنند. فیلد Total که بیانگر تعداد کل رکوردها است و Aggregates که برای گروه بندی بکار می‌رود.

می‌توان برای تمام این‌ها، کلاس و Parser تهیه کرد و یا ... پروژه‌ی سورس بازی به نام  Kendo.DynamicLinq نیز چنین کاری را میسر می‌سازد که در ادامه از آن استفاده خواهیم کرد. برای نصب آن تنها کافی است دستور ذیل را صادر کنید:
 PM> Install-Package Kendo.DynamicLinq
Kendo.DynamicLinq به صورت خودکار System.Linq.Dynamic را نیز نصب می‌کند که از آن جهت صفحه بندی پویا استفاده خواهد شد.


تامین کننده‌ی داده سمت سرور

همانند مطلب کار با Kendo UI DataSource ، یک ASP.NET Web API Controller جدید را به پروژه اضافه کنید و همچنین مسیریابی‌های مخصوص آن‌را به فایل global.asax.cs نیز اضافه نمائید.
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using Kendo.DynamicLinq;
using KendoUI03.Models;
using Newtonsoft.Json;

namespace KendoUI03.Controllers
{
    public class ProductsController : ApiController
    {
        public DataSourceResult Get(HttpRequestMessage requestMessage)
        {
            var request = JsonConvert.DeserializeObject<DataSourceRequest>(
                requestMessage.RequestUri.ParseQueryString().GetKey(0)
            );

            var list = ProductDataSource.LatestProducts;
            return list.AsQueryable()
                       .ToDataSourceResult(request.Take, request.Skip, request.Sort, request.Filter);
        }
    }
}
تمام کدهای این کنترلر همین چند سطر فوق هستند. با توجه به ساختار کوئری استرینگی که در ابتدای بحث عنوان شد، نیاز است آن‌را توسط کتابخانه‌ی JSON.NET تبدیل به یک نمونه از DataSourceRequest نمائیم. این کلاس در Kendo.DynamicLinq تعریف شده‌است و حاوی اطلاعاتی مانند take و skip کوئری LINQ نهایی است.
ProductDataSource.LatestProducts صرفا یک لیست جنریک تهیه شده از کلاس Product است. در نهایت با استفاده از متد الحاقی جدید ToDataSourceResult، به صورت خودکار مباحث صفحه بندی سمت سرور به همراه مرتب سازی اطلاعات، صورت گرفته و اطلاعات نهایی با فرمت DataSourceResult بازگشت داده می‌شود. DataSourceResult نیز در Kendo.DynamicLinq تعریف شده و سه فیلد یاد شده‌ی Data، Total و Aggregates را تولید می‌کند.

تا اینجا کارهای سمت سرور این مثال به پایان می‌رسد.


تهیه View نمایش اطلاعات ارسالی از سمت سرور

اعمال مباحث بومی سازی
<head>
    <meta charset="utf-8" />
    <meta http-equiv="Content-Language" content="fa" />
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

    <title>Kendo UI: Implemeting the Grid</title>

    <link href="styles/kendo.common.min.css" rel="stylesheet" type="text/css" />
    <!--شیوه نامه‌ی مخصوص راست به چپ سازی-->
    <link href="styles/kendo.rtl.min.css" rel="stylesheet" />
    <link href="styles/kendo.default.min.css" rel="stylesheet" type="text/css" />
    <script src="js/jquery.min.js" type="text/javascript"></script>
    <script src="js/kendo.all.min.js" type="text/javascript"></script>

    <!--محل سفارشی سازی پیام‌ها و مسایل بومی-->
    <script src="js/cultures/kendo.culture.fa-IR.js" type="text/javascript"></script>
    <script src="js/cultures/kendo.culture.fa.js" type="text/javascript"></script>
    <script src="js/messages/kendo.messages.en-US.js" type="text/javascript"></script>

    <style type="text/css">
        body {
            font-family: tahoma;
            font-size: 9pt;
        }
    </style>

    <script type="text/javascript">
        // جهت استفاده از فایل: kendo.culture.fa-IR.js
        kendo.culture("fa-IR");
    </script>
</head>
- در اینجا چند فایل js و css جدید اضافه شده‌اند. فایل kendo.rtl.min.css جهت تامین مباحث RTL توکار Kendo UI کاربرد دارد.
- سپس سه فایل kendo.culture.fa-IR.js، kendo.culture.fa.js و kendo.messages.en-US.js نیز اضافه شده‌اند. فایل‌های fa و fa-Ir آن هر چند به ظاهر برای ایران طراحی شده‌اند، اما نام ماه‌های موجود در آن عربی است که نیاز به ویرایش دارد. به همین جهت به سورس این فایل‌ها، جهت ویرایش نهایی نیاز خواهد بود که در پوشه‌ی src\js\cultures مجموعه‌ی اصلی Kendo UI موجود هستند (ر.ک. فایل پیوست).
- فایل kendo.messages.en-US.js حاوی تمام پیام‌های مرتبط با Kendo UI است. برای مثال«رکوردهای 10 تا 15 از 1000 ردیف» را در اینجا می‌توانید به فارسی ترجمه کنید.
- متد kendo.culture کار مشخص سازی فرهنگ بومی برنامه را به عهده دارد. برای مثال در اینجا به fa-IR تنظیم شده‌است. این مورد سبب خواهد شد تا از فایل kendo.culture.fa-IR.js استفاده گردد. اگر مقدار آن‌را به fa تنظیم کنید، از فایل kendo.culture.fa.js کمک گرفته خواهد شد.

راست به چپ سازی گرید
تنها کاری که برای راست به چپ سازی Kendo UI Grid باید صورت گیرد، محصور سازی div آن در یک div با کلاس مساوی k-rtl است:
    <div class="k-rtl">
        <div id="report-grid"></div>
    </div>
k-rtl و تنظیمات آن در فایل kendo.rtl.min.css قرار دارند که در ابتدای head صفحه تعریف شده‌است.

تامین داده و نمایش گرید

در ادامه کدهای کامل DataSource و Kendo UI Grid را ملاحظه می‌کنید:
    <script type="text/javascript">
        $(function () {
            var productsDataSource = new kendo.data.DataSource({
                transport: {
                    read: {
                        url: "api/products",
                        dataType: "json",
                        contentType: 'application/json; charset=utf-8',
                        type: 'GET'
                    },
                    parameterMap: function (options) {
                        return kendo.stringify(options);
                    }
                },
                schema: {
                    data: "Data",
                    total: "Total",
                    model: {
                        fields: {
                            "Id": { type: "number" }, //تعیین نوع فیلد برای جستجوی پویا مهم است
                            "Name": { type: "string" },
                            "IsAvailable": { type: "boolean" },
                            "Price": { type: "number" }
                        }
                    }
                },
                error: function (e) {
                    alert(e.errorThrown);
                },
                pageSize: 10,
                sort: { field: "Id", dir: "desc" },
                serverPaging: true,
                serverFiltering: true,
                serverSorting: true
            });

            $("#report-grid").kendoGrid({
                dataSource: productsDataSource,
                autoBind: true,
                scrollable: false,
                pageable: true,
                sortable: true,
                filterable: true,
                reorderable: true,
                columnMenu: true,
                columns: [
                    { field: "Id", title: "شماره", width: "130px" },
                    { field: "Name", title: "نام محصول" },
                    {
                        field: "IsAvailable", title: "موجود است",
                        template: '<input type="checkbox" #= IsAvailable ? checked="checked" : "" # disabled="disabled" ></input>'
                    },
                    { field: "Price", title: "قیمت", format: "{0:c}" }
                ]
            });
        });
    </script>
- با تعاریف مقدماتی Kendo UI DataSource پیشتر آشنا شده‌ایم و قسمت read آن جهت دریافت اطلاعات از سمت سرور کاربرد دارد.
- در اینجا ذکر contentType الزامی است. زیرا ASP.NET Web API بر این اساس است که تصمیم می‌گیرد، خروجی را به صورت JSON ارائه دهد یا XML.
- با استفاده از parameterMap، سبب خواهیم شد تا پارامترهای ارسالی به سرور، با فرمت صحیحی تبدیل به JSON شده و بدون مشکل به سرور ارسال گردند.
- در قسمت schema باید نام فیلدهای موجود در DataSourceResult دقیقا مشخص شوند تا گرید بداند که data را باید از چه فیلدی استخراج کند و تعداد کل ردیف‌ها در کدام فیلد قرار گرفته‌است.
- نحوه‌ی تعریف model را نیز در اینجا ملاحظه می‌کنید. ذکر نوع فیلدها در اینجا بسیار مهم است و اگر قید نشوند، در حین جستجوی پویا به مشکل برخواهیم خورد. زیرا پیش فرض نوع تمام فیلدها string است و در این حالت نمی‌توان عدد 1 رشته‌ای را با یک فیلد از نوع int در سمت سرور مقایسه کرد.
- در اینجا serverPaging، serverFiltering و serverSorting نیز به true تنظیم شده‌اند. اگر این مقدار دهی‌ها صورت نگیرد، این اعمال در سمت کلاینت انجام خواهند شد.

پس از تعریف DataSource، تنها کافی است آن‌را به خاصیت dataSource یک kendoGrid نسبت دهیم.
- autoBind: true سبب می‌شود تا اطلاعات DataSource بدون نیاز به فراخوانی متد read آن به صورت خودکار دریافت شوند.
- با تنظیم scrollable: false، اعلام می‌کنیم که قرار است تمام رکوردها در معرض دید قرارگیرند و اسکرول پیدا نکنند.
- pageable: true صفحه بندی را فعال می‌کند. این مورد نیاز به تنظیم pageSize: 10 در قسمت DataSource نیز دارد.
- با sortable: true مرتب سازی ستون‌ها با کلیک بر روی سرستون‌ها فعال می‌گردد.
- filterable: true به معنای فعال شدن جستجوی خودکار بر روی فیلدها است. کتابخانه‌ی Kendo.DynamicLinq حاصل آن‌را در سمت سرور مدیریت می‌کند.
- reorderable: true سبب می‌شود تا کاربر بتواند محل قرارگیری ستون‌ها را تغییر دهد.
- ذکر columnMenu: true اختیاری است. اگر ذکر شود، امکان مخفی سازی انتخابی ستون‌ها نیز مسیر خواهد شد.
- در آخر ستون‌های گرید مشخص شده‌اند. با تعیین "{format: "{0:c سبب نمایش فیلدهای قیمت با سه رقم جدا کننده خواهیم شد. مقدار ریال آن از فایل فرهنگ جاری تنظیم شده دریافت می‌گردد. با استفاده از template تعریف شده نیز سبب نمایش فیلد bool به صورت یک checkbox خواهیم شد.


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید:
KendoUI03.zip

نظرات

  • حمیدرضا کبیری در ۱۳۹۳/۰۸/۱۶ ۱۹:۳
    آیا kendo UI کاملا از زبان فارسی پشتیبانی میکند ؟
    برای calender آن ، به تقویم شمی گزینه ای موجود هست ؟
    این گزینه با ورژن ۲۰۱۴٫۱٫۳۱۸  مطابقت دارد ، آیا با ورژنهای جدید مشکلی نخواهد داشت ؟
    • احمد رجبی در ۱۳۹۳/۰۸/۱۶ ۲۰:۱۵
      میتوانید با اضافه کردن این اسکریپت تمامی قسمتهای kendo را به زبان فارسی ترجمه کنید.
    • سعیدجلالی در ۱۳۹۳/۰۸/۱۷ ۹:۱۲
      امکان فارسی شدن تمام بخش‌ها وجود دارد.
      تقویم هم فارسی شده است در این سایت برای نسخه‌های جدیدتر هم باید دوتا فایل جاوا اسکریپت all و mvc رو خودتون تغییر بدهید (با توجه به الگوی انجام شده در فایل فارسی شده فوق)
      ولی برای تقویم زمانبدی scheduler من فارسی ندیده ام
  • سعیدجلالی در ۱۳۹۳/۰۸/۱۷ ۸:۴۴
    با تشکر از مطلب مفید شما من از wrapper mvc مجموعه kendo استفاده میکنم 
    توی مطالب شما در مورد استفاده از  Kendo.DynamicLinq    صحبت شد خواستم بدونم آیا وقتی از wrapper هم استفاده میکنیم استفاده از این پکیج لازم هست؟
    @(Html.Kendo().Grid(Model)
          .Name("gridKendo")
          .Columns(columns =>
          {
              columns.Bound(c => c.Name).Width(50);
              columns.Bound(c => c.Family).Width(100);
              columns.Bound(c => c.Tel);
          })
          .Pageable(pager => pager
              .Input(true)
              .Numeric(true)
              .Info(true)
              .PreviousNext(true)
              .Refresh(true)
              .PageSizes(true)
          )
          )

    چون من با استفاده از telerik profiler وقتی درخواست رو بررسی مکنم توی دستور sql چنین دستوری رو در انتها مشاهده میکنم:
    صفحه اول:
    SELECT *
    FROM (
        SELECT
        FROM table a
    )
    WHERE ROWNUM <= :TAKE

    صفحات بعد:
    SELECT *
    FROM (
        SELECT
            a.*,
            ROWNUM OA_ROWNUM
        FROM (
            FROM table a
        ) a
        WHERE ROWNUM <= :TAKE
    )
    WHERE OA_ROWNUM > :SKIP
    پایگاه داده اوراکل است.
    • وحید نصیری در ۱۳۹۳/۰۸/۱۷ ۹:۳۱
      مطلب فوق نه وابستگی خاصی به وب فرم‌ها دارد و نه ASP.NET MVC. ویو آن یک فایل HTML ساده‌است و سمت سرور آن فقط یک کنترلر ASP.NET Web API که با تمام مشتقات ASP.NET سازگار است. در این حالت یک نفر می‌تواند ASP.NET نگارش خودش را خلق کند؛ بدون اینکه نگران جزئیات وب فرم‌ها باشد یا ASP.NET MVC. ضمنا دانش جاوا اسکریپتی آن هم قابل انتقال است؛ چون اساسا Kendo UI برای فناوری سمت سرور خاصی طراحی نشده‌است و حالت اصل آن با PHP، Java و امثال آن هم کار می‌کند.
  • مهمان در ۱۳۹۳/۰۸/۱۷ ۱۳:۱۷
    در کنترلر این خط باعث بارگذاری تمامی داده‌ها می‌شود
    var list = ProductDataSource.LatestProducts;
    آیا راه حلی وجود دارد که دیتای به تعداد همان pagesize از پایگاه خوانده شود؟
    • وحید نصیری در ۱۳۹۳/۰۸/۱۷ ۱۳:۲۸
      - این فقط یک مثال هست و منبع داده‌ای صرفا جهت دموی ساده‌ی برنامه. فقط برای اینکه با یک کلیک بتوانید برنامه را اجرا کنید و نیازی به برپایی و تنظیم بانک اطلاعاتی و امثال آن نداشته باشد.
      - شما در کدها و کوئری‌های مثلا EF در اصل با یک سری IQueryable کار می‌کنید. همینجا باید متد الحاقی ToDataSourceResult را اعمال کنید تا نتیجه‌ی نهایی در حداقل بار تعداد رفت و برگشت و با کوئری مناسبی بر اساس پارامترهای دریافتی به صورت خودکار تولید شود. در انتهای کار بجای مثلا ToList بنویسید ToDataSourceResult.
  • مهمان در ۱۳۹۳/۰۸/۱۷ ۱۴:۴۱
    سلام من در ویو خودم نمیتونم اطلاعاتم رو تو kendo.grid  ببینم و برای من یک لیست استرینگ در ویو نمایش داده میشه و به این شکل در کنترلر و ویو کد نویسی کردم .
    public class EFController : Controller    {
            //
            // GET: /EF/
     
     
            public ActionResult AjaxConnected([DataSourceRequest] DataSourceRequest request )
            {
                using (var dbef=new dbTestEntities())
                {
                    IQueryable<Person> persons = dbef.People;
                    DataSourceResult result = persons.ToDataSourceResult(request);
                    return Json(result.Data,JsonRequestBehavior.AllowGet);
                }
            }
     
     
        }
    و ویو 
    @{    ViewBag.Title = "AjaxConnected";
    }
     
     
    <h2>AjaxConnected</h2>
    @(Html.Kendo().Grid<TelerikMvcApp2.Models.Person>(  )
          .Name("Grid")
          .DataSource(builder => builder
              .Ajax()
              .Read(operationBuilder => operationBuilder.Action("AjaxConnected", "EF"))
     
     
          )
          .Columns(factory =>
          {
              factory.Bound(person => person.personId);
              factory.Bound(person => person.Name);
              factory.Bound(person => person.LastName);
          })
          .Pageable()
          .Sortable())
    و یک لیست استرینگ بهم در عمل خروجی میده و از خود قالب kendogrid خبری نیست . من اطلاعات رو به طور json پاس میدم و ajaxi میگیرم. 
    حالا قبلش همچین خطلایی داشتم که به allowget ایراد میگرفت ولی در کل با JsonRequestBehavior.AllowGet  حل شد  و  حالا فقط یه لسیت بهم خروجی میده! و از ظاهر گرید خبری نیست. و اگر به جای json نوشته بشه view و با ویو return کنم ظاهر kendogrid رو دارم اما خروجی دارای مقداری نیست! 
    اینم خروجی استرینگ من :(
    [{"personId":1,"Name":"Amin","LastName":"Saadati"},  {"personId":2,"Name":"Fariba","LastName":"Ghochani  "},{"personId":4,"Name":"Milad","LastName":"Rahman  i"},{"personId":5,"Name":"rima","LastName":"rad"},  {"personId":6,"Name":"ali","LastName":"kiva"},{"pe  rsonId":7,"Name":"sahel","LastName":"abasi"},{"per  sonId":8,"Name":"medi","LastName":"ghaem"},{"perso  nId":9,"Name":"mino","LastName":"kafash"},{"person  Id":10,"Name":"behzad","LastName":"tizro"},{"perso  nId":11,"Name":"toti","LastName":"saadati"},{"pers  onId":12,"Name":"parinaz","LastName":"karami"},{"p  ersonId":13,"Name":"sadegh","LastName":"hojati"},{  "personId":14,"Name":"milad","LastName":"ebadipor"  },{"personId":15,"Name":"farid","LastName":"riazi"  },{"personId":16,"Name":"said","LastName":"abdoli"  },{"personId":17,"Name":"behzad","LastName":"ariaf  ar"},{"personId":18,"Name":"jamshid","LastName":"k  otahi"}]
    این سوال رو در چند سایت پرسیدم و به جوابی برایش نرسیدم. و نمیدونم ایراد کد‌های نوشته شده ام کجاست!
    متشکرم
    • وحید نصیری در ۱۳۹۳/۰۸/۱۷ ۱۵:۲
      - قصد پشتیبانی از wrapperهای آن‌را ندارم. لطفا خارج از موضوع سؤال نپرسید. اگر کسی دوست داشت در این زمینه مطلب منتشر کند، خوب. ولی من چنین قصدی ندارم.
      - عرض کردم اگر از wrapperها استفاده کنید، به علت عدم درک زیر ساخت اصلی Kendo UI، قادر به دیباگ کار نخواهید بود.
      - اگر متن را مطالعه کنید در قسمت «پیشنیاز تامین داده مخصوص Kendo UI Grid » دقیقا شکل نهایی خروجی JSON مورد نیاز ارائه شده‌است. این خروجی در سه فیلد data، total و aggregate قرار می‌گیرد. شما الان فقط قسمت data آن‌را بازگشت داده‌اید؛ بجای اصل و کل آن. نام این سه فیلد هم مهم نیست؛ اما هر چیزی که تعیین می‌شوند، باید در قسمت data source در خاصیت schema آن مانند مثالی که در مطلب جاری آمده (در قسمت «تامین داده و نمایش گرید »)، دقیقا مشخص شوند، تا Kendo UI بداند که اطلاعات مختلف را باید از چه فیلدهایی از JSON خروجی دریافت کند.
    • محسن جمشیدی در ۱۳۹۴/۰۴/۰۵ ۱۶:۱۹
      شما می‌بایست برای نمایش این View یک Action دیگر در EFControler تعریف کنید مثلا Index که خروجی آن این View می‌باشد. در این صورت وقتی View  نمایش داده شود گرید یک request به اکشن AjaxConnected میزند و داده‌ها را دریافت کرده و نمایش می‌دهد.
      در حقیقت شما یک Action که این ویو را نمایش دهد کم نوشته اید و این اکشنی که نوشته اید صرفا خوراک گرید را تامین می‌کند
  • وحید محمدطاهری در ۱۳۹۳/۱۰/۰۷ ۱۴:۲۴
    با سلام و خدا قوت

    آقای نصیری، model ای که باید در قسمت schema تعریف بشه چطوری میشه اونو دینامیک تولید کرد.
    من یک چنین حالتی رو ایجاد کردم ولی نمی‌دونم چطوری باید اسم ستونو براش مشخص کنم.
    public class Field
        {
            public string Type { get; set; }
        }

    public class Fields : System.Collections.ObjectModel.Collection<Field>
        {
            public System.Collections.IEnumerable ToList()
            {
                return System.Linq.Enumerable.ToList( System.Linq.Enumerable.Select( this ,
                                                                                     field => new { field.Type } ) );
            }
        }
    این قسمت اطلاعاتی است که برای ایجاد گرید باز گردانده می‌شود.
    return new InitializeInfo
                       {
                               ...
                               Model = GetModel()
                       };

    private Model GetModel ()
            {
                return new Model{ Fields = GetFields().ToList() };
            }
    متد GetColumns شامل 3 ستون می‌باشد که نوع، عنوان و سایر مشخصات رو توش تعریف کردم
    private Fields GetFields()
            {
                var fields = new Fields();
                foreach ( var column in GetColumns() )
                {
                    fields.Add( new Field { Type = column.DataType } );
                }
                return fields;
            }
    الان خروجی که تولید میشه اینجوریه
    "model": {
    
        "fields": [
            {
                "type": "string"
            },
            {
                "type": "string"
            },
            {
                "type": "datetime"
            }
        ]
    }

    ممنون میشم یه راهنمایی کنید.
    • وحید نصیری در ۱۳۹۳/۱۰/۰۷ ۱۴:۴۱
      - پویا هست و خروجی دسترسی هم گرفتید. زمانیکه تعریف می‌کنید:
      new Field { Type = column.DataType }
      یعنی در لیست نهایی، خاصیتی با نام ثابت Type و با مقدار متغیر column.DataType را تولید کن (نام خاصیت، مقدار ثابت نام خاصیت را در JSON نهایی تشکیل می‌دهد).
      + نیازی هم به این همه پیچیدگی نداشت. تمام کارهایی را که انجام دادید با تهیه خروجی ساده <List<Field از یک متد دلخواه، یکی هست و نیازی به anonymous type کار کردن نبود.
      - به همان کلاس فیلد، خواص دیگر مورد نیاز را اضافه کنید (عنوان و سایر مشخصات یک فیلد) و در نهایت لیست ساده <List<Field را بازگشت دهید. هر خاصیت کلاس Field، یک ستون گرید را تشکیل می‌دهد.
      - همچنین دقت داشته باشید اگر از روش مطلب جاری استفاده می‌کنید، اطلاعات ستون‌‌های نهایی باید در فیلد Data نهایی قرار گیرند (قسمت «پیشنیاز تامین داده مخصوص Kendo UI Grid» در بحث).
      • وحید محمدطاهری در ۱۳۹۳/۱۰/۰۷ ۱۵:۴۸
        با تشکر از پاسختون

        درسته این به صورت پویا تولید میشه ولی شکل model ای که شما در این مطلب توضیح دادید با این چیزی که کد من تولید می‌کنه فرق میکنه

        برای شما اول نام فیلد هست بعد نوع اون فیلد، در حالی که نحوه تولید داینامیک اینو نمی‌دونم چطوری باید باشه. 
        model: {
                            fields: {
                                "Id": { type: "number" }, //تعیین نوع فیلد برای جستجوی پویا مهم است
                                "Name": { type: "string" },
                                "IsAvailable": { type: "boolean" },
                                "Price": { type: "number" }
                            }
                        }
              }
        
        • وحید نصیری در ۱۳۹۳/۱۰/۰۷ ۱۶:۱۹
          باید از Dictionary استفاده کنید برای تعریف خواص پویا:
          public class Field
          {
            [JsonExtensionData]
            public Dictionary<string, object> Property { get; set; }
          }
           
          public class FieldType
          {
            public string Type { get; set; }
          }
          و بعد نحوه استفاده از آن به صورت زیر خواهد بود:
          var data = new
          {
            model = new
            {
             fields = new List<Field>
             {
              new Field
              {
                Property = new Dictionary<string, object>
                {
                 {"Id", new FieldType { Type = "number" }},
                 {"Name", new FieldType { Type = "string" }}
                }
              }
             }
            }
          };
          var dataJson = JsonConvert.SerializeObject(data, Formatting.Indented);
          با این خروجی:
          {
            "model": {
              "fields": [
                {
                  "Id": {
                    "Type": "number"
                  },
                  "Name": {
                    "Type": "string"
                  }
                }
              ]
            }
          }
          - اگر از Web API استفاده می‌کنید، ذکر سطر JsonConvert.SerializeObject ضروری نیست و به صورت توکار از JSON.NET‌ استفاده می‌کند.
          - اگر از ASP.NET MVC استفاده می‌کنید، نیاز است از آن کمک بگیرید. از این جهت که خاصیت JsonExtensionData سبب می‌شود تا نام ثابت خاصیت Property، از خروجی نهایی حذف شود و اعضای دیکشنری، جزئی از خاصیت‌های موجود شوند.
          - نکته‌ی «گرفتن خروجی CamelCase از JSON.NET» را هم باید مد نظر داشته باشید.
  • ژوپیتر در ۱۳۹۳/۱۱/۱۲ ۱۲:۴۷
    در صورتی بخواهیم dataSource مربوطه را از همان کنترلر MVC دریافت کنیم، با توجه به اینکه درخواست ارسال شده توسط گرید پارامتریک است، راهکار چیست؟
    • وحید نصیری در ۱۳۹۳/۱۱/۱۲ ۱۳:۳۰
      دو سری مثال رسمی kendo-examples-asp-net و kendo-examples-asp-net-mvc در مورد کار با گرید و یک سری از اجزای مهم آن وجود دارند. سری MVC آن دقیقا از Kendo.DynamicLinq مطرح شده در مطلب جاری، استفاده کرده‌است. برای مثال با این کنترلر و این View.  
  • جوادنب در ۱۳۹۳/۱۲/۲۲ ۱۵:۳۷
    سلام؛ خروجی دستور زیر در کد‌های من فقط Take هستش
    Request.Url.ParseQueryString().GetKey(0)
    اما همین کد در مثال شما خروجیش این هست:
    {"take":10,"skip":0,"page":1,"pageSize":10,"sort":[{"field":"Id","dir":"desc"}]}
    به نظر تون کجا کار را اشتباه عمل می‌کنم؟
    • وحید نصیری در ۱۳۹۳/۱۲/۲۲ ۱۶:۰
      به تعریف productsDataSource دقت کنید. در انتهای آن یک سری خاصیت مانند sort، اندازه‌ی صفحه، صفحه بندی سمت سرور و امثال آن مقدار دهی شده‌اند.
      • جوادنب در ۱۳۹۳/۱۲/۲۲ ۱۶:۳۶
        سلام؛ در حالت دیباگ خروجی کد من:
        -Request.Url{http://localhost:10912/Slider/ReadSlider1?take=10&skip=0&page=1&pageSize=10&sort[0][field]=Id&sort[0][dir]=desc}System.Uri
        ولی از شما:
        -Request.Url{http://localhost:27061/Home/GetProducts?{"take":10,"skip":0,"page":1,"pageSize":10,"sort":[{"field":"Id","dir":"desc"}]}}System.Uri
        داخل خروجی کد من قسمت Sort آن به صورت آرایه است!؟
        • وحید نصیری در ۱۳۹۳/۱۲/۲۲ ۱۹:۲۱
          - اسکریپت‌های کامل این مثال را در این پوشه می‌توانید مشاهده کنید. برای مثال اگر به فایل kendo.all.min.js مراجعه کنید، ابتدای آن ذکر شده‌است: Kendo UI v2014.2.1008 . به احتمال زیاد شما از یک نگارش قدیمی استفاده می‌کنید.
          نکته‌ای در مورد دریافت آخرین نگارش‌های Kendo UI  
  • شروین ایرانی در ۱۳۹۴/۰۲/۱۳ ۱۹:۴۷
    چطوری میشه فاصله‌ای که قبل ازبارگذاری گرید بار اول انجام میشه رو با یک Loading نمایش داد.
    • وحید نصیری در ۱۳۹۴/۰۲/۱۴ ۱:۱۷
      dataSource    : {
          // ....
          requestStart: function () {
              kendo.ui.progress($("#grid"), true);
          },
          requestEnd  : function () {
              kendo.ui.progress($("#grid"), false);
          }
      },
  • عباسپور در ۱۳۹۴/۰۴/۰۶ ۱۱:۲۱
    متد ToDataSourceResult  پارامترهای skip و take می‌پذیرد. اگر نخواهیم نتیجه را صفحه بندی کنیم، چگونه از این متد استفاده کنیم؟
    • وحید نصیری در ۱۳۹۴/۰۴/۰۶ ۱۱:۳۱
      در متن ذکر شده؛ سورس باز هست. در کدهای آن اگر take مساوی صفر باشد، این پارامترها پردازش نمی‌شوند.
  • علی یگانه مقدم در ۱۳۹۴/۰۷/۱۰ ۲۰:۱۶
    یه مشکلی که توی sort بهش برخوردم این هست که گاهی اوقات اطلاعات sort را ارسال نمی‌کنه و به خطا میخوره.
  • کیانی مقدم علیرضا در ۱۳۹۵/۰۹/۰۱ ۱۱:۵۰
    در صورتی که فیلدی خودش یک لیست بود مانند فیلد categories در ذیل به چه صورت فیلتر ارسال گردد ؟
     {"Data":[{"id":1,"Title":"گوشی موبایل تی پی-لینک مدل Neffos C5 دو سیم‌کارت","categories":[{"Name":"م وبایل","Id":1},
    مثلا می‌خواهیم فیلد id  فیلد categories معادل مقدار خاصی باشد.
  • وحید نصیری در ۱۳۹۶/۰۵/۱۰ ۱۸:۵۷
    یک نکته‌ی تکمیلی

    معادل قطعه کد ذیل که مخصوص ASP.NET MVC 5x است:
    var request = JsonConvert.DeserializeObject<DataSourceRequest>(
                    requestMessage.RequestUri.ParseQueryString().GetKey(0)
                );
    در ASP.NET Core به صورت زیر می‌باشد (روش استخراج کل کوئری استرینگ به صورت خام و تبدیل آن به یک رشته‌ی JSON معتبر برای Deserialize شدن):
    var rawQueryString = this.HttpContext.Request.QueryString.ToString();
    // PM> Install-Package Microsoft.AspNetCore.WebUtilities
    var rawQueryStringKeyValue = QueryHelpers.ParseQuery(rawQueryString).FirstOrDefault();
    var dataString = Uri.UnescapeDataString(rawQueryStringKeyValue.Key);
    
    var request = JsonConvert.DeserializeObject<DataSourceRequest>(dataString);
    • محمد دلیری در ۱۳۹۶/۰۶/۱۴ ۱۲:۴۲
      با سلام لطفا این رو به گیت هاب همین مجموعه اضافه می‌کنید
      • وحید نصیری در ۱۳۹۶/۰۶/۱۷ ۱۵:۳۳
        جهت اطلاع

        تمام مثال‌های این سری به ASP.NET Core منتقل شدند. کدهای نهایی آن‌ها را از مخزن کد KendoUI.Core.Samples می‌توانید دریافت کنید.
        این مثال‌ها بر اساس Kendo UI Professional R2 2017 SP1 تهیه شده‌اند (فایل‌های مورد نیاز هم پیوست شده‌اند).
        • محمد دلیری در ۱۳۹۶/۰۶/۱۹ ۱۱:۲۴
          برای تغییر نوع کتابخانه ای Kendo.DynamicLinq از netstandard2  به netcoreapp1.1 چه افزونه‌های باید نصب کرد؟ 
          تا جای که معلومه System.Runtime.Serialization مخصوص netstandard2  و هنوز به  netcoreapp1.1  نیومده!
          آیا این کتابخانه Kendo.DynamicLinqCore جایگزین کاملی برای این کتاب خانه Kendo.DynamicLinq   میباشد؟
          مشکل من این جاهست پروژه‌ی بنده Asp.net core 1.1 هست و نمی‌شود از کتابخانه Kendo.DynamicLinq  استفاده کرد
          • وحید نصیری در ۱۳۹۶/۰۶/۱۹ ۱۳:۱۲
             پشتیبانی از netstandard1.6 هم اضافه شد؛ با این تغییرات
        • محمد دلیری در ۱۳۹۷/۰۵/۰۱ ۱۴:۳۶
          در نسخه‌ی جدید Angular 6 Kendo ui برای صفحه بندی اطلاعات از همین روش Kendo.DynamicLinq.Core  میشه استفاده کرد؟
  • مهدی ملائیان در ۱۳۹۶/۱۱/۲۵ ۱۶:۴۰
    با تشکر .اگر بخواهیم ، اطلاعات جستجو رو از طریق کنترل هایی که خودمون در صفحه قرار دادیم ارسال کنیم به چه شکلی باید عمل کنیم؟
    • کیانی مقدم علیرضا در ۱۳۹۶/۱۱/۲۵ ۱۷:۶
        var gridDataSource = $("#grid").data("kendoGrid").dataSource;    
        var gridListFilter = { filters: [] };
        gridListFilter.logic = "and";
        gridListFilter.filters.push({ field: "Prices", operator: "lte", value: maxVal });
        gridDataSource.filter(gridListFilter);
        gridDataSource.read();
      تا جایی که در ذهن دارم متد filter خواندن داده‌ها روهم انجام میده و نیازی به خط آخر نیست ، ولی بازم تست کنید .
      • مهدی ملائیان در ۱۳۹۶/۱۱/۲۶ ۰:۴۵
        با تشکر . من به دنبال روشی هستم که از کندو برای صفحه بندی و مرتب سازی استفاده کنم و از طریق پارامتر‌های اکشن متد و اعمال آن بر لیست دریافتی از بانک اطلاعاتی جستجو انجام شود.اینکه ارزش‌های درون کنترل‌های مربوط به فرم جستجو رو  چگونه با پارامتر فیلتر کندو در اکشن جایگزین کنم برای من سوال هست
        • محسن خان در ۱۳۹۶/۱۱/۲۶ ۱:۱۸
          در مثالی که زدند، چون این datasource از نوع remote هست، دستورات ()grid.dataSource.read(); grid.refresh مجددا منبع داده گرید را بر اساس اطلاعات دریافتی از سرور پر می‌کنند (re-bind).
          function customSearch() {
               var filters = [];
               var grid = $("#grid").data("kendoGrid");
                 
               filters.push({ field: "f1", operator: "eq", value: 1 });
               filters.push({ field: "f2", operator: "eq", value: "xyz" });
                 
               grid.dataSource.filter({
                    logic: "and",
                    filters: filters
                  });
              grid.dataSource.read();
              grid.refresh();
          }
          در این مثال فقط قسمت value را باید از رابط کاربری که طراحی کردید، دریافت و مقدار دهی کنید.
          • سامان احمدی در ۱۳۹۸/۰۱/۲۰ ۱۱:۱۶
            ممنون روش شما کار کرد.
            فقط مشکل اینجاست که وقتی از این روش استفاده می‌کنم آیکون لودینگ که اول صفحه میاره دیگه نمایش داده نمیشه. چطور میشه آیکون Loading رو با فراخوانی متود refresh نمایش داد ؟
            • محسن خان در ۱۳۹۸/۰۱/۲۰ ۱۳:۴
              var initialLoad = true;
              
              var ds = new kendo.data.DataSource({
              //...
              requestStart: function () {
                if (initialLoad)
                   kendo.ui.progress($("#myGrid"), true);
              },
              requestEnd: function () {
                if(initialLoad)
                    kendo.ui.progress($("#myGrid"), false);
                initialLoad = false;
              },
              //...
              • سامان احمدی در ۱۳۹۸/۰۱/۲۰ ۱۵:۰
                ممنون.
                کلا متود refresh رو برداشتم، بدون فراخوانی متود refresh هم کار می‌کنه، تنها نیاز است که متود read فراخوانی شه، همچنین  آیکون لودینگ هم نمایش داده میشه و نیاز به تنظیمات اضافه ایی ندارد.
      • مهدی ملائیان در ۱۳۹۶/۱۲/۰۸ ۱۱:۲۴
        من با توجه به پاسخ شما و همچینن این توضیح عمل کردم ، زمانی که تنها یک فیلد رو به بخش فیلترها اضافه می‌کنم ، مشکلی نیست و به درستی جستجو انجام می‌شود اما زمانی که یک فیلد دیگر رو اضافه می‌کنم با خطای internal server error مواجه می‌شوم:
        وضعیت کوئری استرینگ در زمانی که خطا صادر می‌شود :
        {"take":10,"skip":0,"page":1,"pageSize":10,"sort":[{"field":"Id","dir":"desc"}],"filter":{"logic":"or","filters":[{"field":"Name","operator":"contains","value":"ا"},{"field":"Description","operator":"contains","value":"ا"}]}}:
        • عثمان رحیمی در ۱۳۹۶/۱۲/۰۸ ۱۵:۰
          خوب شما یک بار اکشن مربوطه را دیباگ کنید و ببینید چه خطایی میدهد چون طبق چیزی که نوشته ای خطا از کلاینت شما نیست .فعلا جهت تست هم که شده یک try...catch اضافه کنید 
  • سامان احمدی در ۱۳۹۸/۰۱/۱۹ ۱۳:۲۳
    یک نکته درباره‌ی فیلترینگ اولیه (پیشفرض)
    اگر خواستید که به صورت پیشفرض (قرار دادن یک یا چند فیلتر به صورت پیش فرض) گرید بر اساس یک Enum فیلتر شود مقدار Enum ارسالی از سمت سرور به رشته تبدیل شود و مقدار ایندکس آن ارسال نشود
    این Enum
    public enum RequestStatus : byte
    {
        Checking,
        Accepted,
        Rejected
    }

    var query = _context.Products.Select(x => new { x.Status.ToString(), ... });
    بعد در سمت کلاینت و filed ها
    "Status": { type: "string" },
    و فیلتر کردن اولیه
    filter: [
        { field: "Status", operator: "eq", value: "Checking" },
    ],
    

    و بعد قسمت Column ها
    field: "Status",
        values: [
        { text: "در حال بررسی", value: "Checking" },
        { text: "تایید شده", value: "Accepted" },
        { text: "رد شده", value: "Rejected" },
    ],
    title: 'وضعیت'

  • کیان سبزی در ۱۳۹۸/۰۶/۰۳ ۲۳:۱۸
    برای این حالات آیا میشه فانکشن‌های kendo رو override کرد یا مثلا بهشون اضافه کرد function سفارشی :
    $("#report-grid").data("kendoGrid").options.editable.mode = "popup";
    $("#report-grid").data("kendoGrid").options.editable.mode = "inline";
    $("#report-grid").data("kendoGrid").options.editable.mode = "incell";
    در نظر بگیرید که ما در پروژه(دات نت کور) مثلا به جای اینکه حالت ویرایش یا حذف و اضافه کردن(عملیات CRUD بدون خواندن) داخل خود کندو انجام بشه، بره در یک صفحه جدید یا بهتر بگم view متناظر با خود اون عملیات (Update - Delete - Create)؟ یا باید این رو با خود دات نت مدیریتش کرد؟ بخاطر اینکه تعداد پراپرتی‌های Model ما زیاد هستش و بصورت inline یا حتی popup فرم بهم ریخته میشه.
    • وحید نصیری در ۱۳۹۸/۰۶/۰۴ ۱:۲۲
      // https://docs.telerik.com/kendo-ui/api/javascript/ui/grid/events/edit
      $("#grid").data("kendoGrid").bind("edit", function(e){
          if(e.model.isNew()) {
             //create
          } else {
             //edit
          }
      });