Feature request: force page even when not currently available

Feature request: force page even when not currently available

burbur Posts: 30Questions: 2Answers: 2

I can't figure out how to post a discussion in a specific category, sorry. As a sidenote, I posted this request before, but it was somehow deleted. I don't understand why or how, I don't think it was breaking any forum rules, maybe I deleted it myself by accident.

Sometimes the number of pages can increase, e.g. when using search() or page.len(). If you combine this with page(), it can force the table back to page 1 even though the requested page is available after drawing the table, e.g. myTable.page.len(10).page(2).draw(false).
In some cases, you know ahead of time that the requested page is actually available, e.g. when restoring a history.state, and so it might be useful to force a specific page even though it's not available in the current state of the table.

This could be done either by providing something like "full-force" as a parameter for draw, or an optional boolean parameter for page (depending on how DataTables works internally, I guess).

I know it's possible to use stateSave, but I've implemented custom state saving because I want to save one page length across all tables, ordering per "type" of table (each table type may appear under several URLs), and everything else simply by query string.

Replies

  • rf1234rf1234 Posts: 3,079Questions: 89Answers: 427

    I know it's possible to use stateSave, but I've implemented custom state saving because I want to save one page length across all tables, ordering per "type" of table (each table type may appear under several URLs), and everything else simply by query string.

    If state saving could solve your issue, then having implemented a custom state saving does not prevent you from saving more data, e.g. your page number. I do this with the page width for example (wide screen or regular width). I just defined this as an extra item and it is saved in local storage or even a database (if the user chooses to have this saved in a database).

  • allanallan Posts: 64,230Questions: 1Answers: 10,599 Site admin

    As a sidenote, I posted this request before, but it was somehow deleted

    Caught in the auto spam detection. Sorry - it does happen sometimes. We get a lot of spam attempts so there will be some false positives.

    Funnily enough there is another thread at the moment about making sure that a page changes if there are no records on the current page. That I consider to be a bug in the server-side processing protocol.

    I'm not sure I fully understand the utility of this. You note that the request page is available - that being the case, I don't see the issue with drawing it?

    It isn't something I've been asked for before, and at the moment I think it is unlikely that I would implement this in DataTables, but I have been persuaded before :).

    Allan

  • kthorngrenkthorngren Posts: 21,840Questions: 26Answers: 5,049
    edited March 6

    it can force the table back to page 1 even though the requested page is available after drawing the table, e.g. myTable.page.len(10).page(2).draw(false)

    Use the page parameter of draw() to stay on the same page when using page(). See the last example in the docs.

    You mentioned above to use page so I'm not sure if this isn't working for your specific solution. Can you provide a simple test case to show the issue you are having so we can offer more specific suggestions?
    https://datatables.net/manual/tech-notes/10#How-to-provide-a-test-case

    Kevin

  • rf1234rf1234 Posts: 3,079Questions: 89Answers: 427

    In addition to Kevin's remark this plug in might also be relevant for you:
    https://datatables.net/plug-ins/api/row().show()

    Here I auto-select a newly created or edited row and then jump to the page the row is on using "row().show()"

  • burbur Posts: 30Questions: 2Answers: 2

    If state saving could solve your issue, then having implemented a custom state saving does not prevent you from saving more data, e.g. your page number. I do this with the page width for example (wide screen or regular width). I just defined this as an extra item and it is saved in local storage or even a database (if the user chooses to have this saved in a database).

    Thanks for the suggestion. Now that I've tried it out, it doesn't seem like stateSave can solve the issue. It doesn't care about the query string, so it doesn't save a state for each history item.

  • rf1234rf1234 Posts: 3,079Questions: 89Answers: 427
    edited March 6

    To be honest I haven't really understood your issue. Could you make a test case? Or maybe the new posts above already have a solution for you?!

    Now that I've tried it out, it doesn't seem like stateSave can solve the issue. It doesn't care about the query string, so it doesn't save a state for each history item.

    I really don't understand this sufficiently but let me give it a try: In my opinion it shouldn't be a problem to save an array of history items (whatever that is) in the saved state. You should be able to JSON.stringify the data and then to JSON.parse them when reading them back.

  • burbur Posts: 30Questions: 2Answers: 2

    Oh, missed a few posts before my previous comment.

    I tried to reproduce the issue on live.datatables.net without success. Note that I'm using serverSide, although I'm not 100% that this doesn't also apply to static tables.

    I also asked a question on SO about this, maybe that explains it better.

    I'm not sure I fully understand the utility of this. You note that the request page is available - that being the case, I don't see the issue with drawing it?

    The page is available after the table is drawn, but not before. So at the moment you call draw, the page is not available, causing it to be reset to page 1. Then after the draw is complete, the page is available.

    I really don't understand this sufficiently but let me give it a try: In my opinion it shouldn't be a problem to save an array of history items (whatever that is) in the saved state. You should be able to JSON.stringify the data and then to JSON.parse them when reading them back.

    Sorry, by "history item" I mean a state created with history.pushState or history.replaceState. Using the popstate event you can then restore data to the table from history.state. In the SO question linked above you can see how I'm using this.

  • kthorngrenkthorngren Posts: 21,840Questions: 26Answers: 5,049
    edited March 6

    In the SO post it looks like you are using custom code for saving the Datatable's state. If you want to save the Datatables state why not use stateSave?

    You can create a server side processing test case using one of the templates found here.

    I'm not familiar with history.pushState or history.replaceState. If you don't want to use stateSave then please provide a test case showing what you currently have so we can help debug.
    https://datatables.net/manual/tech-notes/10#How-to-provide-a-test-case

    Kevin

  • burbur Posts: 30Questions: 2Answers: 2
    edited March 6

    I finally managed to recreate the issue: https://live.datatables.net/pamitavu/1/

    It does appear to only be an issue with serverSide, not with static tables.

    It also appears to occur specifically when using search, see the following test case using page.len where it doesn't occur: https://live.datatables.net/xemeqine/1/

  • burbur Posts: 30Questions: 2Answers: 2
    edited March 6

    I should add that in my case it also occurs when the search filter doesn't change, probably because I'm adding custom filters to the data that further modify the result set, like this (simplified):

    function addAjaxData(data) {
        Object.assign(data, getMyData());
    }
    
    myTable = new DataTable("#myTable", {
        serverSide: true,
        data: {
            ajax: "/get_data/",
            data: function(data) { addAjaxData(data); },
        },
        // other options...
    });
    
  • kthorngrenkthorngren Posts: 21,840Questions: 26Answers: 5,049
    edited March 6

    Now it makes sense what you mean by The page is available after the table is drawn, but not before.. Yes, you will need to call draw() to execute the server side processing search() before going to a page that is available in the result set. See this updated test case:
    https://live.datatables.net/pamitavu/2/edit

    This is because the response to table.search("haley").draw(); indicates there is only one page of data available and this is all the client side Datable knows about. The second search clears the search term and the response indicates there are multiple pages available. This then allows for page() to go to page 2. Yes, it does cause an extra Ajax request for this case.

    I don't believe there is anything built into Datatables to address this particular case. Sounds like that is the feature you are looking for.

    Kevin

  • burbur Posts: 30Questions: 2Answers: 2

    Yes, exactly. Calling draw twice also causes a visible "jump" from page 1 to the requested page, which is undesirable.
    It would be nice if it was possible to be able to get to the desired page immediately instead, even if that could potentially cause loading a non-existent page if used unwisely (or if the data on the server changes significantly).
    In that case, it would ideally behave the same as when you request a non-existent page at initialization, i.e. not throw an error but show an empty table with all pagination buttons inactive.

  • burbur Posts: 30Questions: 2Answers: 2
    edited March 6

    edit: Sorry, I feel like I'm just complicating things unnecessarily. The last few comments are probably sufficient for @allan to decide if and how to implement the proposed feature.

  • burbur Posts: 30Questions: 2Answers: 2

    In case it helps, I'm posting my specific use case here. I won't spam this thread any further after this, don't worry.

    This is my current setup, which works quite nicely, but is a bit hacky:

        let init = true, order_click = false, popstate = false, redraw = false, redraw_done = false, predraw = false;
            // preDraw fires twice, before and after the ajax request completes, except at init.
            filterTable.on("preDraw", function() {
                if (!predraw) { // this is the first event
                    // No need to set state or update form on popstate
                    if (!popstate) {
                        setState(init, redraw_done); // replace state on init & when redrawing
                        
                        // Init or order header click: update form
                        if (init || order_click) {
                            choices["order"].setChoiceByValue(orderToString(getTableOrder()));
                            
                            if (order_click) {
                                setFilterButtonDisabled();
                                order_click = false;
                            }
                        }
                    }
                    else popstate = false;
                    
                    // Determine if we need to redraw. Also redraw on init so that the behaviour is consistent when navigating.
                    let page = loadTablePage();
                    redraw = page > 1 && page > filterTable.page.info().pages;
                }
                
                if ((predraw || init) && redraw) { // This is the second event OR init
                    redraw = false;
                    
                    let page = loadTablePage();
                    if (page > filterTable.page.info().pages) page = 1;
                    
                    // make sure we don't just reload the same page
                    if ((page-1) != filterTable.page()) {
                        redraw_done = true;
                        if (predraw) predraw = false; // reset predraw, we need to do this here also because we're returning here.
                        else init = false; // init cannot be true if predraw is true
                        
                        filterTable.page(page-1).draw("page");
                        return false; // cancel first draw
                    }
                }
                
                redraw_done = false;
                if (predraw) predraw = false; // reset predraw
                else if (!init) predraw = true; // capture the second event
                else init = false; // init cannot be true if predraw is true
            });
    

    If I could force the page, I could eliminate the whole redraw process, preventing double ajax requests and visible page jumping. It would be fine to have it fail gracefully, when the page actually doesn't exist, to an empty table, just as it does now when first initializing the table:

        let init = true, order_click = false, popstate = false, predraw = false;
            // preDraw fires twice, before and after the ajax request completes, except at init.
            filterTable.on("preDraw", function() {
                if (!predraw) { // this is the first event
                    // No need to set state or update form on popstate
                    if (!popstate) {
                        setState(init); // replace state on init
                        
                        // Init or order header click: update form
                        if (init || order_click) {
                            choices["order"].setChoiceByValue(orderToString(getTableOrder()));
                            
                            if (order_click) {
                                setFilterButtonDisabled();
                                order_click = false;
                            }
                        }
                    }
                    else popstate = false;
    
                    if (init) init = false;
                    else predraw = true;
                }
                else predraw = false;
            });
    
  • allanallan Posts: 64,230Questions: 1Answers: 10,599 Site admin
    table.search("").page(2).draw(false);
    

    Yup, that's clarified things for me as well. It's this that is causing the issue. The search action causes the number of records to change, thus that check isn't valid.

    I need to have a bit of a think about that... Possibly there should be a flag to say "ignore checks, I know what I'm doing".

    Allan

  • burbur Posts: 30Questions: 2Answers: 2

    Thanks for the response! Yes, a flag like that would be ideal.

  • burbur Posts: 30Questions: 2Answers: 2

    Hi @allan, sorry to bother. Just wondering if you've decided whether or not an "ignore checks" flag for the page function is something you might implement?

  • allanallan Posts: 64,230Questions: 1Answers: 10,599 Site admin

    Apologies, I haven't focused on this yet - juggling too many other balls! I can see the need for this, but I'm not yet sure about how to implement it or expose it via the API.

    Allan

  • burbur Posts: 30Questions: 2Answers: 2

    No worries, thanks for the update!

Sign In or Register to comment.