分页树列表或可折叠分层网格
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;
}
});
我正在考虑使用 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;
}
});