Black Lives Matter. Please consider donating to Black Girls Code today.

Tabulator Dash Component

Folks I found myself needing functionality from Tabulator (fantastic data table project) http://tabulator.info/

  • Grouping of rows under a header
  • Incase-sensitive searching
  • Custom filters

I found Tabulator had a react port, and went ahead and made a dash version, providing callbacks for updating data / columns, and handling row clicks.

It meets my current needs, and i’m learning as I’m going but it might be of use to others.
And willing to take input / requests if there’s a desire to add more tabulator functionality.

Details : https://github.com/preftech/dash-tabulator
Pypi: pip install dash_tabulator

3 Likes

Very sweet @pjaol and thanks for sharing :tada:

I love your note in the readme:

This readme is probably longer than the code, due to the work of those individuals!

Yes! That’s the power of React and the Dash plugin system! :raised_hands:

Here are some screenshots for those that are just passing through. Definitely some nice features that aren’t covered yet in dash_table.DataTable

thanks @chriddyp, took a bit of yelling at the screen and going what do you mean i can’t concatenate a string that. But got it working.

I think Tabulator would definitely complement the fantastic work in Dash / Plotly.
I’m also taking my company down the path of contributing back to the open source world with this being our first of hopefully many projects.
There’s definitely ways to improve this code, and I would encourage anyone interested to take a look and make suggestions.

This has worked beautifully for me so far.

“Page Click” callbacks would be wonderful so I can implement server-side pagination on large datasets.

@crachel it looks like it’s possible with remote pagination http://tabulator.info/docs/4.4/page#remote
And it seems react-tabulator can do it
https://github.com/ngduc/react-tabulator/blob/master/src/ReactTabulatorExample.tsx#L93-L131

What I might need help in understanding is how to tie dash to that
ajaxURL and just use ajaxResponse in the component to format the data

Any ideas?

Hi @pjaol

Thanks so much for making Tabulator available as Dash component. I really like the header filters.

I’ve been able to make lots of other Tabulator features work in my app too – as long as they are predefined. But I haven’t been able to figure out how to make things work that require javascript. For example, I see how to add conditional formatting in the Tabulator documentation, but how would I do this in Dash? I tried a bunch of different things, but I don’t know enough js yet. (I also tried yelling at the screen, since you said that worked for you, but that didn’t work either :laughing: )

# Setup some columns 
# This is the same as if you were using tabulator directly in js 
columns = [
                {"formatter":"printIcon",  "width": 150, "hozAlign":"center"},
                { "title": "Name", "field": "name", "width": 150, "headerFilter":True},


         # If I wanted to use this conditional formatting for the "Name" columns, how would I do it?

                #{"title":"Name", field:"name", "width":150, "formatter":function(cell, formatterParams){
                #    var value = cell.getValue();
                #    if(value.indexOf("o") > 0){
                #        return "<span style='color:red; font-weight:bold;'>" + value + "</span>";
                #    }else{
                #        return value;
                #    }
                #}},


                { "title": "Age", "field": "age", "hozAlign": "left", "formatter": "progress" },
                { "title": "Favourite Color", "field": "col", "headerFilter":True },
                { "title": "Date Of Birth", "field": "dob", "hozAlign": "center" },
                { "title": "Rating", "field": "rating", "hozAlign": "center", "formatter": "star" },
                { "title": "Passed?", "field": "passed", "hozAlign": "center", "formatter": "tickCross" }
              ]

I would also like to add a button to clear the filters, and get the filters to use in other callbacks. I see it’s available: http://tabulator.info/docs/4.7/filter

table.clearFilters(true)
var headerFilters = table.getHeaderFilters();

Is it possible to make this work with Dash? If so, could you provide an example?

Thanks again for sharing this component – I appreciate your contribution!

1 Like

@pjaol this looks fantastic! Is it possible to add multi-row selection using checkboxes? If so, how would I modify the sample usage app to support this?

Apologies I’ve been a little tied up with work, I’ll take a look this week at it.
I suspect it’s just a case of extending the react component to include a button to do so
Similar to the download button

I can probably include that if you don’t want to extend it, whats the best way to setup the css for folks to style the buttons?

@cufflink
There is a checkbox option for row-formatters http://tabulator.info/examples/4.6#selectable-tick
Where you have to provide a formatter in the columns array

column=[
               {formatter:"rowSelection"... cellClick:function(e,cell){....} }, 
               {'title':"......."},
               {'title':"......."},

However you should probably look at how to extend the component in order to provide your own function to handling the select method.
There may be an option to have a global method you can import in

external_scripts = ['https://oss.sheetjs.com/sheetjs/xlsx.full.min.js', '/js/mycustom.js']
app = dash.Dash(__name__, external_scripts=external_scripts, external_stylesheets=external_stylesheets)

Thanks for your response. I suspected that the component would need to be extended, but I haven’t learned how to do that yet.(I’ve tried but got stuck on some errors I couldn’t fix)

For my purposes, it’s not necessary to incorporate a button - it’s easy enough to use a dash component for that. If I could get access to the filter data in a callback - just like the data, columns, and rowClicked parameter, I could make it work.

Have you tried yelling louder at the screen again? (The 2nd / 3rd time sometimes works for me)
I’ll take a look, I think having an external global set of functions might make it easier for JS callbacks

For python filtering I think it’s easier to add reusable features in the code - but will look to see if there’s an easy way for folks to extend or customize and contribute back.
Honestly the hardest part is figuring out the project setup, how callbacks work, and where the code is.

Ann Marie for your use case you are looking at 2 parts?

  1. Filter data python side based on header filters
  2. Ability to clear header filters client side

Haha - there has been plenty of yelling going on here and some bad language too :blush:

I’ve gone through all the documentation on how to do custom components in Dash, and if I could get my environment set up right, I’m sure I’d be able to extend Tabulator. I’ve spent a lot of time with the Tabulator docs too, so I think I understand how things work there. I’m sure I’ll get it figured out eventually and then I’d be delighted to contribute. I’d also like to learn more about the external global methods.

Tabulator does a great job with the filtering, so I don’t need to do that client side. In my application I have about 10 columns with filters, and it would be a nice UI feature to be able to reset the filters without the user having to delete each one.

But the most important thing for me is to be able to update the filter in a callback. When I update columns the filter disappears, but it’s not really gone and it still filters the data according to what was previously in the filter. Very weird. A user would have to remember what column had the filter and hit delete in what looks like an empty field. If I can re-populate the filter when I update the columns, it would work. In the meantime, I’m back to a dash table.

Hi pjaol

Many thanks for making tabulator available in dash. I wonder if you could help me with setting the background color of a cell such that the entire cell is colored but the text is still visible? I have failed so far.

What I have tried so far:
Options 1: Use plain html:

columns = [
                { "title": "Name", "field": "name", "width": 150, "headerFilter":True},
                { "title": "Age", "field": "age", "hozAlign": "left", "formatter": "progress" },
                { "title": "Favourite Color", "field": "col", "headerFilter":True, "formatter":"html"},
                { "title": "Date Of Birth", "field": "dob", "hozAlign": "center" },
                { "title": "Rating", "field": "rating", "hozAlign": "center", "formatter": "star" },
                { "title": "Passed?", "field": "passed", "hozAlign": "center", "formatter": "tickCross" }
              ]

data = [
                {"id":1, "name":"Oli Bob", "age":"12", "col":"<style>body{margin:0px} p{background-color: rgb(255,0,0); margin: 0;border: 0}</style> <body><p>Red text </p></body>", "dob":"",},
                {"id":2, "name":"Mary May", "age":"1", "col":"blue", "dob":"14/05/1982"},
                {"id":3, "name":"Christine Lobowski", "age":"42", "col":"green", "dob":"22/05/1982"},
                {"id":4, "name":"Brendon Philips", "age":"125", "col":"orange", "dob":"01/08/1980"},
                {"id":5, "name":"Margret Marmajuke", "age":"16", "col":"yellow", "dob":"31/01/1999"},
                {"id":6, "name":"Fred Savage", "age":"16", "col":"yellow", "rating":"1", "dob":"31/01/1999"},
                {"id":6, "name":"Brie Larson", "age":"30", "col":"blue", "rating":"1", "dob":"31/01/1999"},
              ]

However, this does not color the entire cell. Fiddling around with margin did not help either:
image

Option 2: The color formater. Here the problem is that the text is not visible
, and I cannot set the font color:

columns = [

                { "title": "Name", "field": "name", "width": 150, "headerFilter":True},

                { "title": "Age", "field": "age", "hozAlign": "left", "formatter": "progress" },

                { "title": "Favourite Color", "field": "col", "headerFilter":True, "formatter":"color"},

                { "title": "Date Of Birth", "field": "dob", "hozAlign": "center" },

                { "title": "Rating", "field": "rating", "hozAlign": "center", "formatter": "star" },

                { "title": "Passed?", "field": "passed", "hozAlign": "center", "formatter": "tickCross" }

              ]


data = [

                {"id":1, "name":"Oli Bob", "age":"12", "col":"red", "dob":"",},

                {"id":2, "name":"Mary May", "age":"1", "col":"blue", "dob":"14/05/1982"},

                {"id":3, "name":"Christine Lobowski", "age":"42", "col":"green", "dob":"22/05/1982"},

                {"id":4, "name":"Brendon Philips", "age":"125", "col":"orange", "dob":"01/08/1980"},

                {"id":5, "name":"Margret Marmajuke", "age":"16", "col":"yellow", "dob":"31/01/1999"},

                {"id":6, "name":"Fred Savage", "age":"16", "col":"yellow", "rating":"1", "dob":"31/01/1999"},

                {"id":6, "name":"Brie Larson", "age":"30", "col":"blue", "rating":"1", "dob":"31/01/1999"},

              ]

image

Option 3: Write a customized formatter in JS:

columns = [
                { "title": "Name", "field": "name", "width": 150, "headerFilter":True},
                { "title": "Age", "field": "age", "hozAlign": "left", "formatter": "progress" },
                { "title": "Favourite Color", "field": "col", "headerFilter":True, "formatter":"function(cell){var col = cell.getValue();cell.getElement().style.backgroundColor = '#A6A6DF';return col}"},
                { "title": "Date Of Birth", "field": "dob", "hozAlign": "center" },
                { "title": "Rating", "field": "rating", "hozAlign": "center", "formatter": "star" },
                { "title": "Passed?", "field": "passed", "hozAlign": "center", "formatter": "tickCross" }
              ]


data = [
                {"id":1, "name":"Oli Bob", "age":"12", "col":"red", "dob":"",},
                {"id":2, "name":"Mary May", "age":"1", "col":"blue", "dob":"14/05/1982"},
                {"id":3, "name":"Christine Lobowski", "age":"42", "col":"green", "dob":"22/05/1982"},
                {"id":4, "name":"Brendon Philips", "age":"125", "col":"orange", "dob":"01/08/1980"},
                {"id":5, "name":"Margret Marmajuke", "age":"16", "col":"yellow", "dob":"31/01/1999"},
                {"id":6, "name":"Fred Savage", "age":"16", "col":"yellow", "rating":"1", "dob":"31/01/1999"},
                {"id":6, "name":"Brie Larson", "age":"30", "col":"blue", "rating":"1", "dob":"31/01/1999"},
              ]

–>Does not work. I guess I cannot pass a JS function at all, or my syntax is wrong.

Could you please let me know if there is a way to solve this?

Many thanks

HI @fair

It’s true you can’t pass a JS function call, but you could try adding this to the css in the assets folder:

.tabulator-row .tabulator-cell {
padding:0;
}

Hi AnnMarieW

Many thanks for your answer. Please apologize my ignorance, but where could I find the assets folder? I only found C:\python\Lib\site-packages\dash_tabulator but there is no CSS file in it.

In order to include custom CSS in your Dash app, create a folder called assets in the same directory as your app (not the dash_tabulator files). Then create a file with the CSS and save it in the assets folder. You can name the file anything you like as long as it ends with .css Then Dash will automatically use this css in your app.

You can see a better explanation and some examples here: https://dash.plotly.com/external-resources

If you haven’t done this before, I recommend that you copy and paste the examples from the tutorial and give it a try to make sure it works for you. It’s actually pretty slick!