dash-table icon indicating copy to clipboard operation
dash-table copied to clipboard

Dash Table with row_selectable="multi" does not respond to data or columns callbacks

Open shavavo opened this issue 5 years ago • 12 comments

DataTable with row_selectable="multi" does not respond to any columns or data callbacks.

Debug shows Javascript error:

TypeError: Cannot read property '0' of undefined

    at e.getWeight (http://127.0.0.1:8050/_dash-component-suites/dash_table/bundle.js?v=3.7.0&m=1557928877:18:65588)

    at http://127.0.0.1:8050/_dash-component-suites/dash_table/bundle.js?v=3.7.0&m=1557928877:18:98263

    at http://127.0.0.1:8050/_dash-component-suites/dash_table/bundle.js?v=3.7.0&m=1557928877:18:275516

    at http://127.0.0.1:8050/_dash-component-suites/dash_table/bundle.js?v=3.7.0&m=1557928877:12:10031

    at Object.t [as a] (http://127.0.0.1:8050/_dash-component-suites/dash_table/bundle.js?v=3.7.0&m=1557928877:1:1711)

    at e.value (http://127.0.0.1:8050/_dash-component-suites/dash_table/bundle.js?v=3.7.0&m=1557928877:18:98224)

    at http://127.0.0.1:8050/_dash-component-suites/dash_table/bundle.js?v=3.7.0&m=1557928877:18:96967

    at e.memoizedCreateEdges (http://127.0.0.1:8050/_dash-component-suites/dash_table/bundle.js?v=3.7.0&m=1557928877:1:2020)

    at e.value (http://127.0.0.1:8050/_dash-component-suites/dash_table/bundle.js?v=3.7.0&m=1557928877:18:99012)

    at http://127.0.0.1:8050/_dash-component-suites/dash_table/bundle.js?v=3.7.0&m=1557928877:18:115236

Example:

import dash
import dash_table
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import pandas as pd

df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/solar.csv')

app = dash.Dash(__name__)

app.layout = html.Div(children=[
    dash_table.DataTable(
        id='table',
        row_selectable="multi"
    ),
    html.Div(id='dummy_input')
])

@app.callback(Output('table', 'columns'), [Input('dummy_input', 'children')])
def update_columns(dummy):
    return [{"name": i, "id": i} for i in df.columns]
    
@app.callback(Output('table', 'data'), [Input('dummy_input', 'children')])
def update_data(dummy):
    return df.to_dict('records')

if __name__ == '__main__':
    app.run_server(debug=True)

shavavo avatar May 22 '19 16:05 shavavo

I run into the same issue, with a very similar stack trace, but only when I add the property filter_action = 'native' to my DataTable. Anyone have an idea what might be breaking? Below is the part of my code where I create the DataTable. The app, including the table, works fine if I remove the filter_action property.

dash_table.DataTable(
        id = 'files-table',
        columns = files_table_columns,
        data = [],
        row_selectable = 'multi',
        row_deletable = True,
        editable = False,
        dropdown = {},
        sort_action = 'native',
        filter_action = 'native',
        style_header = {
            'backgroundColor': 'rgb(230, 230, 230)',
            'fontWeight': 'bold'
        },
        style_table = {
            'maxHeight': '300px',
            'overflowY': 'scroll'
        },
        fixed_rows = {
            'headers': True,
            'data': 0
        }
    ),

rp2532 avatar Jan 10 '20 19:01 rp2532

Incidentally I just found that the filtering action works fine if I remove the lines row_selectable = 'multi', row_deletable = True, So it seems like something about the combination of selectable AND deletable rows AND the filter_action is resulting in the problem.

rp2532 avatar Jan 10 '20 19:01 rp2532

@Marc-Andre-Rivet seems to me that this could be related to #447

rp2532 avatar Jan 10 '20 19:01 rp2532

@rp2532 I have created a table with editable=True and filter_action=native. When I try and add in row_deletable=True or row_selectable=True, the table does not render.

It seems that having filter_action=native in combination with (row_deletable=True OR row_selectable="multi") is not possible, like you are seeing.

phildibert avatar Jan 15 '20 15:01 phildibert

I'm also having a problem with the combination of row_selectable=multi and filter_action=native. getting the same error Cannot read property '0' of undefined. developer tools shows this in details:

(This error originated from the built-in JavaScript code that runs Dash apps. Click to see the full stack trace or open your browser's console.)
TypeError: Cannot read property '0' of undefined

    at e.getWeight (http://localhost:3000/_dash-component-suites/dash_table/async-table.v4_6_1m1582839172.js:7:117699)

    at http://localhost:3000/_dash-component-suites/dash_table/async-table.v4_6_1m1582839172.js:7:131047

    at http://localhost:3000/_dash-component-suites/dash_table/async-table.v4_6_1m1582839172.js:7:197843

    at http://localhost:3000/_dash-component-suites/dash_table/async-table.v4_6_1m1582839172.js:1:427

    at Object.t [as a] (http://localhost:3000/_dash-component-suites/dash_table/bundle.v4_6_1m1583158233.js:1:6127)

    at e.value (http://localhost:3000/_dash-component-suites/dash_table/async-table.v4_6_1m1582839172.js:7:131008)

    at http://localhost:3000/_dash-component-suites/dash_table/async-table.v4_6_1m1582839172.js:7:129754

    at e.memoizedCreateEdges (http://localhost:3000/_dash-component-suites/dash_table/bundle.v4_6_1m1583158233.js:1:10407)

    at e.value (http://localhost:3000/_dash-component-suites/dash_table/async-table.v4_6_1m1582839172.js:7:131814)

    at t.tableFn (http://localhost:3000/_dash-component-suites/dash_table/async-table.v4_6_1m1582839172.js:7:156594)

dash used is as follows on python 3.8.1:

dash==1.9.1
dash-core-components==1.8.1
dash-cytoscape==0.1.1
dash-html-components==1.0.2
dash-renderer==1.2.4
dash-table==4.6.1

wolfgangpfnuer avatar Mar 09 '20 12:03 wolfgangpfnuer

@wolfgangpfnuer i went through the same problem and i realized that if i create my DataTable using an empty pandas DataFrame with one column, it works. Try it yourself, for example:

This one will throw the "Cannot read property '0' of undefined" error:

import dash
import dash_table
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import pandas as pd

df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/solar.csv')

app = dash.Dash(__name__)

app.layout = html.Div(children=[
    dash_table.DataTable(
        id='table',
        filter_action='native',
        row_selectable="single"
    ),
    html.Div(id='dummy_input')
])

@app.callback(Output('table', 'columns'), [Input('dummy_input', 'children')])
def update_columns(dummy):
    return [{"name": i, "id": i} for i in df.columns]
    
@app.callback(Output('table', 'data'), [Input('dummy_input', 'children')])
def update_data(dummy):
    return df.to_dict('records')

if __name__ == '__main__':
    app.run_server(debug=True)

However, this one works:

import dash
import dash_table
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import pandas as pd

df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/solar.csv')

app = dash.Dash(__name__)

start_table_df = pd.DataFrame(columns=['Start Column'])  ####### inserted line

app.layout = html.Div(children=[
    dash_table.DataTable(
       data=start_table_df.to_dict('records'), ####### inserted line
       columns = [{'id': c, 'name': c} for c in start_table_df.columns], ####### inserted line
        id='table',
        filter_action='native',
        row_selectable='single'
    ),
    html.Div(id='dummy_input')
])

@app.callback(Output('table', 'columns'), [Input('dummy_input', 'children')])
def update_columns(dummy):
    return [{"name": i, "id": i} for i in df.columns]
    
@app.callback(Output('table', 'data'), [Input('dummy_input', 'children')])
def update_data(dummy):
    return df.to_dict('records')

if __name__ == '__main__':
    app.run_server(debug=True)

montovaneli avatar Apr 18 '20 18:04 montovaneli

@montovaneli I cannot reproduce your workaround - if I have a data_table with BOTH filter_action and row_selectable enabled, I get the error upon startup that says TypeError: Cannot read property '0' of undefined However, if only one of filter_action or row_selectable is used (or neither of them), the error does not appear.

Code below is modified from keval's post on this page - https://community.plotly.com/t/datatable-fails-to-update-on-callback/32720/4


import dash
import dash_core_components as dcc 
import dash_html_components as html 
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
import dash_table
from plotly.subplots import make_subplots

import os
import numpy as np 
import pandas as pd
import io
import base64

# Empty DataFrame to start the DataTable (workaround for bug in DataTable - https://github.com/plotly/dash-table/issues/436#issuecomment-615924723)
start_table_df = pd.DataFrame(columns=['Start Column'])

# Create dash app
app = dash.Dash(__name__)

### LAYOUT ###
# App Layout
app.layout = html.Div([
    html.H3("Post Covid-19 Tool"),

    dcc.Upload(
        id='upload-data',
        children=html.Div([
            'Drag and Drop or ',
            html.A('Select Files')
        ]),
        style={
            'width': '100%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'center',
            'margin': '10px'
        },
        # Allow multiple files to be uploaded
        multiple=False
    ),

    html.Div([
        dash_table.DataTable(
            data=start_table_df.to_dict('records'), 
            columns = [{'id': c, 'name': c} for c in start_table_df.columns],
            id='emp-table',
            #page_size = 14,
            #sort_action='native',
            filter_action='native',
            row_selectable='single',
            #row_deletable=True,
            #editable=True
        ),
    ])
])

# file upload function
def parse_contents(contents, filename):

    content_type, content_string = contents.split(',')

    decoded = base64.b64decode(content_string)

    try:

        if 'csv' in filename:
            # Assume that the user uploaded a CSV file
            df = pd.read_csv(io.StringIO(decoded.decode('utf-8')))

        elif 'xls' in filename:
            # Assume that the user uploaded an excel file
            df = pd.read_excel(io.BytesIO(decoded))

    except Exception as e:
        print(e)
        return None

    return df

### CALLBACKS ###
# Callback: Return Datatable
@app.callback([Output('emp-table', 'data'),
               Output('emp-table', 'columns')],
              [Input('upload-data', 'contents')],
              [State('upload-data', 'filename')])
def render_table(contents, filename):

    if contents and filename:

        df_geo = parse_contents(contents, filename)

        columns = [{"name": i, "id": i} for i in df_geo.columns]

        data = df_geo.to_dict('records')

        # Debug
        print(df_geo.head())

        return data, columns

    else:

        return [], []


if __name__ == '__main__':
    app.run_server(debug=True, port=8000)

rp2532 avatar May 23 '20 00:05 rp2532

@Marc-Andre-Rivet @chriddyp Any insight into this issue, i.e. why filter_action does not work in combination with either row_selectable or editable or row_deletable? Any idea when it might be fixed?

Incidentally a related issue on the community forum is in keval's comment on this page - https://community.plotly.com/t/datatable-fails-to-update-on-callback/32720/5

rp2532 avatar May 23 '20 00:05 rp2532

@rp2532 that is because you are returning empty lists when you don't have uploaded data. Try this:

import dash
import dash_core_components as dcc 
import dash_html_components as html 
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
import dash_table
from plotly.subplots import make_subplots

import os
import numpy as np 
import pandas as pd
import io
import base64

# Empty DataFrame to start the DataTable (workaround for bug in DataTable - https://github.com/plotly/dash-table/issues/436#issuecomment-615924723)
start_table_df = pd.DataFrame(columns=[''])

# Create dash app
app = dash.Dash(__name__)

### LAYOUT ###
# App Layout
app.layout = html.Div([
    html.H3("Post Covid-19 Tool"),

    dcc.Upload(
        id='upload-data',
        children=html.Div([
            'Drag and Drop or ',
            html.A('Select Files')
        ]),
        style={
            'width': '100%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'center',
            'margin': '10px'
        },
        # Allow multiple files to be uploaded
        multiple=False
    ),

    html.Div([
        dash_table.DataTable(
            data=start_table_df.to_dict('records'), 
            columns = [{'id': c, 'name': c} for c in start_table_df.columns],
            id='emp-table',
            #page_size = 14,
            #sort_action='native',
            filter_action='native',
            row_selectable='single',
            #row_deletable=True,
            #editable=True
        ),
    ])
])

# file upload function
def parse_contents(contents, filename):

    content_type, content_string = contents.split(',')

    decoded = base64.b64decode(content_string)

    try:

        if 'csv' in filename:
            # Assume that the user uploaded a CSV file
            df = pd.read_csv(io.StringIO(decoded.decode('utf-8')))

        elif 'xls' in filename:
            # Assume that the user uploaded an excel file
            df = pd.read_excel(io.BytesIO(decoded))

    except Exception as e:
        print(e)
        return None

    return df

### CALLBACKS ###
# Callback: Return Datatable
@app.callback([Output('emp-table', 'data'),
               Output('emp-table', 'columns')],
              [Input('upload-data', 'contents')],
              [State('upload-data', 'filename')])
def render_table(contents, filename):

    if contents and filename:

        df_geo = parse_contents(contents, filename)

        columns = [{"name": i, "id": i} for i in df_geo.columns]

        data = df_geo.to_dict('records')

        # Debug
        print(df_geo.head())

        return data, columns

    else:

        return start_table_df.to_dict('records'), [{'id': '', 'name': ''}]


if __name__ == '__main__':
    app.run_server(debug=True, port=8000)

montovaneli avatar May 23 '20 00:05 montovaneli

@montovaneli Confirming, that indeed works!

rp2532 avatar May 23 '20 01:05 rp2532

Same issue here and @montovaneli's workaround did the trick as well. Thanks!

OliverSieweke avatar Jul 12 '20 11:07 OliverSieweke

Just to provide more color on this. It appears that having columns set to an empty list on the examples above will reproduce this error. On the other hand data can be set to an empty list.

cyprienc avatar May 20 '21 07:05 cyprienc