Hello
As a reference I use the sample in https://demos.telerik.com/aspnet-mvc/grid/filter-multi-checkboxes. By clicking on the filter icon, a multi-check drop-down is displayed. I'd like to show a single selection drop-down (containing the distinct row values in the corresponding column as in the multi-check sample) instead of the multi-check drop-down. By clicking on one item in this drop-down, the grid content should be filtered using this selection.
A code sample would be greatly appreceated.
I'm using Kendo.Mvc Version 2018.2.516.545
Thank you and regards
Daniel
6 Answers, 1 is accepted
You can use the basic approach from this demo: https://demos.telerik.com/aspnet-mvc/grid/filter-menu-customization. It shows how you can create custom UI for the filter menu which for the City column is a dropdown that takes its data from its own action, so you can provide the desired distinct values.
You can also take the approach from this therad where you can use the change event of the dropdown to filter the grid data source immediately: https://www.telerik.com/forums/filtering-from-a-drop-down-list.
Here is the modified view of the demo that incorporates this:
@(Html.Kendo().Grid<Kendo.Mvc.Examples.Models.EmployeeViewModel>()
.Name(
"Grid"
)
.Columns(columns => {
columns.Template(@<text>@item.FirstName @item.LastName</text>)
.ClientTemplate(
"#=FirstName# #=LastName#"
)
.Title(
"Name"
);
columns.Bound(e => e.City)
.Filterable(filterable => filterable.UI(
"cityFilter"
))
.Width(200);
columns.Bound(e => e.Title)
.Filterable(filterable => filterable.UI(
"titleFilter"
))
.Width(350);
})
.Filterable(filterable => filterable
.Extra(
false
)
.Operators(operators => operators
.ForString(str => str.Clear()
.StartsWith(
"Starts with"
)
.IsEqualTo(
"Is equal to"
)
.IsNotEqualTo(
"Is not equal to"
)
))
)
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action(
"FilterMenuCustomization_Read"
,
"Grid"
))
)
)
<script type=
"text/javascript"
>
function
cityFilter(element) {
element.kendoDropDownList({
dataSource: {
transport: {
read:
"@Url.Action("
FilterMenuCustomization_Cities
")"
}
},
change:
function
(evt) {
var
value =
this
.value();
var
grid = $(
"#Grid"
);
if
(value) {
grid.data(
"kendoGrid"
).dataSource.filter({ field:
"City"
, operator:
"eq"
, value: value });
}
else
{
grid.data(
"kendoGrid"
).dataSource.filter({});
}
},
optionLabel:
"--Select City--"
});
}
function
titleFilter(element) {
element.kendoAutoComplete({
dataSource: {
transport: {
read:
"@Url.Action("
FilterMenuCustomization_Titles
")"
}
}
});
}
</script>
You can even customize thie particular menu by walking the DOM, adding classes and hiding other elements, for example this will hide the Clear and Filter buttons:
<style>
.hiddenFilterElems .k-button {display: none;}
</style>
<script type=
"text/javascript"
>
function
cityFilter(element) {
element.parents(
"form"
).first().addClass(
"hiddenFilterElems"
);
element.kendoDropDownList({
Regards,
Marin Bratanov
Progress Telerik
Hi Marin
thank you for your answer and for your code sample. What I'd like to omit is the roundtrip to the server (see e.g. the code "read: "@Url.Action("FilterMenuCustomization_Cities")"" in your sample. What I'd like to do is to access the list of possible row values in the corresponding column which the user wants to filter. Please see the "server operations" sample in https://demos.telerik.com/aspnet-mvc/grid/filter-multi-checkboxes, when you click the City-filter, a list of distinct cities appear... this is the list I am talking about... Is it possible to access this list in the "function cityFilter(element)"-function?
Thank you again for your help and best regards.
Daniel
When having server operations, both having all distinct values from the data source, and not having a separate request is a self-contradicting scenario because the client cannot know what data there is in the actual data source, it only has the current page.
If you are OK with having the dropdown use only the current data in the grid (after filtering by city you will only have data for that city, so the dropdown will only have 1 option), you can do something like this: https://dojo.telerik.com/@bubblemaster/oCeYuNEP. This can work best if there is no editing in the grid and this example does not use server operations. If this suits your needs, you can use the snippet below to translate it to the MVC wrapper.
If you have editing in the grid, you'd need to hook to the filterMenuOpen event and do a setDataSource() on the dropdown to update with new data or remove old values.
Below is a sample implementation that steps on the initial grid data source that you can also use as base in case defining separate data sources is not an option. Storing the data source in a data object in the grid wrapper is not the best solution, so I am leaving it up to you to determine where you can store data in such a case (even a global variable is an option).
The key point is setting the dataSource of the dropdown to a suitable data source. The exapmle above shows one way to fetch it from the grid. The example below shows another. You can use a separate data source control or call your own service once to populate an array when the grid loads - the menu uses lazy initialization so it will be created when shown only.
I hope this information helps you choose the best approach for your case and needs.
@(Html.Kendo().Grid<Kendo.Mvc.Examples.Models.EmployeeViewModel>()
.Name(
"Grid"
)
.Events(ev => ev.DataBound(
"storeData"
))
.Columns(columns =>
{
columns.Template(@<text>@item.FirstName @item.LastName</text>)
.ClientTemplate(
"#=FirstName# #=LastName#"
)
.Title(
"Name"
);
columns.Bound(e => e.City)
.Filterable(filterable => filterable.UI(
"cityFilter"
))
.Width(200);
columns.Bound(e => e.Title)
.Filterable(filterable => filterable.UI(
"titleFilter"
))
.Width(350);
})
.Filterable(filterable => filterable
.Extra(
false
)
.Operators(operators => operators
.ForString(str => str.Clear()
.StartsWith(
"Starts with"
)
.IsEqualTo(
"Is equal to"
)
.IsNotEqualTo(
"Is not equal to"
)
))
)
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action(
"FilterMenuCustomization_Read"
,
"Grid"
))
)
)
<style>
.hiddenFilterElems .k-button {
display: none;
}
</style>
<script type=
"text/javascript"
>
function
cityFilter(element) {
element.parents(
"form"
).first().addClass(
"hiddenFilterElems"
);
element.kendoDropDownList({
dataSource: $(
"#Grid"
).data(
"origData"
),
change:
function
(evt) {
var
value =
this
.value();
var
grid = $(
"#Grid"
);
if
(value) {
grid.data(
"kendoGrid"
).dataSource.filter({ field:
"City"
, operator:
"eq"
, value: value });
}
else
{
grid.data(
"kendoGrid"
).dataSource.filter({});
}
},
optionLabel:
"--Select City--"
,
dataTextField:
"City"
,
dataValueField:
"City"
});
}
function
storeData(e) {
var
gridData = e.sender.dataSource.data();
var
uniqueDsResult = removeDuplicates(e.sender.dataSource.data(),
"City"
);
e.sender.element.data(
"origData"
, uniqueDsResult);
}
function
titleFilter(element) {
element.kendoAutoComplete({
dataSource: {
transport: {
read:
"@Url.Action("
FilterMenuCustomization_Titles
")"
}
}
});
}
function
removeDuplicates(items, field) {
var
getter =
function
(item) {
return
item[field] },
result = [],
index = 0,
seen = {};
while
(index < items.length) {
var
item = items[index++],
text = getter(item);
if
(text !== undefined && text !==
null
&& !seen.hasOwnProperty(text)) {
result.push(item);
seen[text] =
true
;
}
}
return
result;
}
</script>
Regards,
Marin Bratanov
Progress Telerik
Hi Marin
Thank you for providing your suggestions. Yes, I want to use the current data in the grid, and it is a simple no editing scenario. Following your dojo sample, I tried to slim down the filter dialog: no extra filter, only eq: string operator, no Filter and Clear buttons. Furthermore, I replaced the kendoDropdownList with a simple kendoListBox. The plan is, as soon as an item in this listBox is selected (using the change: event), the grid should be filtered by applying this filter. Please have a look at https://dojo.telerik.com/umAFOJUK, there you'll find my work so far. The problem is: as soon as I click on the city filter button, an exception is thrown and the change: event is never fired.
kendo.all.js:9555 Uncaught TypeError: e.value is not a function
at init.refresh (VM14497 kendo.all.min.js:29)
at init.bind (VM14497 kendo.all.min.js:29)
at r.applyBinding (VM14497 kendo.all.min.js:29)
at r.bind (VM14497 kendo.all.min.js:29)
at s (VM14497 kendo.all.min.js:28)
at s (VM14497 kendo.all.min.js:28)
at s (VM14497 kendo.all.min.js:28)
at Object.a [as bind] (VM14497 kendo.all.min.js:28)
at init.refresh (VM14497 kendo.all.min.js:48)
at init._init (VM14497 kendo.all.min.js:48)
How can I correct this error?
Thank you again for your help and best regards.
Daniel
The ListBox does not have "value" method, which is mandatory for the filter elements. As a workaround you could try to render the ListBox over another element and handle its change event to manually filter the Grid. Following is the modified dojo example, which renders the ListBox:
Best Regards,
Konstantin Dikov
Progress Telerik
Hi Konstantin
Thank you very much for your help. Your dojo-sample did the trick...
I added some additional functionality like sort the kendoListBox's dataSource and caching all the individual column values for further use.
Best regards and thank's again!
Daniel