This is a migrated thread and some comments may be shown as answers.

Custom filter.

10 Answers 1310 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Kevin
Top achievements
Rank 1
Kevin asked on 06 Mar 2019, 07:25 PM

Currently I have a grid that uses a helper to specify the attributes:

.Filterable(f => f
.Extra(false)
.Operators(o => o
.ForString(str => str
.Clear()
.Contains("Contains")
.StartsWith("Starts with")
.IsEqualTo("Is equal to")
.IsNotEqualTo("Is not equal to")
)
.ForNumber(num => num
.Clear()
.IsEqualTo("Is equal to")
.IsGreaterThan("Is greater than")
.IsLessThan("Is less than")
)
)

The filterable data in the grid is encoded, s. o 'a b' is stored as 'a%20b'. I would like to build a custom filter using the helpers that allows the user to filter on 'a b'?

 

10 Answers, 1 is accepted

Sort by
0
Kevin
Top achievements
Rank 1
answered on 06 Mar 2019, 08:05 PM
One added piece of information. I am stuck using an older version of the Kendo Grid (Kendo UI v2015.1.318). So solutions that involve added features since then may not work for me. Thank you.
0
Tsvetina
Telerik team
answered on 08 Mar 2019, 12:38 PM
Hello Kevin,

Could you explain in a bit more detail what you mean by a custom filter? Do you want to apply a filter without using the Grid filtering menu? Also, in what scenario and where is the space from the filter value encoded? If Ajax binding is used in the Grid, the DataSource passes the data using JSON format and such encoding is not applied to the filter parameters. Or are you using server binding for the Grid?
It would help if you could share the full Grid declaration together with the explanation of the specific problem.

Regards,
Tsvetina
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Kevin
Top achievements
Rank 1
answered on 08 Mar 2019, 01:09 PM

I have attached an example of the full definition. As shown in the attached screen-shot I would like for the user to be able to type in 'a b' when the data stored is 'a%20b'. There are no matches for 'a b' but there are matches for 'a%20b'. I want to be able to intercept the filtering process so that I can either transform the filter string that was typed in to 'a%20b' or transform what is being matched into something that includes spaces.

Thank you for you help.

@{
const string gridName = "GlobalAgendaDashboardGrid"; 
const string tabName = "globalAgendaDashboardTab"; }

@(Html.Kendo().Grid<GlobalAgendaDashboardGridViewModel>()
.Name(gridName)
.Columns(columns =>
{
GridHelpers.FilterableColumn(columns, m => m.Title) 
.ClientTemplate(@"" +
           "<div style='display: none;'>#: decodeURIComponent(Title) #</div>");
GridHelpers.FilterableColumn(columns, m => m.Description) 
.ClientTemplate(@"" +
           "<div style='display: none;'>#: decodeURIComponent(Description) #</div>");
GridHelpers.FilterableColumn(columns, m => m.SessionItemId);
GridHelpers.FilterableColumn(columns, m => m.Authors);
GridHelpers.FilterableColumn(columns, m => m.GartnerSpeakers);
GridHelpers.FilterableDropdownColumn(columns, m => m.SessionCategory, "sessionCategory", gridName);
GridHelpers.FilterableDropdownColumn(columns, m => m.RegionHtmlString, "region", gridName).Encoded(false)

;
GridHelpers.FilterableColumn(columns, m => m.Location);
GridHelpers.FilterableDropdownColumn(columns, m => m.StatusHtmlString, "status", gridName) 
.ClientTemplate(@"" +
          "# if (SessionItemId) { #" +
          "<a href='" + Url.Action("Details", "Item") + "/#: SessionItemId #' target='_blank'>#: IsDeclined ? StatusTextWithDeclinedInformation : StatusText #</a>" + 
          "# } else { #" +
          "<a href='" + Url.Action("Details", "EventSeriesItem") + "/#: Id #' target='_blank'>#: IsDeclined ? StatusTextWithDeclinedInformation : StatusText #</a>" +
          "# } #").Encoded(false)

;
columns.Template( @<text></text>).Width(120)
.HeaderTemplate("<div style='padding: 3px;margin-left:37px;color: #41708F;'>Action</div>")

.HtmlAttributes( new { @class = "actionMenuTemplateCell",@style="overflow:visible;" })
 
.ClientTemplate(@"" +
        "<div class='btn-group-xs'>" +
        "# if (CanEdit) { #" +
        "# if (RegionalItemId != null) { #" +
        "# if (SessionItemId == null) { #" +
        "# if (EventWorkflowStageId == 1) { #" +
        "<button id='addToGlobalAgendaButton_#:Key#' class='btn btn-primary' onclick='Gartner.GlobalAgendaTabGrid.promote(#= RegionalItemId #)'>Add</button>" +
        "# } #" +
        "<button id='removeFromGlobalAgendaButton_#:Key#' class='btn btn-danger' onclick='Gartner.GlobalAgendaTabGrid.decline(#= RegionalItemId #)' # if (IsDeclined) { # disabled # } #>Remove</button>" +
        "# } #" +
        "# } #" +
        "# } #" +
        "</div>")
;
GridHelpers.FilterableColumn(columns, m => m.IsDeclined).Hidden() ;
GridHelpers.FilterableColumn(columns, m => m.Id).Hidden() .ClientGroupHeaderTemplate("Title: #= decodeURIComponent(Gartner.GlobalAgendaTabGrid.seriesTitleTemplate(value)) # <br/>Description: #= decodeURIComponent(Gartner.GlobalAgendaTabGrid.seriesDescriptionTemplate(value)) # <br/>Author: #= Gartner.GlobalAgendaTabGrid.seriesAuthorTemplate(value) # <span class='badge badge-light'>#= count#/#= Gartner.GlobalAgendaTabGrid.seriesSizeTemplate(value) #</span>");
})
.AutoBind(false)
.DataSource(dataSource => 
{
dataSource
.Ajax() .Events(events => events.RequestEnd("Gartner.GlobalAgendaTabGrid.onReadRequestEnd")) 
.PageSize(25)
.Read(read => read.Action("GlobalAgendaDashboardGridDataSource", "Home")) .Aggregates(aggregates =>{
    aggregates.Add(p => p.Id).Count();
})
.Group(g => g.Add(p => p.Id))
;
})
.Deferred() 
.Events(events => events.DataBound("function(e) { Gartner.refreshFiltersForGrid('" + gridName + "'); Gartner.buildActionMenu(e);Gartner.GlobalAgendaTabGrid.onDataGridBound(e);}"
))
.Excel(excel => excel
.FileName("Global Agenda Dashboard Grid.xlsx") 
.Filterable(true)
.ProxyURL(Url.Action("Export", "Excel"))
.AllPages(true)
)
.Filterable(f => f
.Extra(false)
.Operators(o => o
.ForString(str => str
.Clear()
.Contains("Contains")
.StartsWith("Starts with")
.IsEqualTo("Is equal to")
.IsNotEqualTo("Is not equal to")
)
.ForNumber(num => num
.Clear()
.IsEqualTo("Is equal to")
.IsGreaterThan("Is greater than")
.IsLessThan("Is less than")
)
)

.Pageable() 
.Resizable(resize => resize.Columns(true)) 
.Scrollable() 
.Sortable() 
.Groupable()
.ToolBar(tools =>
{
 
tools.Template(
@<text>
  <div class="show-declined-checkbox-group">
  <input class="k-checkbox" id="ShowDeclinedItemsCheckBox" name="ShowDeclinedItemsCheckBox" type="checkbox">
  <label class="k-checkbox-label" for="ShowDeclinedItemsCheckBox">Show Declined Items</label>
  </div>
  <div class="show-appoved-checkbox-group">
  <input class="k-checkbox" id="ShowApprovedItemsCheckBox" name="ShowApprovedItemsCheckBox" type="checkbox">
  <label class="k-checkbox-label" for="ShowApprovedItemsCheckBox">Items to be Approved</label>
            <span id="needs-approval-span" class="badge badge-light"></span>
  </div>
  </text>
); 
})
)

@Html.DeferTypeScriptObject("GlobalAgendaTabGrid", new
{
TabId = tabName,
GridId = gridName
})   

@Html.DeferTypeScriptObject("ToggleShowDeclinedItems", new
{
gridSelector = "#" + gridName,
toggleSelector = "#" + "ShowDeclinedItemsCheckBox"
})   
@Html.DeferTypeScriptObject("ToggleShowApprovedItems", new
{
gridSelector = "#" + gridName,
toggleSelector = "#" + "ShowApprovedItemsCheckBox"
})   

0
Tsvetina
Telerik team
answered on 11 Mar 2019, 01:32 PM
Hi Kevin,

With this configuration, the filtering is taking place on the server, in the GlobalAgendaDashboardGridDataSource method. If you are following our examples, this method accepts a parameter of type DataSourceRequest. You could loop through the Filters collection in this parameter and replace the filter values with ones that are properly formatted to match your data. 

For example, you can declare a recursive function that traverses all filters and replaces the filter values:
private void RecurseFilterDescriptors(IList<IFilterDescriptor> requestFilters)
{
    foreach (var filterDescriptor in requestFilters)
    {
        if (filterDescriptor is FilterDescriptor)
        {
            var currentDescriptor = (FilterDescriptor)filterDescriptor;
           // you can have a check on the Member name if you need to replace values only for a given field
            if (currentDescriptor.Member == "ShipCity" && currentDescriptor.Value.GetType() == typeof(string)) {
                currentDescriptor.Value = currentDescriptor.Value.ToString().Replace(" ", "%20");
            }
        }
        else if (filterDescriptor is CompositeFilterDescriptor)
        {
            var descriptor = (CompositeFilterDescriptor)filterDescriptor;
            RecurseFilterDescriptors(descriptor.FilterDescriptors);
        }
    }
}

Then, you can call this method on the request param before passing it to ToDataSourceResult():
public ActionResult Orders_Read([DataSourceRequest]DataSourceRequest request)
{
    var formattedRequest = ReplaceFilterValues(request);
    var result = Enumerable.Range(0, 50).Select(i => new OrderViewModel
    {
        OrderID = i,
        Freight = i * 10,
        OrderDate = DateTime.Now.AddDays(i),
        ShipName = "ShipName " + i,
        ShipCity = "ShipCity " + i
    });
 
    return Json(result.ToDataSourceResult(request));
}


Regards,
Tsvetina
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Kevin
Top achievements
Rank 1
answered on 12 Mar 2019, 09:33 PM

Thank you. This has been most helpful. My problem now is that this line

result.ToDataSourceResult(request)

seems to return an empty result. I look at result and in my case it has 14 rows, but the result of the above is zero rows?

 

0
Tsvetina
Telerik team
answered on 14 Mar 2019, 03:20 PM
Hi Kevin,

Are you applying filtering logic of your own on the data? The ToDataSourceResult also applies filtering and there could be a conflict between the two queries. Could you show us what your Read controller method currently looks like? 

If you want to apply filtering of your own, make sure you do not call ToDataSourceResult afterwards. You can see such an example in this demo:
Grid / Custom ajax binding

Regards,
Tsvetina
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Kevin
Top achievements
Rank 1
answered on 14 Mar 2019, 04:59 PM

The Read property in defining the grid looks like:

.Read(read => read.Action("GlobalAgendaDashboardGridDataSource", "Home"))

In that data source I get the filtered data

                var filteredData = (IList<GlobalAgendaDashboardGridViewModel>)result.ToDataSourceResult(filterOnly).Data;
Then do a lot of manipulation of the filtered data then finally

                var dsResult = filteredData.ToDataSourceResult(request);
                var rawResult = new
                {
                    dsResult.AggregateResults,
                    dsResult.Data,
                    dsResult.Errors,
                    dsResult.Total,
                    needsApproval
                };
                return Json(rawResult);

I guess I am not sure how to turn this into the proper response if I am not suppose to call ToDataSourceResult again?

 

Thanks again for the support.

0
Tsvetina
Telerik team
answered on 15 Mar 2019, 02:42 PM
Hi Kevin,

It depends on the manipulation that you do on the data. If you are able to take care of applying the sorting and paging, too (if your Grid uses them), you can just create a new DataSourceResult object and populate it with the result:
var rawResult = new DataSourceResult()
{
    Data = filteredData.Data,
    Total = filteredData.Total
};
 
return Json(rawResult);


Regards,
Tsvetina
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Kevin
Top achievements
Rank 1
answered on 15 Mar 2019, 05:56 PM

Thanks again.

What do I fill in for Aggregates? And added properties? Currently I do

                var rawResult = new
                {
                    dsResult.AggregateResults,
                    dsResult.Data,
                    dsResult.Errors,
                    dsResult.Total,
                    needsApproval
                };

I understand inserting

    Data = filteredData.Data,
    Total = filteredData.Total

How do I mimic the current behavior that includes an Errors property, an AggregateResults, and a custom property 'needsApproval'.

 

 

0
Tsvetina
Telerik team
answered on 19 Mar 2019, 11:43 AM
Hello Kevin,

If you also need to apply aggregates and possibly other data operations, you either need to revert to using ToDataSourceResult on the data or you need to calculate and pass these values manually.

To keep using ToDataSourceResult, you can try deleting the filter before calling the method a second time:
var filters = request.Filters;
RecurseFilterDescriptors(filters);
var filterRequest = new DataSourceRequest { Filters = filters };
var filteredData = (IList<GlobalAgendaDashboardGridViewModel>)result.ToDataSourceResult(filterRequest).Data;
// do subsequent manipulation
 
request.Filters = null;
var dsResult = filteredData.ToDataSourceResult(request);
return Json(dsResult);



Regards,
Tsvetina
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
Tags
Grid
Asked by
Kevin
Top achievements
Rank 1
Answers by
Kevin
Top achievements
Rank 1
Tsvetina
Telerik team
Share this question
or