Tabulator: Tabulator 3.4 - Download & Remote Pagination

Created on 28 Feb 2018  路  10Comments  路  Source: olifolkerd/tabulator

Thanks again for the awesome plugin. I noticed that, whenever I use the Download Table Databuilt-in function in Tabulator 3.4 with a table having remote pagination, the resulting file (be it CSV, JSON or XLSX) only contains the currently available/visible/fetched rows.

I guess this is the expected and legitimate behaviour when remote paging is enabled, since the previous/next/remaining rows haven't been fetched. However, I think that it would be possible to find a way to download all pages as a whole using the downloadDataMutator function and re-fetching all data using the given AjaxURL, stripping the page & size attributes while keeping all the remaining info (filters, sorting & more).

Am I just trying to reinvent the wheel here? Is there a "standard" way to achieve that already?

EDIT: I implemented an hack to do that and made a post about it on this blog: feel free to use it if you need a "download all data" feature!

Question - Ask On Stack Overflow

Most helpful comment

I pursued a different approach, here's my hack:

1) When the "download all data" button is clicked, I fetch all values from the server (using the same WS which I did to populate the table) and store them within a allData global variable:

    $("a#download_XLSX_all").click(function (e) {
        e.stopPropagation();
        e.preventDefault();
        $.ajax({
            url: '/ws_to_fetch_all_data/',
        }).done(function (data) {
            // store the whole data into a global variable
            allData = data.data;
            $("div.tabulator").tabulator("download", "xlsx", "data.xlsx");
        });
    });

2) Within the Tabulator's downloadDataMutator method, I replace the "current" (paginated) table data with the allData global variable (if present/not null):

            downloadDataMutator: function (data) {
                // if the allData global variable is present, 
               // we use it instead of "current" paginated data.
                if (allData) {
                    data = allData;
                    allData = null;
                }

               // [TODO: do other column-based trasformations, if required

                return data;
            }

That's about it.

A great advantage of using such hack is that it also allows me to support a "download this page only" button and a "download all data" button at the same time / within the same page.

All 10 comments

Hey @Darkseal

Thanks for getting in touch.

There is no inbuilt way to do this for the reasons you have mentioned above. But your work around could work with a couple of tweaks.

You wouldn't be able to make the ajax call with in the downloadDataMutator function as the ajax call is asynchronous and wouldn't be able to set its value to the return of the function. what you could do is make the ajax call first, send the returned data to a variable and access that variable from within the downloadDataMutator function.

Let me know if you need any other help.

Cheers

Oli :)

I ran into the same issue. My ugly hack involves:

  1. Create a new tabulator object in the beginning:
  const table2 = $('#newtable'); let flag = false;
  table2.tabulator({
    columns: columns, height: 10,
    renderComplete: () => flag && table2.tabulator('download', 'xlsx', 'table.xlsx', {sheetName:"MyData"})
  });
  1. When the download button is clicked, it set a flag call tabulator.setData().
  $('#btn17').click(e => {
    flag = true;
    table2.tabulator('setData', url, { filters: filters });
  });
  1. When renderComplete (set in step 1), it checks the flag in step 2 and then call tabulator.download()

A few wishlist again:

  1. The table does not load if it is hidden ($.hide()). I guess it is expected but is there a way to load data into a hidden jQuery object / DOM node?
  2. Is there a better way other than using the flag? I tried tableBuilt, dataLoaded and ajaxResponse and actually they all seems to fire when table is initialized (before setData)? e.g. Is there some callback when setData is completed?

I pursued a different approach, here's my hack:

1) When the "download all data" button is clicked, I fetch all values from the server (using the same WS which I did to populate the table) and store them within a allData global variable:

    $("a#download_XLSX_all").click(function (e) {
        e.stopPropagation();
        e.preventDefault();
        $.ajax({
            url: '/ws_to_fetch_all_data/',
        }).done(function (data) {
            // store the whole data into a global variable
            allData = data.data;
            $("div.tabulator").tabulator("download", "xlsx", "data.xlsx");
        });
    });

2) Within the Tabulator's downloadDataMutator method, I replace the "current" (paginated) table data with the allData global variable (if present/not null):

            downloadDataMutator: function (data) {
                // if the allData global variable is present, 
               // we use it instead of "current" paginated data.
                if (allData) {
                    data = allData;
                    allData = null;
                }

               // [TODO: do other column-based trasformations, if required

                return data;
            }

That's about it.

A great advantage of using such hack is that it also allows me to support a "download this page only" button and a "download all data" button at the same time / within the same page.

@Darkseal Does your approach replace the paginated table view by allData? I was not expecting this when I click 'Download all data' so I see another invisible tabulator the only way.

@mateddy , my "data-swapping" approach does the following:

  • downloads all the unpaginated data in a single WS call;
  • stores the results in the allData global variable;
  • fires the download method, who also triggers the downloadDataMutatorhook;
  • within the downloadDataMutatorhook, it uses the data contained within the previously-set allData global variable (checking if it's not null), replacing the "actual" table data; that variable is then set to null because it's not needed anymore.

No need to create another tabulator since the data-swap perfectly works.

EDIT: I published this hack here to better explain how it works: feel free to have a look at it!

@Darkseal solution does not work for version 4.2.5. I can download all data when i call setData on the table, but this is not a good approach since i need to load all data inside the DOM.

Is there any other fix to download all data from a remote table without needing to load it into the table first?

I managed to fix the error i got at version 4.2.5 and have @Darkseal solution working with this approach:

The error i got was:

Uncaught TypeError: Cannot read property 'top' of undefined

Then i looked at the downloadDataFormatter _data_ param structure and found it has a _calcs_ object with _top_ and _bottom_ attributes. So, i simply created a calcs object inside the global data Darkseal suggested and now it works.

This is my _downloadDataFormatter_ function:

function onDownloadBtnClick(data) {
            if (downloadData) {
                downloadData.calcs = {
                    top: {},
                    bottom: {}
                };
                data = downloadData;
                downloadData = null;
            }

            // Tratamento para exibi莽茫o de strings vazias em vez de valores nulos nos arquivos finais
            data.data.forEach(row => {
                for (key in row)
                    row[key] = row[key] ? row[key] : '';
            });

            return data;
        }

Thank you very much for your comments, they where very useful.
In addition to what Diegovictor said, in 4.5 instead of what you did I assigned downloadData to data.data instead of data = downloadData, and it was not necessary to create that calcs dictionary.

I found another workaround to get all data, ignoring pagination. It takes advantage to preserve filters if needed. Just save page and page size data, then reset it to first page and full data with setPageSize(true), download, then reset to initial values.

Consider table is your tabulator

function downloadData() {
    var page = table.getPage();
    var pageSize = table.getPageSize();
    table.setPageSize(true);
    table.setPage(1);
    table.download("csv", "data.csv");
    table.setPageSize(pageSize);
    table.setPage(page);
}

HerveEmagma works fantastic, thank you

Was this page helpful?
0 / 5 - 0 ratings

Related issues

andreivanea picture andreivanea  路  3Comments

yaxino picture yaxino  路  3Comments

mohanen picture mohanen  路  3Comments

Manbec picture Manbec  路  3Comments

tomheaps picture tomheaps  路  3Comments