Displaying active SearchPane filters with saved state

Displaying active SearchPane filters with saved state

ajdurantajdurant Posts: 5Questions: 1Answers: 0

Link to test case: https://live.datatables.net/bijepoqa/2/edit

Description of problem:

I'm using the filterChanged function to update a div to show the currently selected filters from searchPanes. This is similar to the question https://datatables.net/forums/discussion/comment/186843 and in the linked example I have it working and updating as the filter items are selected:

However, I'm also using saveState, so that when the page is reloaded the filters are active, but the list is not populated:

In my filterChanged function I'm getting the selected rows from the table API:

var items = $('.dtsp-searchPane table').DataTable().rows({
    selected: true
}).data().map( (item) => item.display);
document.getElementById('filterItems').innerHTML = count > 0 ? items.join(', ') : '';

But because I'm using a button with searchPanes, the table doesn't exist on page load, only after it's been clicked.

It looks like I should be able to get the selections from the main datatable state: $('#example').DataTable().state().searchPanes.selectionList which in the example contains the same values as the display, but in my actual code these are just ids and not the display values.

Is there a better way to do this? Is there some way to initialise the table in the background without clicking the button? Do I need to use the values from the state selectionList, and somehow look up the display values - if so how do I find the data?

Answers

  • allanallan Posts: 65,740Questions: 1Answers: 10,934 Site admin

    The searchPanes button has a delayInit option which is true by default and means that the SearchPanes instance won't be initialised until the button is pressed. You can set it to false to have it load up immediately: https://live.datatables.net/bijepoqa/5/edit

    That said, it looks like you'll still need to do:

                  let sp = $('#example').DataTable().state().searchPanes;
                  let items = sp && sp.selectionList ? sp.selectionList : [];
    

    The filterChanged doesn't look great at all - triggering when it doesn't need to: https://live.datatables.net/bijepoqa/6/edit .

    SearchPanes is over due for a rewrite - there are too many quirks like this. Hopefully this will get you going tho...

    Allan

  • kthorngrenkthorngren Posts: 22,476Questions: 26Answers: 5,167
    edited May 20

    Is there a better way to do this? Is there some way to initialise the table in the background without clicking the button?

    One option is to use button().trigger() to open the SP window then immediately close it by using JS or jQuery to click the background. Updated test case:
    https://live.datatables.net/bijepoqa/4/edit

    Do I need to use the values from the state selectionList, and somehow look up the display values - if so how do I find the data?

    I'm not sure why you are getting the ID instead of the display value. Likely due to your data structure and how you are handling the data.

    @allan may have better ideas and may be able to help with why you are getting the ID with searchPanes.selectionList.

    EDIT: See he has a better answer :smile:

    Kevin

  • ajdurantajdurant Posts: 5Questions: 1Answers: 0

    Thanks for the quick response.

    The searchPanes button has a delayInit option which is true by default and means that the SearchPanes instance won't be initialised until the button is pressed. You can set it to false to have it load up immediately

    Setting this to false doesn't seem to make a difference. As you note, the table still isn't in the dom, so my existing method doesn't work. And looking at .state().searchPanes the data seems to be there in either case.

    Using .state().searchPanes does seem to be the way forward. I just need to work out how to map the data to the display value.

    For my actual project all the data comes from the server side, and is provided a list of {value: <int>, label: <str>} items for the searchPane.

    When first loaded .state().searchPanes has both panes and selectionList where panes contains all the data I could use to look up, but after changing the selections, only selectionList is in there and I don't know where to look up the data other than my original $('.dtsp-searchPane table').DataTable().rows({selected: true})

    The filterChanged doesn't look great at all - triggering when it doesn't need to

    I did notice this and thought it was a bit strange, but doesn't particularly affect me other than unnecessary processing, so I wasn't too worried.

  • ajdurantajdurant Posts: 5Questions: 1Answers: 0

    And playing about a bit more with reading the state in filterChanged it doesn't seem to update until after that has run. So the state read is old, even when the count has updated:

    I guess I can read the state on first load, then use the other method for future updates. I've also seen it not always updating as the filters change (including the count staying the same number).

  • ajdurantajdurant Posts: 5Questions: 1Answers: 0

    Setting delayInit to false doesn't seem to make a difference. As you note, the table still isn't in the dom, so my existing method doesn't work. And looking at .state().searchPanes the data seems to be there in either case.

    delayInit seems to only be used here: if (dt.init().stateSave || config.delayInit === false) _buttonSourced(dt, node, config); and I'm using stateSave so that will be why it has no effect.

  • ajdurantajdurant Posts: 5Questions: 1Answers: 0
    edited May 21

    Okay, I think I've now got this working, but it's pretty hacky. I can't easily show it in live.datatables.net as the SearchPane data is not using the value/label items.

    function(count) {
        // Update button text
        this.buttons('searchPanes:name').text(this.i18n('searchPanes.collapse', this.context[0].oLanguage.searchPanes !== void 0 ? this.context[0].oLanguage.searchPanes.collapse : this.context[0]._searchPanes.c.i18n.collapse, count));
    
        // Update filter pills
        let sp = this.state().searchPanes;
        if (sp) {
            console.log(sp);
            let item_names = [];
            // Use pane data on initial load, this avoids DOM queries when the panes not yet visible
            // then fallback to DOM query when panes are visible and state data is not available (and selectionList has not yet been updated)
            if (sp.panes) {
                let state_items = sp && sp.selectionList ? sp.selectionList : [];
                for (let pane of state_items) {
                    // lookup pane from sp.panes using column then get the display for each item id in rows
                    let pane_data = sp.panes.find(p => p.id === pane.column);
                    let pane_items = pane_data && pane_data.arrayFilter ? pane_data.arrayFilter.filter(o => pane.rows.includes(o.filter)) : [];
                    item_names.push(...pane_items.map(i => i.display));
                }
            } else {
                item_names = $('.dtsp-searchPane table').DataTable().rows({ selected: true }).data().toArray().map( (item) => item.display );
            }
    
            console.log('filter changed', item_names);
    
            let item_str = item_names.reduce((item_str, item) => item_str + '<span class=\"badge\">' + item + '</span>', '');
            document.getElementById('filterItems').innerHTML = count > 0 ? item_str : '';
        }
    }
    

    I'd prefer not to do the DOM query, but I can't find a way to get the pane data after the panes have been loaded into the DOM. Other than diving into this.context[0]._searchPanes.s.panes which seems like getting too much into the internals.

Sign In or Register to comment.