Sort the values in a multiple select filter

10 posts, 0 answers
  1. Carolyn
    Carolyn avatar
    22 posts
    Member since:
    Aug 2018

    Posted 11 Oct 2018 Link to this post

    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

  2. Tsvetina
    Admin
    Tsvetina avatar
    2481 posts

    Posted 15 Oct 2018 Link to this post

    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.
  3. Carolyn
    Carolyn avatar
    22 posts
    Member since:
    Aug 2018

    Posted 15 Oct 2018 in reply to Tsvetina Link to this post

    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

  4. Tsvetina
    Admin
    Tsvetina avatar
    2481 posts

    Posted 16 Oct 2018 Link to this post

    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.
  5. Carolyn
    Carolyn avatar
    22 posts
    Member since:
    Aug 2018

    Posted 16 Oct 2018 in reply to Tsvetina Link to this post

    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

  6. Carolyn
    Carolyn avatar
    22 posts
    Member since:
    Aug 2018

    Posted 17 Oct 2018 in reply to Carolyn Link to this post

    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...

  7. Tsvetina
    Admin
    Tsvetina avatar
    2481 posts

    Posted 18 Oct 2018 Link to this post

    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.
  8. Abhishek
    Abhishek avatar
    6 posts
    Member since:
    Nov 2017

    Posted 26 Mar 2019 in reply to Tsvetina Link to this post

    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 ?

     

     

  9. Abhishek
    Abhishek avatar
    6 posts
    Member since:
    Nov 2017

    Posted 26 Mar 2019 in reply to Tsvetina Link to this post

    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?

     

  10. Tsvetina
    Admin
    Tsvetina avatar
    2481 posts

    Posted 27 Mar 2019 Link to this post

    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.
Back to Top