如何修改Kendo Grid代码实现类Excel的动态列筛选功能
Kendo Grid实现DeptName列的动态筛选(类Excel效果)
当前现状
我们的Kendo Grid能基于各列筛选条件展示过滤后的结果,但列筛选下拉列表始终显示该列的全部原始选项,不会排除已经被其他列筛选过滤掉的项。我们需要实现类似Excel的动态筛选——筛选下拉列表的选项会随其他列已应用的筛选自动更新,只保留当前网格中存在的唯一值。
技术问题
如何修改Kendo代码,让DeptName列的筛选下拉仅显示当前网格数据中的唯一值,同时自动适配其他列已应用的所有筛选条件?
现有代码
function BindGrid() { var grid = new kendo.data.DataSource({ transport: { read: function (options) { $.ajax({ type: "GET", url: "Spartan2PHQDataGrid.aspx/GetPHQGridData", data: "", contentType: "application/json; charset=utf-8", dataType: "json", success: function (response) { options.success(JSON.parse(response.d)); } }); } }, schema: { model: { fields: { //Format: { editable: true}, //AdType: { editable: true}, //ShortDescription: { editable: true}, //PrintQty: { editable: true}, } } }, pageSize: 10, }); kGrid = $("#Grid").kendoGrid({ toolbar: "<input type='button' class='k-button' onclick='SaveClicked(this)' value='Save'/> <input type='button' class='k-button' onclick='refreshGrid()' value='Cancel'/>", dataSource: grid, scrollable: true, filterable: { extra: false, operators: { string: { startswith: "Starts with", eq: "Is equal to", neq: "Is not equal to" } } }, sortable: true, resizable: true, reorderable: true, pageable: { alwaysVisible: true, pageSizes: [10, 20, 50, 100] }, editable: true, columns: [ { field: "Id", title: "Id", hidden: true, headerAttributes: { "class": "grid-header" } }, { title: "select_all", width: "70px", headerTemplate: "<input type='checkbox' id='header-chb' onclick='checkAllClick($(this))' class='k-checkbox header-checkbox'>", headerAttributes: { "class": "grid-header check-header" }, attributes: { "class": "checkbox-cell", }, template: "<input type=\"checkbox\" class=\"row-checkbox\" />", }, { field: "DeptName", title: "Dept", width: "100px", editable: true, headerAttributes: { "class": "grid-header" }, filterable: { multi: true, search: true } }, { field: "UPC", title: "UPC", width: "120px", filterable: true, editable: true, headerAttributes: { "class": "grid-header" } }, { field: "BrandName", title: "Brand", width: "120px", filterable: true, editable: true, headerAttributes: { "class": "grid-header" } }, { field: "ShortDescription", title: "Description", width: "180px", editable: false, headerAttributes: { "class": "grid-header" } }, { field: "Headline", title: "Headliner", width: "150px", editable: false, headerAttributes: { "class": "grid-header" } }, { field: "AdStartDate", title: "Effective Date", width: "140px", editable: true, headerAttributes: { "class": "grid-header" }, format: "{0: MM/dd/yyyy}", type: "date", filterable: { ui: "datetimepicker" }, }, { field: "Format", title: "Format", width: "110px", filterable: true, editable: false, headerAttributes: { "class": "grid-header" }, filterable: { multi: true, search: true } }, { field: "PrintQty", title: "Qty", width: "80px", editable: false, headerAttributes: { "class": "grid-header" } }, { field: "AdType", title: "AdType", width: "100px", filterable: true, editable: false, headerAttributes: { "class": "grid-header" } }, { field: "SaleType", title: "Sale Type", width: "100px", filterable: true, editable: false, headerAttributes: { "class": "grid-header" } }, ] }).data("kendoGrid"); }
尝试的无效代码
filterMenuInit: function (e) { var filteredDeptNames = getFilteredDeptNames(this); var filterMenu = e.container.find("[data-field='DeptName'] .k-filter-menu"); if (filterMenu.length > 0) { var dropdownlist = filterMenu.data("kendoDropDownList"); if (dropdownlist) { dropdownlist.dataSource.data(filteredDeptNames); dropdownlist.refresh(); } } },
问题分析与解决方案
之前的代码无效原因:
filterMenuInit仅在筛选菜单第一次初始化时触发,后续其他列筛选后不会重新执行,无法更新下拉选项- DOM选择器定位错误,应该直接通过
e.field判断当前打开的是哪列的筛选菜单
正确的实现方式是使用filterMenuOpen事件(每次打开筛选菜单时触发),实时基于当前已过滤的数据源生成唯一的DeptName选项:
修改后的完整代码(关键部分)
在Grid配置中添加filterMenuOpen事件,并实现对应的处理函数:
kGrid = $("#Grid").kendoGrid({ // 保留原有配置... filterable: { extra: false, operators: { string: { startswith: "Starts with", eq: "Is equal to", neq: "Is not equal to" } } }, // 添加filterMenuOpen事件 filterMenuOpen: function(e) { // 仅处理DeptName列的筛选菜单 if (e.field !== "DeptName") return; // 获取当前数据源经过所有筛选后的全部数据(注意:如果用服务器端分页/过滤,需调整为获取全量过滤后数据) var filteredData = this.dataSource.data(); // 提取DeptName的唯一值,去重并排序 var uniqueDepts = []; var seen = {}; filteredData.forEach(function(item) { var dept = item.DeptName; if (dept && !seen[dept]) { seen[dept] = true; uniqueDepts.push({ value: dept, text: dept }); } }); uniqueDepts.sort(function(a, b) { return a.text.localeCompare(b.text); }); // 获取筛选菜单中的多选下拉组件 var multiSelect = e.container.find("[data-role='multiselect']").data("kendoMultiSelect"); if (multiSelect) { // 更新下拉数据源 multiSelect.dataSource.data(uniqueDepts); // 保留已选中的有效选项 var selectedValues = multiSelect.value(); if (selectedValues.length > 0) { multiSelect.value(selectedValues.filter(function(val) { return seen[val]; })); } } }, // 保留原有配置... }).data("kendoGrid");
核心逻辑说明
- 事件选择:用
filterMenuOpen替代filterMenuInit,确保每次打开筛选菜单都能重新计算选项 - 获取过滤后的数据:通过
this.dataSource.data()获取当前所有已过滤的数据(如果是服务器端分页/过滤,需在客户端缓存全量数据或从后端获取过滤后的全量列表) - 去重处理:遍历数据提取唯一的DeptName值,整理成MultiSelect需要的
{value:..., text:...}格式 - 更新下拉组件:找到对应列的MultiSelect组件,更新其数据源,并保留用户已选中的有效选项
内容的提问来源于stack exchange,提问作者Faizan Ali
相关产品推荐
相关产品推荐

