Multi Filter checkbox for Dropdown

8 posts, 0 answers
  1. Barry
    Barry avatar
    6 posts
    Member since:
    Nov 2014

    Posted 23 Jul 2015 Link to this post

     

    I have a grid in which one of the columns is a dropdown. I'd like to enable the Multi Filter checkboxes for this column.

    How can I do this?  If I set Multi to true it just displays the usual dropdown filter menu.

     

  2. Barry
    Barry avatar
    6 posts
    Member since:
    Nov 2014

    Posted 23 Jul 2015 in reply to Barry Link to this post

    Apologies - please delete this post.

     

  3. UI for ASP.NET MVC is VS 2017 Ready
  4. Barry
    Barry avatar
    6 posts
    Member since:
    Nov 2014

    Posted 23 Jul 2015 in reply to Barry Link to this post

    Ok I am not actually this crazy normally... I thought I had got this working but that is not the case. 

    The Multi Filter Checkboxes now appear for the dropdown columns however, they contain ALL possible dropdown values and not just the values that are in the grid. 

    Is there anyway to restrict this so that it behaves like the other multi filter checkboxes?

  5. Georgi Krustev
    Admin
    Georgi Krustev avatar
    3706 posts

    Posted 27 Jul 2015 Link to this post

    Hello Barry,

    In general, you will need to enable the "Multi" option, telling the grid to use MultiCheck widget as a filter menu for the particular column. You can see this in our online "Filter multi checkboxes" demo:
    In general, the MultiCheck widget will filter the items internally in order to display distinct ones. If you are using a remote binding, then you will need to return the unique values manually. Please refer to the "Filter_Multi_CheckboxesController.cs" file, in the aforementioned online demo.

    Regards,
    Georgi Krustev
    Telerik
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
  6. Barry
    Barry avatar
    6 posts
    Member since:
    Nov 2014

    Posted 27 Jul 2015 in reply to Georgi Krustev Link to this post

    Hi Georgi,

    I am using remote binding, however I need the Filter Checkboxes datasource to be re-bound any time the grid is filtered. To complicate matters further, I have pagination enabled on the grid. So I need the filter menus to use distinct values from the entire dataset, not just page 1, and to also take in to account any filtering already applied to the grid.

    Thanks

     Barry

     

  7. Georgi Krustev
    Admin
    Georgi Krustev avatar
    3706 posts

    Posted 29 Jul 2015 Link to this post

    Hello Barry,

    Basically, when widget uses remote binding, then the server will need to handle the filtration, pagination and etc. That being said, the MultiCheck will send the required parameters to the server and the service in question should return only the correct data set.

    The local binding, requires to update the widget manually as it is shown in this Dojo demo.

    If I am missing something in the question or you are experiencing any difficulties with the implementation, please send us a repro demo that demonstrates the issue. Thus we will be able to review it locally and advice you further.

    Regards,
    Georgi Krustev
    Telerik
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
  8. Barry
    Barry avatar
    6 posts
    Member since:
    Nov 2014

    Posted 29 Jul 2015 in reply to Georgi Krustev Link to this post

    I figured out how I needed to do this. So for anyone else who may have the problem... here is my solution.

     I added a custom data attribute to my grid headers to identity whether my custom multi filter should be used:

    .HeaderHtmlAttributes(new { data_multi_filter = ​"True" })

    I then hooked up to the FilterMenuInit event on the grid:

    .Events(ev => ev.FilterMenuInit("GridFilterMenuInitialized")

     My JS Function for FilterMenuInit looks like this:

     

    function GridFilterMenuInitialized(e) {
       
       
            var popup = e.container.data("kendoPopup");
            var parent = popup.options.anchor.parent();
            var fieldName = parent.data("field");
            var gridDataSource = this.dataSource;
            var currentFilters;
     
            if (gridDataSource.filter())
            {
                // Transform the filters that have been applied in the grid to a long string
                currentFilters = gridDataSource.transport.parameterMap({filter: gridDataSource.filter()}).filter;
            }     
         
            if (parent.data("multi-filter") == "True")
            {
                // Only do this for multi-filters
                // Leave the default filter for everything else
                 
                // Grab an element in the DOM that we can attach our listview too
                var helpTextElement = $(e.container).children(":first").children(":first");
           
                // Destroy and remove any Kendo List View widget already created for this field
                var isCreated = $('#lst'+fieldName)
                if (isCreated.data("kendoListView"))
                {
                     
                    isCreated.data("kendoListView").destroy();
                     
                }
                if (isCreated)
                {
                    isCreated.remove();
                }
     
                // Create an instance of a listview
                // We don't actually bind to anything here
                // It is done on the fly in the popup open event (see further down)
                var element = $("<div id='lst" + fieldName + "' class='checkbox-container'></div>").insertAfter(helpTextElement).kendoListView({
                        dataSource: null
                         
                    });
                 
     
                // Hide all those other dropdown and filtering stuff
                $('.checkbox-container').siblings("span").hide()
                $('.checkbox-container').siblings("input").hide()
     
     
                // Hook up the Filter event
                e.container.find("[type='submit']").click(function (e) {
                    e.preventDefault();
                    e.stopPropagation();
                 
                    // Apply whatever have been checked in the checkbox list to the grid
                    var filter =gridDataSource.filter() || { logic: "and", filters: [] };
                    var fieldFilters = $.map($(element).find(":checkbox:checked"), function (input) {
                        return {
                            field: fieldName,
                            operator: "eq",
                            value: input.value
                        };
                    });
                    if (fieldFilters.length) {
                        removeFiltersForField(filter, fieldName);
                        filter.filters.push({
                            logic: "or",
                            filters: fieldFilters
                        });
                        gridDataSource.filter(filter);
                    }
                
                    popup.close();
     
                });
     
                // Hookup the Clear button event
                e.container.find("[type='reset']").click(function (e) {
                    e.preventDefault();
                    e.stopPropagation();
                 
                    // clear whatever have been checked in the checkbox list to the grid
                    var filter =gridDataSource.filter() || { logic: "and", filters: [] };
                    var fieldFilters = $.map($(element).find(":checkbox:checked"), function (input) {
                        return {
                            field: fieldName,
                            operator: "eq",
                            value: input.value
                        };
                    });
                 
                    if (fieldFilters.length) {
                        removeFiltersForField(filter, fieldName);
                        gridDataSource.filter(filter);
                    }
                
                    popup.close();
     
                });
               
                // bind to the popup open event and do some custom work for multifilters
                e.container.data("kendoPopup").bind("open", function(e) {
           
                    if (gridDataSource.filter())
                    {
                        // Transform the filters that have been applied in the grid to a long string
                        currentFilters = gridDataSource.transport.parameterMap({filter: gridDataSource.filter()}).filter;
                    }  
     
                    if (parent.data("multi-filter") == "True")
                    {
                        // This is a multi filter lets do some custom stuff
                        var checkboxesDataSource;
     
                           // It's multi filter
                            checkboxesDataSource = new kendo.data.DataSource({
                                transport: {
                                    read: {
                                        url: '@Url.Action("GetMultiFilterOptionsForColumn")',
                                        dataType: "json",
                                        data: { field: fieldName, currentFilters: currentFilters }
                                    }
                                 
                            });
                        }
              
                        // Grab the current ListView
                        var lst = $("#lst" + fieldName).data("kendoListView");
                        var lstOptions = lst.options;
     
                        // Rebind the datasource
                        // This is important as the current filters and/or fieldname may have changed.
                        lstOptions.dataSource = checkboxesDataSource;
                         
                        // rebind the template
                        lstOptions.template = "<div><input type='checkbox' #: check # value='#: fieldvalue #'/>#: fieldtext #</div>";
                         
                        // Destroy the current listview
                        lst.destroy();
                        // and recreate it with the updated datasource options
                        // this will initiate a .dataSource.transport.read() call to the specified URL
                        $("#lst" + fieldName).kendoListView(lstOptions);
                        $("#lst" + fieldName).css({ "height": "300",
                                                    "width" : "200",
                                                    "overflow" : "scroll"
                                                    });
                    }
                     
                  });
     
            }
     
             
     
     
        }
     
        // Helper function for filtering
        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;
                    }
                });
            }
        }

    In my Controller I have the following method:

     

    public ActionResult GetMultiFilterOptionsForColumn(string field,
               string currentFilters)
           {
               // This gets the possible distinct values for the CURRENT grid items.
     
               currentFilters = currentFilters ?? "";
     
               DataSourceRequest request = new DataSourceRequest();
               request.Filters = Kendo.Mvc.Infrastructure.FilterDescriptorFactory.Create(currentFilters == null ? "" : currentFilters.ToString());
     
              // Here you would call you service/repo and get the data that you need
              // to display dynamically in the filter options.
              // In my case I am returning a list of string values
               var values = _serviceObject.CallMethodToGetAllDistinctDataValues(field, request);
                
               // Construct a new anonymous type to be consumed by the filter menu (converting NULLS to string values)
               var arr = values.Select(c =>
                         new { fieldvalue = (c == null ? "NULL" : c.ToString()),
                  fieldtext = (c == null ? "NULL" : c.ToString()),
                   check = currentFilters.Contains(field) == true ? "checked" : ""
                 }).ToArray();
     
               return Json(arr, JsonRequestBehavior.AllowGet);         
           }

     What happens here is that any column header with the data attribute multi-filter will have a custom checkbox list with the items returned from the controller method. The call to the controller is made when the filter popup opens. There is additional custom code to hook up the Filter and Clear buttons.

    If a column does not have the custom data attribute of multi-filter then the default Kendo Filter is displayed.

     

    I hope this helps someone else, please do let me know if there are any errors or a misunderstanding of how things work!

     

    Thanks

    Barry

     

     

     

     

     

  9. Rami Alshahabi
    Rami Alshahabi avatar
    4 posts
    Member since:
    Aug 2008

    Posted 29 Mar in reply to Barry Link to this post

    Hi Barry

    Excellent work but can you please give us the code for var values = _serviceObject.CallMethodToGetAllDistinctDataValues(field, request);

    Many thanks

     

     

Back to Top
UI for ASP.NET MVC is VS 2017 Ready