分页树列表或可折叠分层网格

Paged TreeList or collapsible hierarchical Grid

我正在考虑使用 Kendo UI 网格。而且它看起来功能非常齐全。然而它有一个致命的缺陷。他们的 Grid / Hierarchy 只是嵌套的网格,并不是真正的层次结构。绝对不是递归或层次结构。

幸运的是,他们有一个 TreeList 网格可以做到这一点。但它的功能远不如普通的 Grid。它实际上缺乏对分页的支持,这使得它完全无法使用:(

我在某处读到他们没有实现分页,因为当展开父项时,页面将包含一个额外的项目并且可能超过页面大小。如果你问我的话,这个论点相当薄弱……显然它需要按根分页。

真的很简单。这是一个使用 OData

的示例

OData 示例

GET ~/Products?$filter=ParentId eq null&$expand=Children&$top={pagesize}&$skip={pagenumber}&$count=true

结果

{
    "@odata.count": 33,
    "value": [
        {
            "Id": 1,
            "ParentId": null,
            ...
            "Children": [
                {
                    "Id": 2,
                    "ParentId": 1,
                }
            ]
        },
        ...
    ]
}

完美!换句话说,它不是后端的限制。虽然我可以看到节目过滤可能会变得棘手。

回到主题。我正在寻找有关如何为 TreeList 实现分页或如何在普通网格中获取可折叠行(递归层次结构样式)的建议。实际上后者会更可取,因为它的功能更完整。

只要您的结构看起来相同,因为父对象具有子对象数组,那么您可以在子网格上使用与父网格相同的 detailInit 函数,从而本质上为您提供递归层次网格,在每个网格级别进行分页过滤。

这是我做的一个例子。 http://jsbin.com/zosiji/1/edit?html,js,output 我认为就是这样。 父对象有一个 id 和名称 属性,以及一个子对象数组,它们一直遵循相同的结构。

在这里找到了一个例子:http://www.telerik.com/forums/does-treelist-support-pagination#5VlH4IMBXEuh819pYvyEvQ

查看:

<kendo-treelist k-options="treeListOptions"></kendo-treelist>
<kendo-pager k-options="pagerOptions"></kendo-pager>

控制器逻辑:

// default filtering and sorting
var defaultFilter = { field: "ParentId", operator: "eq", value: null };
var defaultSort = { field: "Name", dir: "asc" };

var myODataSource = new kendo.data.DataSource({
    type: "odata-v4",
    transport: {
        read: {
            url: "/odata/Products",
            data: {
                "$expand": "Children($levels=max)"
            }
        }
    },
    pageSize: 15,
    serverPaging: true,
    serverSorting: true,
    serverFiltering: true,
    filter: defaultFilter,
    sort: defaultSort,
    schema: {
        parse: function(response) {
            if (myODataSource.transport.options.read.data.$expand == "Parent($levels=max)") {
                // if "$expand=Parent($levels=max)" then the hierarchy will be reversed Children -> Parent
                // thus we need to flatten Parents
                var ary = _.flattenHierarchy(response.value, 'Parent');
                // and remove duplicate parents coming from different tree branches
                response.value = _.uniq(ary);
            } else {
                // if "$expand=Children($levels=max)" then the hierarchy will be as expected Parent -> Children
                // thus we need to flatten Children
                response.value = _.flattenHierarchy(response.value, 'Children');
            }

            return response;
        }
    },
    change: function(e) {
        treeListDataSource.read();
    }
});

// filter hack!
// http://www.telerik.com/forums/any-filtering-event#--cNXsvF5U6zinsTsyL4eg
var originalFilterFn = kendo.data.TreeListDataSource.fn.filter;
kendo.data.TreeListDataSource.fn.filter = function (e) {
    if (arguments.length > 0) {
        if (e === null) {
            // if e is null, then the filter is cleared. So we need to filter by roots to get the normal tree
            myODataSource.transport.options.read.data.$expand = "Children($levels=max)";
            myODataSource.filter(defaultFilter);
        } else {
            // else we're filtering and the result nodes need to include parents to maintain the tree hierarchy
            myODataSource.transport.options.read.data.$expand = "Parent($levels=max)";
            myODataSource.filter(e);
        }
    }

    return originalFilterFn.apply(this, arguments);
};

// sort hack!
var originalSortFn = kendo.data.TreeListDataSource.fn.sort;
kendo.data.TreeListDataSource.fn.sort = function (e) {
    if (arguments.length > 0) {
        myODataSource.sort(e);
    }

    return originalSortFn.apply(this, arguments);
};

var treeListDataSource = new kendo.data.TreeListDataSource({
    transport: {
        read: function (options) {
            var data = myODataSource.data().toJSON();
            options.success(data);
        }
    },
    sort: defaultSort,
    schema: {
        model: {
            id: "Id",
            fields: {
                parentId: { field: "ParentId", type: "number", nullable: true },
                Id: { field: "Id", type: "number" }
            },
            expanded: true
        }
    }
});

$scope.treeListOptions = {
    autoBind: false,
    dataSource: treeListDataSource,
    filterable: true, //{ mode: "row"}, not supported (yet) by treelist
    sortable: true,
    resizable: true,
    reorderable: true,
    columns: [
        { field: "Name" },
        { field: "Description" }
    ]
};

$scope.pagerOptions = {
    autoBind: false,
    dataSource: myODataSource,
    info: true,
    pageSizes: [2, 3, 4, 5, 6],
    refresh: true,
};

myODataSource.read();

下划线函数

_.mixin({
    flattenHierarchy: function self(objAry, childPropertyName) {
        // default values
        childPropertyName = typeof childPropertyName !== 'undefined' ? childPropertyName : 'children';

        var result = [];
        _.each(objAry, function(obj) {
            // the object it self without children
            var strippedObj = _.omit(obj, childPropertyName);
            result.push(strippedObj);

            if (obj.hasOwnProperty(childPropertyName) && obj[childPropertyName] !== null) {
                // child object(s)
                var children = obj[childPropertyName];
                if (!_.isArray(children))
                    children = [children];

                result.pushArray(self(children, childPropertyName));
            }
        });

        return result;
    }
});