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

Sort the values in a multiple select filter

9 Answers 621 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
Carolyn
Top achievements
Rank 1
Carolyn asked on 11 Oct 2018, 07:03 PM

Hi,

I have tried the "work around" for creating a separate datasource on a filterable column in a grid but I'm getting an HTTP 500 response when I execute it.  My controller code is executed but all I see when the filter dialog box opens is a spinning wheel...

Here is the link to the "work around": https://docs.telerik.com/kendo-ui/knowledge-base/sort-multi-check-filter-mvc-grid

My filterable grid column code:

columns.Bound(c => c.WallThickness).Format("{0:0.000}").Filterable(ftb =>
                                                {
                                                    ftb.Multi(true);                                                   
                                                    ftb.DataSource(ds => ds                                                   
                                                    .Custom()  
                                                    .Type("Json")
                                                    .Transport(t => t.Read("SortWallThickness", "Materials"))
                                                    .Schema(s => s
                                                    .Data("Data"))
                                                    .Sort(sort => { sort.Add("WallThickness").Ascending();
                                                    })
                                                    );
                                                })

 

My controller code:

public ActionResult SortWallThickness([DataSourceRequest] DataSourceRequest request)
        {
            List<string> data = WeldObject.GetWT(0);
            return Json(new[] { data }.ToDataSourceResult(request, ModelState));
        }

 

Appreciate your help,

Carolyn

9 Answers, 1 is accepted

Sort by
0
Tsvetina
Telerik team
answered on 15 Oct 2018, 01:08 PM
Hi Carolyn,

Could you try setting the DataSource type to "aspnetmvc-ajax":
columns.Bound(c => c.WallThickness).Format("{0:0.000}").Filterable(ftb =>
   {
       ftb.Multi(true);                                                   
       ftb.DataSource(ds => ds                                                   
       .Custom()  
       .Type("aspnetmvc-ajax")
       .Transport(t => t.Read("SortWallThickness", "Materials"))
       .Schema(s => s
       .Data("Data"))
       .Sort(sort => { sort.Add("WallThickness").Ascending();
       })
       );
   })

This will ensure that the sort parameters will be passed in the correct format to the server. 

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
Carolyn
Top achievements
Rank 1
answered on 15 Oct 2018, 01:57 PM

Hi Tsvetina,

Unfortunately that did not work...I'm still getting the HTTP 500 error saying it cannot find the transport code "SortWallThickness" (upon inspection in the browser).

The weird thing is, when I run the debugger, I can see that the SortWallThickness code is executed.  Could there be something wrong with that code - specifically returning Json?

 

public ActionResult SortWallThickness([DataSourceRequest] DataSourceRequest request)
  {
            List<string> data = WeldObject.GetWT(0);
            return Json(new[] { data }.ToDataSourceResult(request, ModelState));
 }

 

Thanks for your help,

Carolyn

0
Tsvetina
Telerik team
answered on 16 Oct 2018, 01:37 PM
Hello Carolyn,

I tried an identical column definition and it works on my side:
    @(Html.Kendo().Grid<TelerikMvcApp32.Models.OrderViewModel>()
.Name("grid")
.Columns(columns =>
{
    columns.Bound(p => p.OrderID).Filterable(false);
    columns.Bound(p => p.OrderDate).Format("{0:MM/dd/yyyy}");
    columns.Bound(c => c.Freight).Format("{0:0.000}").Filterable(ftb =>
    {
        ftb.Multi(true);
        ftb.DataSource(ds => ds
        .Custom()
        .Type("aspnetmvc-ajax")
        .Transport(t => t.Read("Orders_Read", "Grid"))
        .Schema(s => s
        .Data("Data"))
        .Sort(sort =>
        {
            sort.Add("Freight").Ascending();
        })
        );
    });
    columns.Bound(p => p.ShipName).Width(150);
    columns.Bound(p => p.ShipCity).Width(150);
})
.Pageable()
.Sortable()
.Scrollable()
.Filterable()
.HtmlAttributes(new { style = "height:550px;" })
.DataSource(dataSource => dataSource
.Ajax()
.PageSize(20)
.Read(read => read.Action("Orders_Read", "Grid"))
)
)

public ActionResult Orders_Read([DataSourceRequest]DataSourceRequest 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));
}


Could you show me your full Grid declaration with the latest changes and a screenshot of the error that you see in the browser console? Also, check if there is any additional information in the Response of the failed request (you can inspect it in the Network tab in the browser developer tools).

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
Carolyn
Top achievements
Rank 1
answered on 16 Oct 2018, 02:25 PM

Hi Tsvetina,

Thank you for all your help.  In looking at how you coded your ActionResult I was able to get it to work.  

Here is how I changed my controller side code following your example:

 public ActionResult SortWallThickness([DataSourceRequest] DataSourceRequest request)
        {
            List<string> data = WeldObject.GetWT(0);
           
            var result = data.Select(x => new WallThicknessVM()
            {
                WallThickness = Convert.ToDouble(x)
            });
            return Json(result.ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
         
        }

 

Basically I created a view model with just the property WallThickness in it and I added the JsonRequestBehavior.AllowGet in the return statement.

Much Thanks,

Carolyn

0
Carolyn
Top achievements
Rank 1
answered on 17 Oct 2018, 08:57 PM

Hello this is a new question related to the above question:

When I call the .Transport() function as documented above, is there a way to pass in a parameter?  

I would like the parameter to be the filter selections from a different "filterable" column.

Is this possible?  

My goal is to limit the values in a filter based on other column filter selections...

0
Tsvetina
Telerik team
answered on 18 Oct 2018, 01:02 PM
Hello Carolyn,

To filter the current column data based on other columns, it is better to declare a standalone DataSource, so you can access and filter it when there is a new filter applied in the Grid.
I am attaching a sample project, where the multi-checkbox filter DataSource is filtered on Grid filter. In this specific example, a single DataSource is shared between all columns that have multi-checkbox filters, but the same approach is applicable with multiple DataSources, too.

The main logic follow below:

@(Html.Kendo().DataSource<TelerikMvcApp3.Models.OrderViewModel>()
        .Name("dataSource1")
        .Ajax(dataSource => dataSource
        .Read(read => read.Action("Orders_Read", "Grid"))
        .Sort(sort=>sort.Add("ShipName").Ascending())
        )
)
<div class="container-fluid">
    <div class="row">
        <div class="col-xs-18 col-md-12">
            @(Html.Kendo().Grid<TelerikMvcApp3.Models.OrderViewModel>()
        .Name("grid")
        .Columns(columns =>
        {
            columns.Bound(p => p.OrderID).Filterable(false);
            columns.Bound(p => p.Freight).Filterable(f => f.Multi(true).DataSource("dataSource1"));
            columns.Bound(p => p.OrderDate).Format("{0:MM/dd/yyyy}");
            columns.Bound(p => p.ShipName).Filterable(f => f.Multi(true).DataSource("dataSource1"));
            columns.Bound(p => p.ShipCity).Filterable(f => f.Multi(true).DataSource("dataSource1"));
        })
        .Pageable()
        .Sortable()
        .Scrollable()
        .Filterable()
        .Events(e=>e.Filter("onFilter"))
        .HtmlAttributes(new { style = "height:550px;" })
        .DataSource(dataSource => dataSource
             .Ajax()
             .PageSize(20)
             .Read(read => read.Action("Orders_Read", "Grid")))
         )
        </div>
    </div>
</div>
<script>
    function onFilter(e) {
        var dataSource = this.dataSource,
            newFilters = e.filter,
            currentFilters = dataSource.filter(),
            field = e.field;
 
        // if filters are cleared, clear them from the external DataSource, too
        if (!newFilters) {
            removeFiltersForField(currentFilters, field);
            dataSource1.filter(currentFilters);
            return;
        }
        // if new filters are aplied and there are existing filters in the DataSource, join them
        if (currentFilters && currentFilters.filters) {
            removeFiltersForField(currentFilters, field);
            currentFilters.filters.push(newFilters);
        }
            // if there are no existing filter, build the initial Grid filter expression
        else {
            currentFilters = { logic: "or", filters: [newFilters] };
        }
 
        dataSource1.filter(currentFilters);
    }
    function removeFiltersForField(expression, field) {
        if (expression.filters) {
            expression.filters = $.grep(expression.filters, function (filter) {
                removeFiltersForField(filter, field);
                if (filter.filters) {
                    return filter.filters.length;
                } else {
                    return filter.field != field;
                }
            });
        }
    }
</script>


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
Abhishek
Top achievements
Rank 1
answered on 26 Mar 2019, 10:15 AM

Thanks, this was helpful. I do have 1 question though regarding the values repeating. Can we somehow make values unique in filter menu pop up, like it is when we are not using external datasource. See attached the image from your solution. Basically I changed your code to have only shipname and not shipname1,2,3., Can we make shipname appear only once ?

 

 

0
Abhishek
Top achievements
Rank 1
answered on 26 Mar 2019, 10:42 AM

Also the field I would like to apply custom sorting (by implementing IComparer or something), is that possible ? currently in your code you have 

"DataSource<TelerikMvcApp3.Models.OrderViewModel>()
        .Name("dataSource1")
        .Ajax(dataSource => dataSource
        .Read(read => read.Action("Orders_Read", "Grid"))
        .Sort(sort=>sort.Add("ShipName").Ascending())
        )"

is it possible to have sort here implement my sorting?

 

0
Tsvetina
Telerik team
answered on 27 Mar 2019, 02:53 PM
Hello Abhishek,

Yes, you can return unique values if you apply a Distinct operator with a custom comparer in the controller method that returns the checkboxes data. You can see an example in this demo:
Grid / Filter Multi Checkboxes

See how the DataSource of the Server Operations Grid calls the Unique method on the controller and passes it the current column field:
columns.Bound(e => e.Country).Width(220).Filterable(ftb => ftb.Multi(true).ItemTemplate("itemTemplate")
    .DataSource(ds => ds.Read(r => r.Action("Unique", "Grid").Data("{ field: 'Country' }")))
);

The Unique method looks like this:
public ActionResult Unique(string field)
{
    var result = GetEmployees().Distinct(new EmployeeComparer(field));
 
    return Json(result, JsonRequestBehavior.AllowGet);
}

Inside it, you can also apply your custom sorting.

The EmployeeComparer compares two employee models only by the field that is passed to it:
public class EmployeeComparer : IEqualityComparer<EmployeeViewModel>
{
    private string field;
 
    private PropertyInfo prop;
 
    public EmployeeComparer(string field)
    {
        this.field = field;
        prop = typeof(EmployeeViewModel).GetProperty(field);
    }
 
    public bool Equals(EmployeeViewModel x, EmployeeViewModel y)
    {
        var valueX = prop.GetValue(x, null);
        var valueY = prop.GetValue(y, null);
        if (valueX == null)
        {
            return valueY == null;
        }
        return valueX.Equals(valueY);
    }
 
    public int GetHashCode(EmployeeViewModel obj)
    {
        var value = prop.GetValue(obj, null);
        if (value == null)
        {
            return 0;
        }
        return value.GetHashCode();
    }
}

As you probably noticed already, this will work properly for multiple columns only if you declare a separate DataSource in each column, so that you can pass the field name based on the current column.

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
General Discussions
Asked by
Carolyn
Top achievements
Rank 1
Answers by
Tsvetina
Telerik team
Carolyn
Top achievements
Rank 1
Abhishek
Top achievements
Rank 1
Share this question
or