Kendo UI - JSON 响应 - 使用带有服务器分组和服务器聚合的远程数据源的网格

Posted

技术标签:

【中文标题】Kendo UI - JSON 响应 - 使用带有服务器分组和服务器聚合的远程数据源的网格【英文标题】:Kendo UI - JSON Response for - Grid using Remote Data Source W/Server Grouping & Server Aggregates 【发布时间】:2016-05-02 13:57:56 【问题描述】:

我有一个项目,我在其中使用 KendoUI Grid,使用服务器而不是本地获取数据。

我不确定我的服务器应该提供什么 JSON 响应才能使分组工作。我的目标是当用户将一列拖到分组标题时,我知道要返回什么样的 JSON 响应,以便 GRID 按该列和可能添加到该标题的任何其他列进行分组。

鉴于上图,我如何创建一个 JSON 响应来实现它(因此它显示了它应该分组的内容)? 我知道我必须自己在服务器上执行此操作,但不确定 JSON 需要如何格式化。此外,如果我想在创建组时在组旁边显示一个“计数”字段,以便我知道每个组中有多少项目(我认为这是汇总?)

我当前的网格代码如下所示:

<div id="grid" style="height:100%;"></div>
<script>
    $(window).on("resize", function() 
      kendo.resize($("#grid"));
    );

    var crudServiceBaseUrl = "/api",
    dataSource = new kendo.data.DataSource(
        transport: 
            read:  
                url: crudServiceBaseUrl + "/companies",
                dataType: "json",
                type: "POST"
            ,
            update: 
                url: crudServiceBaseUrl + "/companies/update",
                dataType: "json",
                type: "POST"
            ,
            destroy: 
                url: crudServiceBaseUrl + "/companies/destroy",
                dataType: "json",
                type: "POST"
            ,
            create: 
                url: crudServiceBaseUrl + "/companies/create",
                dataType: "json",
                type: "POST"
            ,
            parameterMap: function(options, operation) 
                if (operation !== "read" && options.models) 
                    return models: kendo.stringify(options.models);
                
            
        ,
        error: function (e) 
            /* the e event argument will represent the following object:
            
                errorThrown: "custom error",
                errors: ["foo", "bar"]
                sender: ... the Kendo UI DataSource instance ...
                status: "customerror"
                xhr: null
            
            */
            //alert("Status: " + e.status + "; Error message: " + e.errorThrown);
            console.log("Status: " + e.status + "; Error message: " + e.errorThrown);
            console.log("Errors: " + e.errors.join("; "));
        ,
        autoSync: false,
        serverPaging: true,
        serverFiltering: true,
        serverSorting: true,
        serverGrouping: true,
        serverAggregates: true,
        pageSize: 20,
        columnResizeHandleWidth: 6,
        schema: 
            total: "itemCount",
            data: "items",
            groups: "groups",
            aggregates: "aggregates",
            group: 
                field: "phone", aggregates: [ field: "phone", aggregate: "count" ]
            ,
            model: 
                id: "id",
                fields: 
                    id:  editable: false, nullable: true ,
                    name:  validation:  required: true  ,
                    phone:  
                        type: "string",
                        validation:  
                            required: true,
                            phonerule: function(e)
                                if (e.is("[data-phonerule-msg]"))
                                
                                    var input  = e.data('kendoMaskedTextBox');
                                    //If we reached the end of input then it will return -1 which means true, validation passed
                                    //Otherwise it won't === -1 and return false meaning all the characters were not entered.
                                    return input.value().indexOf(input.options.promptChar) === -1;
                                
                                return true; //return true for anything else that is not data-phonerule-msg
                             
                         
                    ,
                    email:  type: "string", validation:  required: true, email:true  
                
            
        
    );

    $("#grid").kendoGrid(
        dataSource: dataSource,
        groupable: true,
        sortable: 
            mode: "multiple",
            allowUnsort: true
        ,
        selectable: "multiple cell",
        allowCopy:true,
        toolbar: ["create","excel"],
        excel: 
            fileName: "Kendo UI Grid Export.xlsx",
            //Below is only used as fallback for old browsers without support
            proxyURL: "//demos.telerik.com/kendo-ui/service/export",
            filterable: true
        ,
        pageable: 
            refresh: true,
            pageSizes: true,
            buttonCount: 5
        ,
        reorderable: true,
        resizable: true,
        columnMenu: true,
        filterable: true,
        editable: "popup",
        mobile: true,
        columns: [
             
                field: "name",
                title: "Company Name",
                aggregates: ["count"],
                groupFooterTemplate: "Count: #=count#"
            ,
             
                field: "phone",
                title: "Phone",
                editor: function(container, options)
                    //pattern="[(][0-9]3[)] [0-9]3-[0-9]4"
                    var input = $('<input type="tel" data-phonerule-msg="Invalid Phone Number!" class="k-textbox"  required />');
                    input.attr("name", options.field);
                    input.kendoMaskedTextBox(
                        mask: "(999) 000-0000"
                    );
                    input.appendTo(container);
                ,
                aggregates: ["count"],
                groupFooterTemplate: "Count: #=count#"
            ,
             
                field: "email",
                title: "Email",
                editor: function(container, options)
                    var input = $('<input type="email" data-email-msg="Invalid email!" class="k-textbox" required/>');
                    input.attr("name", options.field);
                    input.appendTo(container);
                ,
                aggregates: ["count"],
                groupFooterTemplate: "Count: #=count#"
            ,
             
                command: ["edit", "destroy"],
                title: "Operations",
                width: "240px"
            
        ],
    );
</script>

我当前通过 Symfony 3.0 生成演示 javascript 的代码如下。

DefaultController.php

/**
     * @Route("/api/companies", name="api_companies_read")
     */
public function apiCompaniesReadAction(Request $request)
    
        $data["itemCount"] = "7";

        // $tdata["field"] = "";
//      $tdata["value"] = "";
//      $tdata["items"] = "hey";
//      $data["groups"][] = $tdata;

        $tdata["id"]    = "1";
        $tdata["name"] = "Joe";
        $tdata["phone"] = "(714)475-8651";
        $tdata["email"] = "Joe@whatever.com";

        $data["items"][] = $tdata;

        $tdata["id"]    = "2";
        $tdata["name"] = "Rachel";
        $tdata["phone"] = "(563)812-4184";
        $tdata["email"] = "rachel@yahoo.com";

        $data["items"][] = $tdata;

        $tdata["id"]    = "3";
        $tdata["name"] = "John";
        $tdata["phone"] = "(563)812-4184";
        $tdata["email"] = "John@yahoo.com";

        $data["items"][] = $tdata;

        $tdata["id"]    = "4";
        $tdata["name"] = "Richard";
        $tdata["phone"] = "(563)812-4184";
        $tdata["email"] = "John@yahoo.com";

        $data["items"][] = $tdata;

        $tdata["id"]    = "5";
        $tdata["name"] = "Sister";
        $tdata["phone"] = "(563)812-4184";
        $tdata["email"] = "John@yahoo.com";

        $data["items"][] = $tdata;

        $tdata["id"]    = "6";
        $tdata["name"] = "Brother";
        $tdata["phone"] = "(563)812-4184";
        $tdata["email"] = "Brother@yahoo.com";

        $data["items"][] = $tdata;

        $tdata["id"]    = "7";
        $tdata["name"] = "Sibling";
        $tdata["phone"] = "(563)812-4184";
        $tdata["email"] = "Sibling@yahoo.com";

        $data["items"][] = $tdata;

//      schema: 
//          total: "total",
//          model: 
//              id: "CompanyID",
//              fields: 
//                  CompanyID:  editable: false, nullable: true ,
//                  Name:  validation:  required: true  ,
//                  Phone:  type: "string" ,
//                  Email:  type: "string" 
//              
//          
//      

        // replace this example code with whatever you need
        return new JsonResponse($data);

    

它创建的当前 JSON 如下所示。

JSON

"itemCount":"7","items":["id":"1","name":"Joe","phone":"(714)475-8651","email":"Joe@whatever.com","id":"2","name":"Rachel","phone":"(563)812-4184","email":"rachel@yahoo.com","id":"3","name":"John","phone":"(563)812-4184","email":"John@yahoo.com","id":"4","name":"Richard","phone":"(563)812-4184","email":"Richard@yahoo.com","id":"5","name":"Sister","phone":"(563)812-4184","email":"Sister@yahoo.com","id":"6","name":"Brother","phone":"(563)812-4184","email":"Brother@yahoo.com","id":"7","name":"Sibling","phone":"(563)812-4184","email":"Sibling@yahoo.com"]

我发现一篇文章 here 看起来很接近我想要的,只是很难理解。 This 也可能有帮助。

我创建了JSFiddle 可以玩,只需要提供有效数据。

更新

我让服务器分页工作!第一个主要变化是数据源的传输,您必须更改 parameterMap 以发送 json,以便您可以访问它试图告诉您的服务器进行更改的内容。

dataSource = new kendo.data.DataSource(
        transport: 
            read:  
                url: crudServiceBaseUrl + "/companies",
                dataType: "json",
                type: "POST"
            ,
            update: 
                url: crudServiceBaseUrl + "/companies/update",
                dataType: "json",
                type: "POST"
            ,
            destroy: 
                url: crudServiceBaseUrl + "/companies/destroy",
                dataType: "json",
                type: "POST"
            ,
            create: 
                url: crudServiceBaseUrl + "/companies/create",
                dataType: "json",
                type: "POST"
            ,
            parameterMap: function(options, operation) 
                return kendo.stringify(options);
            
        ,

我的数据源如上所示,但您可以根据自己的需要进行调整。接下来是我的php文件。

PHP /公司

/**
     * @Route("/api/companies", name="api_companies_read")
     */
    public function apiCompaniesReadAction(Request $request)
    

        $request_body = file_get_contents('php://input');
        $json = json_decode($request_body);

        //Based on the JSON Payload response adjust the search in the database
        if($json)
            $page       = $json->page;
            $pageSize   = $json->pageSize;
            $skip       = $json->skip;
            $take       = $json->take;
        else
            $page = 1;
            $pageSize = 20;
            $skip = 1;
            $take = 1;
        

        $repository = $this->getDoctrine()->getRepository('AppBundle:Company');

        /*
        findBy(
            array        $criteria,
            array        $orderBy  = null, 
            integer|null $limit    = null,
            integer|null $offset   = null
        )
        */

        $company_total = $repository->findAll();
        $company_records = $repository->findBy(array(),array(),$pageSize,($page-1)*$pageSize);

        $data["total"] = count($company_total);

        foreach($company_records as $company)
            $temp["id"]     = $company->getId();
            $temp["name"]   = $company->getName();
            $temp["phone"]  = $company->getPhone();
            $temp["email"]  = $company->getEmail();
            $data["data"][] = $temp;
        

        //converts data to JSON
        return new JsonResponse($data);

    

请记住,上面是一个 Symfony 3.0 php 实现,使用注解来设置路由。该代码中的重要部分是。

$request_body = file_get_contents('php://input');
        $json = json_decode($request_body);

        //Based on the JSON Payload response adjust the search in the database
        if($json)
            $page       = $json->page;
            $pageSize   = $json->pageSize;
            $skip       = $json->skip;
            $take       = $json->take;
        else
            $page = 1;
            $pageSize = 20;
            $skip = 1;
            $take = 1;
        

file_get_contents('php://input'); 获取 KendoUI 控件发回的 javascript 对象。

更新 #3 让服务器排序正常工作!

以下是使用 Symfony 3.0 和 Doctrine 进行服务器排序的示例实现!

PHP

/**
     * @Route("/api/companies", name="api_companies_read")
     */
    public function apiCompaniesReadAction(Request $request)
    
        $request_body = file_get_contents('php://input');
        $json = json_decode($request_body);

        //print_r($json);

        //default parameters in case of error.
        $page = 1;
        $pageSize = 20;
        $skip = 1;
        $take = 1;
        $sort = array();

        //Based on the JSON Payload response adjust the search in the database
        if(isset($json))
            $page       = $json->page;
            $pageSize   = $json->pageSize;
            $skip       = $json->skip;
            $take       = $json->take;

            if(isset($json->sort))
                //"sort":["field":"name","dir":"asc"]:

                foreach($json->sort as $sortObj)
                    $sort[$sortObj->field] = $sortObj->dir;
                
            

        

        $repository = $this->getDoctrine()->getRepository('AppBundle:Company');

        /*
        findBy(
            array        $criteria,
            array        $orderBy  = null, 
            integer|null $limit    = null,
            integer|null $offset   = null
        )
        */

        $company_total = $repository->findAll();
        $company_records = $repository->findBy(array(),$sort,$pageSize,($page-1)*$pageSize);

        $data["total"] = count($company_total);

        foreach($company_records as $company)
            $temp["id"]     = $company->getId();
            $temp["name"]   = $company->getName();
            $temp["phone"]  = $company->getPhone();
            $temp["email"]  = $company->getEmail();
            $data["data"][] = $temp;
        

        //converts data to JSON
        return new JsonResponse($data);

    

【问题讨论】:

有人有什么想法吗? 【参考方案1】:

第一个链接是正确的。我必须将数据从网格导出为 pdf 和 excel,以遵守网格分组。我终于弄清楚了语义。网格具有列和数据对象,您可以通过网格调用获得它们。数据对象保存分组信息。这里是处理操作。

简而言之,您必须递归地迭代数据对象才能确定分组。对于数据中的每个元素,您需要检查 data[x].value 是否存在。如果它存在,那么它是一个带有子数据的组对象。如果它不存在,那么您使用普通的 data[row][column].field 来获取子数据。这里的技巧是为每个 dat[0].value 有数据的点调用一个递归函数,最后在展开时处理 data[row][column]。

下面是一个 js 函数,您可以使用它来检查 Grid 的 json dta。我会在您的网格中添加一两个组,并查看应用/不应用分组的数据之间的差异,这应该是您需要添加到数据中的内容....我认为您几乎将数据添加为 data=[ [],[]]除非是组,否则就是data=[value='groupName',[],[]]。

function showData(gridName) 
        var grid = $('#' + gridName).data('kendoGrid');
        var data = grid.dataSource.data().toJSON();
        console.log(data);
    

【讨论】:

这很复杂,我的特定项目使用 php,但我可以尝试翻译,不幸的是,我不是 c# 专家,所以很难弄清楚那里发生了什么。我想我仍然对 JSON 响应的样子感到困惑,您能否提供代码生成的示例? 感谢您尝试解决这个问题,这非常复杂。 对不起,我以为你在读数据。我添加了一个 js 函数来检查分组时数据的样子。这是无证的,可能会改变,我会谨慎行事。 只是好奇,应用分组时 php 包装器不应该返回所需的格式吗? Yes & No,我调查了它,但对如何使用 PHP Wrappers 来做到这一点感到非常困惑。我有点困惑的原因是因为我认为包装器依赖于他们自己的模型定义,但我的模型是在学说中定义的。不过,我会使用您制作的那个功能,这肯定会帮助我更好地理解格式。如果可能的话,我会再次研究 PHP 包装器,看看我是否能找到一种方法让它与 Doctrine Entities 一起工作。

以上是关于Kendo UI - JSON 响应 - 使用带有服务器分组和服务器聚合的远程数据源的网格的主要内容,如果未能解决你的问题,请参考以下文章

ASP MVC TreeView的Kendo UI:代表Tree显示Json响应

带有自定义JSON Web服务器的Kendo UI Grid - “未捕获的TypeError:this.replace不是函数”

Kendo UI Treeview 和 JSON

如何使用嵌套 Json 填充 Kendo UI 网格?

带有backbonejs的剑道UI

在 Kendo UI Scheduler 上响应更改视图