
How do I force a refresh of the filter menu item in a grid column when the grid is filtered after filterMenuItem has been fired?
The grid data source is having an initial filter, that might make the grid empty at initialization.
Scenario 1:
If I click on one of the grid column filter buttons (let us call it ReviewStatus) with a multi check box filter (filterMenuItem is fired), then there is of course no options available when the grid is empty. If I then clear all filters on the data source by calling grid.dataSource.filter({}) and the grid is now having rows, then there is still no options available in the ReviewStatus filter.
Scenario 2:
The first thing I do is to clear all filters such that there is rows available in the grid. If I use a multi check filter on one of the other columns to filter the grid then the options in ReviewStatus will be updated accordingly.
How do I force the filter menu item to sync the data source with the grid data source after the filter menu item is initialized?
My grid has a the following grid options:
dataSource:{
type:
"odata"
,
serverFiltering:
true
,
serverSorting:
true
,
filter:{ field: "Country": operator: "eq", value: "UK" }
},
columns: [
{ field: "ReviewStatus", filterable: { multi: true } },
{ field: "Country", filterable: { multi: true } },
{ field: "Severity", filterable: { multi: true } },
{ field: "SensorName", filterable: { multi: true } }
],
filterable:
true
,
navigatable:
true
,
selectable:
"multiple"
,
resizable:
true
,
reorderable:
true
,
sortable:
true
,
columnMenu:
false
,
pageable:
false
23 Answers, 1 is accepted
Hello Jonas Nygaard,
When filter multi checkboxes are used, they are populated only once, when the respective filtering menu is opened for the first time. This ensures good performance and avoids repetitive and unnecessary HTTP requests. By design, the checkboxes's data is refreshed automatically in the following cases:
- the Grid dataSource is replaced via the setDataSource method
- a Grid data item is added, removed or modified
Given this my recommendations would be:
- use setDataSource to toggle the Grid data. This has two benefits - the checkboxes' data will refresh automatically, and also, the custom business logic will integrate better with the Grid functionality.
http://dojo.telerik.com/oKerE/4
- use a separate DataSource instance for the checkboxes data, and call its read() method when the Grid data is toggled
http://dojo.telerik.com/IjEre
- there is third option - define one DataSource instance and assign it to both the Grid and in columns.filterable.dataSource. This will refresh the checkbox data automatically, but if you filter the Grid data via the Grid UI, you will also filter (reduce) the checkbox items.
http://dojo.telerik.com/AyEca/2
Regards,
Boyan Dimitrov
Telerik

I like the third option where the checkbox items are reduced as the grid is filtered. I can, however, not get it to work properly. Instead of having unique checkbox items I get an item for each row in the grid.
I am using angularjs and am setting the grid data source to the multi filterables just before setting the grid options on the angular scope.
I have the first option working where I use grid.setDataSource() when serverFiltering is enabled and use grid.dataSource.filter() serverFiltering is disabled.
Hello Jonas Nygaard,
this seems strange - can you modify the example provided by Boyan so that we may exhibit the problem on our side? Thank you in advance.
Regards,
Petyo
Telerik

In http://dojo.telerik.com/AyEca/2
Try filtering Unit Price by selecting $18.00 and $19.00. Then open the Unit Price filter again and you will see 4 $18.00 and 2 $19.00 checkbox items.
Hello Jonas Nygaard,
Indeed the logic for finding and displaying the distinct(unique) values will not be executed in case of using same DataSource instance for the Kendo UI Grid and multi filter check box.
I would suggest to use the following approach in order to refresh the multi checkbox filter options:
$(
".k-grid-filter"
).click(
function
(e){
var
fmc = $(e.target).closest(
"th"
).data(
"kendoFilterMultiCheck"
);
fmc.checkSource.read();
fmc.container.empty();
fmc.refresh();
});
Please refer to the http://dojo.telerik.com/oKerE/14 example.
Regards,Boyan Dimitrov
Telerik


I was able to fix this globally for my app (without performance impact) by overriding these two methods on kendoFilterMultiCheck (TypeScript)
refresh(e: any): void {
var didChange = false;
this.checkSource.one("change",() => {
didChange = true;
});
super.refresh(e);
if (!didChange && this.form && e && this.options.forceUnique && e.sender === this.dataSource && !this.dataSource.options.serverPaging) {
this.isStale = true;
}
}
protected _click(e: JQueryEventObject): void {
if (this.isStale) {
this.isStale = false;
var field = this.field;
this.checkSource.data(this.dataSource.data().slice(0).distinct(t => <any>t[field]));
this.container.empty();
this.createCheckBoxes();
}
super._click(e);
}
distinct() is our own addition to the Array prototype.

Hi Sean,
When you say you override it, you actually change the Kendo JS file or did you extend it?

Hi Bojan,
I was looking for a similar behavior in my project as the third option presented by you. I am using asp.net instead of html5.
This is my kendo grid definition in the view :
<div class="grid-container-fluid">
@(Html.Kendo().Grid<ManageSuperUserViewModel>()
.Name("ManageSuperuserGrid")
.ToolBar(tools => tools.Excel())
.Excel(excel => excel
.FileName("Super Users List.xlsx")
.AllPages(true)
)
.Columns(columns =>
{
columns.Bound(x => x.Id).Visible(false);
columns.Bound(x => x.L1Process.Name).Width(200).Filterable(ftb => ftb.Multi(true).Search(true).DataSource(ds => ds
.Read(r => r
.Action("L1ProcessReadNameValues", "SuperUser")
))).HeaderHtmlAttributes(new { style = "overflow: visible; white-space: normal" }).Title("L1 Process");
columns.Bound(x => x.User.UserNameDescriptive).Width(220).Filterable(ftb => ftb.Multi(true).Search(true).DataSource(ds => ds
.Read(r => r
.Action("UserReadNameValues", "SuperUser")
.Type(HttpVerbs.Post)
))).HeaderHtmlAttributes(new { style = "overflow: visible; white-space: normal" }).Title("Super User Names");
columns.Bound(x => x.Profile.Name).Width(250).Filterable(ftb => ftb.Multi(true).Search(true).DataSource(ds => ds
.Read(r => r
.Action("UserProfileNameValues", "SuperUser")
.Type(HttpVerbs.Post)
))).HeaderHtmlAttributes(new { style = "overflow: visible; white-space: normal" }).Title("Profile Names");
columns.Bound(x => x.RoleType.Name).Width(150).Filterable(ftb => ftb.Multi(true).Search(true).DataSource(ds => ds
.Read(r => r
.Action("UserRoleTypeNameValues", "SuperUser")
.Type(HttpVerbs.Post)
))).HeaderHtmlAttributes(new { style = "overflow: visible; white-space: normal" }).Title("Role Types");
columns.Bound(x => x.BusinessUnit.Name).Width(150).Filterable(ftb => ftb.Multi(true).Search(true).DataSource(ds => ds
.Read(r => r
.Action("UserBusinessUnitNameValues", "SuperUser")
.Type(HttpVerbs.Post)
))).HeaderHtmlAttributes(new { style = "overflow: visible; white-space: normal" }).Title("Business Org 1");
columns.Bound(x => x.Market.Name).Width(150).Filterable(ftb => ftb.Multi(true).Search(true).DataSource(ds => ds
.Read(r => r
.Action("UserMarketNameValues", "SuperUser")
))).HeaderHtmlAttributes(new { style = "overflow: visible; white-space: normal" }).Title("Business Org 2");
columns.Bound(x => x.BusinessType.Name).Width(150).Filterable(ftb => ftb.Multi(true).Search(true).DataSource(ds => ds
.Read(r => r
.Action("UserBusinessTypeNameValues", "SuperUser")
.Type(HttpVerbs.Post)
))).HeaderHtmlAttributes(new { style = "overflow: visible; white-space: normal" }).Title("Business Org 3");
columns.Bound(x => x.ProcessRole.ProcessRoleId).Width(100).Title("ProcessRole").Filterable(false).Visible(false);
columns.Bound(x => x.CanEdit).Visible(false);
columns.Command(command =>
{
if(user.CanManageSuperUser() || User.IsInRole("Administrator") || user.IsInProcessRole(Enums.ProcessRolesEnum.GPD) || user.IsInProcessRole(Enums.ProcessRolesEnum.Delegates))
{
command.Edit().UpdateText("Save").CancelText("Cancel").Text("Edit");
//command.Destroy();
command.Custom("Delete").Click("deleteRow");
}
}).Width(180);
})
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(50)
//.ServerOperation(false)
.Model(model =>
{
model.Id(p => p.Id);
})
.Read(read => read.Action("GetUserDetails", "SuperUser"))
.Create(create => create.Action("AddUserDetails", "SuperUser"))
.Update(update => update.Action("UpdateUserDetails", "SuperUser"))
//.Destroy(destroy => destroy.Action("DeleteUserDetails", "SuperUser"))
.Sort(sort =>
{
sort.Add(profile => profile.Profile.Name);
})
)
.ToolBar(toolbar =>
{
if (user.CanManageSuperUser() || User.IsInRole("Administrator") || user.IsInProcessRole(Enums.ProcessRolesEnum.GPD) || user.IsInProcessRole(Enums.ProcessRolesEnum.Delegates))
{
toolbar.Create().Text("Add New SuperUser");
}
toolbar.Custom().Name("Clear").Text("Clear All Filters").Url("#").HtmlAttributes(new { onclick = "clearAllFilters()" });
})
.Editable(editable => editable.Mode(GridEditMode.PopUp).TemplateName("SuperuserEdit").Window(window => { window.Width(1000); }).DisplayDeleteConfirmation(true))
.Filterable()
.Resizable(resize => resize.Columns(true))
.Scrollable()
.Sortable(sortable =>
{
sortable.AllowUnsort(false);
//sortable.SortMode(GridSortMode.MultipleColumn);
})
.Reorderable(reorderable => reorderable.Columns(true))
.Pageable(pageable => pageable
.Refresh(true)
.PageSizes(true)
.PageSizes(new int[] { 50, 100, 200, 300, 400, 500 })
.ButtonCount(5))
.Events(events =>
{
events.Edit("onEditManageSuperuserGrid");
events.DataBound("onDataBoundManageSuperuserGrid");
//events.FilterMenuInit("filterMenuInit");
})
)
</div>
This definition of kendo grid refreshes the filters when the user click in the icon because in the event I am implementing this solution :
$(".k-grid-filter").click(function (e) {
var fmc = $(e.target).closest("th").data("kendoFilterMultiCheck");
fmc.checkSource.read();
//fmc.container.empty();
fmc.refresh();
});
I am always uploading all the data in the filter everytime the user clicks on filter icon. But I don't want this solution, I want only to upload in the filters the possible values that exist in the datasource of the grid. (Similar as your behavior in the option three explained upper).
I would be extremely grateful if you could help me in this matter.
Could you tell me how to assign the same datasource in asp.net?
Best Regards.
Raúl.
Hello Raul,
A possible solution would be passing some data to the checkSource of the kendoFilterMultiCheck widget. The checkSource is a Kendo UI DataSource instance which contains the unique items in the Kendo UI Grid (according to the entire data for the Kendo UI Grid). The easist way to change the items shown when kendoFilterMultiCheck is opened is to pass an array of items to the data method of the checkSource. Please refer to the code snippet below:
fmc.checkSource.data([{LastName: "test"}, {LastName: "test2"}])
Regards,
Boyan Dimitrov
Telerik by Progress

Hello Boyan,
Thank you for you quick reply ...
I want a solution like you use in the third option ('http://dojo.telerik.com/AyEca/2'). Datasource of the Grid is synchronized with the KendofilterMultiCheck. The problem is that you are using HTML script and I am using asp.net code.
Could you please translate your code in HTML to asp.net in order to see how you are configuring the kendo grid and how you are defining the datasource?.
Thanks in advanced.
Raúl.
Hello Raul,
I am afraid that in the context of ASP.NET MVC wrappers the DataSource instance are created automatically. Given this developer is not able to define shared DataSource (Kendo UI Grid and the FilterMultiCheckBox widget share same DataSource instance).
Regards,Boyan Dimitrov
Telerik by Progress

Hello Boyan,
Thank you for your reply ...
As workaround to this limitation ... Could I access to the Grid DataSource when kendoFilterMultiCheck is opened?
My idea is populate the filter that the user is clicking with the different values of the Grid Datasource that exist in the column selected.
I pretend to populate the filter in the 'data' method of the checkSource.:
$(".k-grid-filter").click(function (e) {
var fmc = $(e.target).closest("th").data("kendoFilterMultiCheck");
!!!! -------------------- Here your code ----------------------------------- !!!!!!
//fmc.checkSource.read();
fmc.refresh();
});
Using the event of the filter (k-grid-filter) and 'data' method like in your example --> fmc.checkSource.data([{LastName: "test"}, {LastName: "test2"}])
Can you provide one or several sentences to obtain an array with the different values of the Grid DataSource when the event click of the filter is fired?
Thanks in advanced.
Raúl.
Hello Raul,
A reference to the DataSource associated with Kendo UI Grid can be accessed by using the property dataSource of the Kendo UI Grid instance. Once there is a reference to the DataSource instance the data method can be used in order to get the data items from the DataSource.
Regards,Boyan Dimitrov
Telerik by Progress

Is there any plans to add this feature natively?
I found this very useful in the Silverlight version of Kendo and currently the lack of this feature is very disappointing. i have not been able to find a workaround (the suggestion here displays duplicates) except to manually check for existing filters and manually filter them out before sending the individual datasource for each column filter
I believe that solution with sharing same Kendo UI DataSource instance (the one that associated with the Kendo UI Grid) with each checkbox filter widget should work for you. Could you please test the http://dojo.telerik.com/EWIbAbiR example and let us know if you have any issues with this approach?
Regards,
Boyan Dimitrov
Progress Telerik

I believe that solution with sharing same Kendo UI DataSource instance (the one that associated with the Kendo UI Grid) with each checkbox filter widget should work for you. Could you please test the http://dojo.telerik.com/EWIbAbiR example and let us know if you have any issues with this approach?
Regards,
Boyan Dimitrov
Progress Telerik
[/quote]
The problem with that approach is the duplicates, so if table has 3 of $18.00 the filterbox displays 3 $18.00 checkboxes, i want it to only show unique data within each column
Currently i can only do this by using a datasource for each column and sending it the existing filters that are applied so that it takes it into account when returning results. You can imagine this is a very tedious process (also has alot of calls to the server) and would like to avoid it if there is a more straightforward approach, as mentioned none mentioned in this thread satisfies my needs
Please refer to the https://dojo.telerik.com/IhIKUZiB example where I implement something similar to the behavior you are trying to achieve. I would like to mention that from user perspective filtering the checkbox items in the filter menu might be problem, because user is not able to change the filtering without clearing the filter.
In other words if user tries to filter on Unit Price field and check the $18 check box, the grid will show only items with Unit Price $18. If user tries to open the menu again the menu will show only $18 checked. To modify the filter (to show items with $19 price) user should clear the filter so all other check boxes will be visible in the filter menu and select the new one.
Regards,
Boyan Dimitrov
Progress Telerik

Please refer to the https://dojo.telerik.com/IhIKUZiB example where I implement something similar to the behavior you are trying to achieve. I would like to mention that from user perspective filtering the checkbox items in the filter menu might be problem, because user is not able to change the filtering without clearing the filter.
In other words if user tries to filter on Unit Price field and check the $18 check box, the grid will show only items with Unit Price $18. If user tries to open the menu again the menu will show only $18 checked. To modify the filter (to show items with $19 price) user should clear the filter so all other check boxes will be visible in the filter menu and select the new one.
Regards,
Boyan Dimitrov
Progress Telerik
[/quote]
This is perfect! only problem is, is there a way to sort the multicheck items alphabetically with this approach?
i normally do this when the 'FilterMenuInit' is triggered with this code:
var filterMultiCheck = this.thead.find("[data-field=" + e.field + "]").data("kendoFilterMultiCheck");
filterMultiCheck.container.empty();
filterMultiCheck.checkSource.sort({ field: e.field, dir: "asc" });
var view = filterMultiCheck.checkSource.view();
var json = view.toJSON();
filterMultiCheck.checkSource.data(json);
filterMultiCheck.createCheckBoxes();
But obviously with your implementation it doesnt work together, if you could give me a solution for this it would be highly appreciated

Never mind i have been able to sort it out :) Thank you very much for your help Boyan Dimitrov
if anyones interested, i solved it by using my 'FilterMenuInit' code and just ran a sort method on the items in the code provided by Boyan, this sorting did not work until i enabled the 'FilterMenuInit' code
$(
".k-grid-filter"
).on(
"click"
,
function
(e) {
function
dynamicSort(property) {
var
sortOrder = 1;
if
(property[0] ===
"-"
) {
sortOrder = -1;
property = property.substr(1);
}
return
function
(a, b) {
if
(sortOrder == -1) {
return
b[property].localeCompare(a[property]);
}
else
{
return
a[property].localeCompare(b[property]);
}
}
}
function
distinct(items, field) {
var
getter = kendo.getter(field,
true
),
result = [],
index = 0,
seen = {};
items.sort(dynamicSort(field));
while
(index < items.length) {
var
item = items[index++],
text = getter(item);
if
(text !== undefined && !seen.hasOwnProperty(text)) {
result.push(item);
seen[text] =
true
;
}
}
console.log(result);
return
result;
}
var
fmc = $(e.target).closest(
"th"
).data(
"kendoFilterMultiCheck"
);
if
(fmc.dataSource.filter()) {
fmc.checkSource.filter(fmc.dataSource.filter());
}
fmc.checkSource.data(distinct(fmc.dataSource.view(), fmc.field));
fmc.refresh();
});

Hi,
Thank you very much for your post. In my case I was experiencing 2 problems, first one each filter alter grid data and remove data unnecesary, so I have to clone dataSource with Object.assign and separate one dataSource for grid and another one for columns. Finally this solution doesn't work fine on paginate data so I update code:
$(".k-grid-filter").on("click", function (e) {
function dynamicSort(property) {
var sortOrder = 1;
if (property[0] === "-") {
sortOrder = -1;
property = property.substr(1);
}
return function (a, b) {
if (sortOrder == -1) {
return b[property].localeCompare(a[property]);
} else {
return a[property].localeCompare(b[property]);
}
}
}
function distinct(items, field) {
var getter = kendo.getter(field, true),
result = [],
index = 0,
seen = {};
items.sort(dynamicSort(field));
while (index < items.length) {
var item = items[index++],
text = getter(item);
if (text !== undefined && !seen.hasOwnProperty(text)) {
result.push(item);
seen[text] = true;
}
}
console.log(result);
return result;
}
var fmc = $(e.target).closest("th").data("kendoFilterMultiCheck");
if (fmc.dataSource.filter()) {
fmc.checkSource.filter(fmc.dataSource.filter());
}
fmc.checkSource.data(distinct(fmc.dataSource.data(), fmc.field));
fmc.refresh();
});

Dont want to spam but i have been getting alot of inconsistency with my previous method(wish we could edit posts), so this is final solution that works (but as mentioned, does not work if you have paging enabled as it only gives the options that are visible on that page), this will put the items in ASC order
This is probably not the cleanest thing ever (the 'onFilterMenuInit' is just same thing but minified, had to add this or else i would get inconsistencies)
$(
".k-grid-filter"
).on(
"click"
,
function
(e) {
function
dynamicSort(property) {
var
sortOrder = 1;
if
(property[0] ===
"-"
) {
sortOrder = -1;
property = property.substr(1);
}
return
function
(a, b) {
var
strA = a[property];
var
strB = b[property];
if
(strA !=
null
){
strA = strA.toString();
}
else
{
strA =
""
;
}
if
(strB !=
null
) {
strB = strB.toString();
}
else
{
strB =
""
;
}
if
(sortOrder == -1) {
return
strB.localeCompare(strA);
}
else
{
return
strA.localeCompare(strB, undefined, { numeric:
true
});
}
}
}
function
distinct(items, field) {
var
getter = kendo.getter(field,
true
),
result = [],
index = 0,
seen = {};
items.sort(dynamicSort(field));
while
(index < items.length) {
var
item = items[index++],
text = getter(item);
if
(text !== undefined && !seen.hasOwnProperty(text)) {
result.push(item);
seen[text] =
true
;
}
}
return
result;
}
var
fmc = $(e.target).closest(
"th"
).data(
"kendoFilterMultiCheck"
);
if
(fmc !=
null
&& fmc !=
'undefined'
) {
if
(fmc.dataSource.filter()) {
fmc.checkSource.filter(fmc.dataSource.filter());
}
fmc.checkSource.data(distinct(fmc.dataSource.view(), fmc.field));
if
(fmc.container !=
null
) {
fmc.container.empty();
}
fmc.refresh();
}
});
function
onFilterMenuInit(e) {
function
dynamicSort(e) {
var
t = 1;
return
"-"
=== e[0] && (t = -1, e = e.substr(1)),
function
(r, c) {
var
n = r[e], a = c[e];
return
n =
null
!= n ? n.toString() :
""
, a =
null
!= a ? a.toString() :
""
, -1 == t ? a.localeCompare(n) : n.localeCompare(a) } }
function
distinct(e, t) {
var
r = kendo.getter(t, !0), c = [], n = 0, a = {};
for
(e.sort(dynamicSort(t)) ; n < e.length;) {
var
i = e[n++], f = r(i); void 0 === f || a.hasOwnProperty(f) || (c.push(i), a[f] = !0) }
return
c }
var
fmc =
this
.thead.find(
"[data-field="
+ e.field +
"]"
).data(
"kendoFilterMultiCheck"
);
null
!= fmc &&
"undefined"
!= fmc && (fmc.dataSource.filter() && fmc.checkSource.filter(fmc.dataSource.filter()), fmc.checkSource.data(distinct(fmc.dataSource.view(), fmc.field)),
null
!= fmc.container && fmc.container.empty(), fmc.refresh());
}

Thanks for sharing. If you change this:
if
(fmc !=
null
&& fmc !=
'undefined'
) {
if
(fmc.dataSource.filter()) {
fmc.checkSource.filter(fmc.dataSource.filter());
}
fmc.checkSource.data(distinct(fmc.dataSource.view(), fmc.field));
if
(fmc.container !=
null
) {
fmc.container.empty();
}
fmc.refresh();
}
to this:
if
(fmc !==
null
&& fmc !== undefined) {
var
grid = $(e.target).closest(
".k-grid"
).getKendoGrid();
var
dataSource = grid.dataSource;
var
allData = dataSource.data();
var
filters = dataSource.filter();
var
query =
new
kendo.data.Query(allData);
var
data = query.filter(filters).data;
fmc.checkSource.data(distinct(data, fmc.field));
if
(fmc.container !==
null
) {
fmc.container.empty();
}
fmc.refresh();
}
it will also works on paged data. (tested with ServerOperation = false)