Skip to content
This repository has been archived by the owner on Apr 2, 2019. It is now read-only.

Clearing search field after filtering and selecting all the results modifies the selection #577

Open
vijeshm opened this issue May 1, 2015 · 1 comment

Comments

@vijeshm
Copy link

vijeshm commented May 1, 2015

I'm using backgrid-filter and backgrid-select-all extensions.

Issue: Filtering the rows and clicking on the select-all header will add the currently filtered items to the selection. When I clear the search field, more items will get added to the selection. This issue can be seen in the complete example section of backgridjs.com as well.

Browsers: Firefox 37.0.2, Chrome 42.0.2311.90 (64-bit)

Steps to reproduce (Screenshots taken from Firefox 37.0.2):

  1. Initial state.
    screen shot 2015-05-01 at 9 16 07 pm
  2. I filter the rows using the query 'us', and I get a bunch of results.
    screen shot 2015-05-01 at 9 14 56 pm
  3. I select all of the filtered rows by clicking on the select-all header cell.
    screen shot 2015-05-01 at 9 15 21 pm
  4. I clear the result. Now, you see that all the rows are selected. (page 1)
    screen shot 2015-05-01 at 9 15 41 pm
  5. I head over to the next page. All the rows are selected over there as well. (page 2)
    screen shot 2015-05-01 at 9 15 54 pm

As far as I know, the issue is with the clear function of backgrid-filter. This is just resetting the collection with the shadow collection. This will generate a backgrid:refresh event. At this stage in the steps to reproduce, the header will still be selected. The corresponding event handler in backgrid-select-all will check for the status of the header cell. Since it is checked, the handler will trigger a backgrid:select event on all the models in the collection. This will select all the models, irrespective of whether or not it appeared in the filtered rows.

An approach to solve the issue:
We could uncheck the header cell in the clear function (but not raise a change event, since this'll cause all the selections in the filtered rows to get unselected). But ofcourse, the filter extension must work independently of select-all extension. i.e, filter extension doesnt know the existence of select-all extension. However, what we could do is, to provide an option to override the clear function in the initialize method with options.clear. When passing the configuration during initialization of backgrid-filter, the user can pass a function that unchecks the header cell and then calls apply on the Backgrid.Extension.ClientSideFilter's clear method.
For example,

var searchFilter = new Backgrid.Extension.ClientSideFilter({ clear: function() {
  // uncheck the header cell

  // apply clear on client side filter's prototype with 'this' context
  Backgrid.Extension.ClientSideFilter.prototype.clear.apply(this);
}})

I've tried it on my local environment and it works as expected. Please do let me know if this is a good approach to solve the issue, and I'll send a pull request.

The reason that I'm posting backgrid issues list is that the bug surfaces only when both the extensions (filter and select-all) are used together.

@sasfeat
Copy link

sasfeat commented Sep 11, 2015

@vijeshm ,
I've tried to add clear function at the initialization, but it still doesn't work. I think that I did not understand the part of changing the code in the initialize method. What should I really change there?

define([//'appModels/appCollection',
        //'appModels/appModel',
        //'appViews/appView',
        //'appControllers/appController',
        //'appModels/appGrid',
        'jquery',
        'backbone',
        'backgrid',
        'backgrid_filter',
        'backgrid_selector'],

        function($,Backbone,Backgrid){
            var Application = (function(){
                //var appController;
                var appCollection;
                //var appView;
                var filter;
                var columns;
                var appModel;
                var appGrid;
                var baseUrl = "data/territories.json";
                var module = function(){
                    self = this
                };
                module.prototype = {
                    constructor : module,
                    init:function(){
                        self.initModel();
                        self.initCollection();
                        self.initColumns();
                        self.initFilter();
                        self.initSelector();
                        self.initGrid();
                    },
                    initModel:function(){
                        appModel = Backbone.Model.extend({});

                    },
                    initCollection:function(){
                        var baseCollection = Backbone.Collection.extend({
                            model:appModel,
                            url:baseUrl
                        });
                        appCollection = new baseCollection();

                    },
                    initColumns:function(){
                        columns = [{
                            name: "id", // The key of the model attribute
                            label: "ID", // The name to display in the header
                            editable: false, // By default every cell in a column is editable, but *ID* shouldn't be
                            // Defines a cell type, and ID is displayed as an integer without the ',' separating 1000s.
                            cell: Backgrid.IntegerCell.extend({
                              orderSeparator: ''
                            })
                          }, {
                            name: "name",
                            label: "Name",
                            // The cell type can be a reference of a Backgrid.Cell subclass, any Backgrid.Cell subclass instances like *id* above, or a string
                            cell: "string" // This is converted to "StringCell" and a corresponding class in the Backgrid package namespace is looked up
                          }, {
                            name: "pop",
                            label: "Population",
                            cell: "integer" // An integer cell is a number cell that displays humanized integers
                          }, {
                            name: "percentage",
                            label: "% of World Population",
                            cell: "number" // A cell type for floating point value, defaults to have a precision 2 decimal numbers
                          }, {
                            name: "date",
                            label: "Date",
                            cell: "date"
                          }, {
                            name: "url",
                            label: "URL",
                            cell: "uri" // Renders the value in an HTML anchor element
                        }];
                    },
                    initGrid:function(){
                        appGrid = new Backgrid.Grid({
                            columns:columns,
                            collection:appCollection
                        });
                        appView = Backbone.View.extend({
                            el:$("#grid"),
                            initialize:function(){

                                //this.$el.append(appGrid.render().$el);
                                this.$el.append(selectorGrid.render().$el);
                                appCollection.fetch({reset:true});
                            }

                        });
                        new appView()
                        //$("#mainTable").append(appGrid.render().$el);
                    },
                    initFilter:function(){
                        filter = new Backgrid.Extension.ClientSideFilter({
                            collection:appCollection,
                            fields:['name'],
                            clear:function(){
                                Backgrid.Extension.ClientSideFilter.prototype.clear.apply(this);
                            }
                        });
                        filterView = Backbone.View.extend({
                            el:$('#searchbar'),
                            initialize:function(){
                                this.$el.append(filter.render().$el);
                                $(filter.$el).css({float:'left',margin:"20px"})
                            }
                        });
                        new filterView();


                    },
                    initSelector:function(){
                        selectorGrid = new Backgrid.Grid({
                            columns:[{
                                name:"",    //empty name field
                                cell:"select-row",
                                headerCell: "select-all"
                                }].concat(columns),
                            collection:appCollection    
                        });

                    },  
                }
                return module;

            })();
        return Application;
});

Here I use it in the initFilter function:

initFilter:function(){
                        filter = new Backgrid.Extension.ClientSideFilter({
                            collection:appCollection,
                            fields:['name'],
                            clear:function(){
                                Backgrid.Extension.ClientSideFilter.prototype.clear.apply(this);
                            }
                        });
                        filterView = Backbone.View.extend({
                            el:$('#searchbar'),
                            initialize:function(){
                                this.$el.append(filter.render().$el);
                                $(filter.$el).css({float:'left',margin:"20px"})
                            }
                        });
                        new filterView();

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants