💸 Reduce costs by consolidating proprietary analytics & reporting software to open-source & Dash.
Challenge us to replace your analytics with Dash and reduce costs.

DataTable fails to update on callback

I’m trying to build a dash app with an upload box (dcc.Upload()) and a datatable (dash_table.DataTable()) where the names of files uploaded through the upload box are supposed to be added to the table. Files seem to be successfully uploaded - the filenames of uploaded files are printed correctly to the console by the print statement inside the update_file_table() callback function. However, no new rows appear in the DataTable. Code is below. Also, on startup of the app, I see an error that says:

TypeError: Cannot read property ‘forEach’ of null

Code:

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

import pandas as pd
import os
import numpy as np

from jv import JVData

data_folder = ‘/Users/rohit/Documents/Academics/Swift/Analysis/Sandbox/jvplot’

dashapp = dash.Dash(name)

jvdata =

dashapp.layout = html.Div([
dcc.Upload(
id = ‘file-upload’,
children = html.Div([
'Drag and drop here or ',
html.A(‘click here to select files’)
]),
style = {
‘width’: ‘100%’,
‘height’: ‘60px’,
‘lineHeight’: ‘60px’,
‘borderWidth’: ‘1px’,
‘borderStyle’: ‘dashed’,
‘borderRadius’: ‘5px’,
‘textAlign’: ‘center’,
‘margin’: ‘10px’
},
multiple = True
),

dash_table.DataTable(
    id = 'file-table',
    columns = [{'name': 'Filename', 'id': 'Filename'}],
    row_selectable = 'multi',
    row_deletable = True,
    data = [],
),

])

@dashapp.callback(
Output(component_id=‘file-table’, component_property=‘data’),
[Input(component_id=‘file-upload’, component_property=‘contents’)],
[State(component_id=‘file-upload’, component_property=‘filename’),
State(component_id=‘file-upload’, component_property=‘last_modified’)]
)
def update_file_table(list_of_contents, list_of_filenames, list_of_last_modified):
if list_of_contents is not None:
[jvdata.append(JVData(f)) for f in list_of_filenames]

    data = [{'Filename': jv.filepath} for jv in jvdata]

    print(data)

    return data

JVData is a class that contains (among other things) the filepath as an instance variable.

In your update_file_table function, you should also check list_of_filenames for is not None as you do not set filename to a default value on page load. Thus, on app startup, this parameter is None. I suspect this could be the problem.

Hmm, I see what you mean, and added a condition to check this in the if statement at the start of the update_file_table() function -

if list_of_contents is not None and list_of_filenames is not None:

However, the same problem persists - I still get the JavaScript error

TypeError: Cannot read property ‘forEach’ of null

and the app still fails to update the table with names of uploaded files.

I found a fix, although I’m not entirely clear on why it works -

Updating not just the ‘data’ field of the DataTable, but also the ‘columns’ field makes it correctly show uploaded files. Relevant code (where the callback is defined) -

@dashapp.callback( [Output(component_id='file-table', component_property='data'), Output(component_id='file-table', component_property='columns')], [Input(component_id='file-upload', component_property='contents')], [State(component_id='file-upload', component_property='filename')] ) def update_file_table(list_of_contents, list_of_filenames): if list_of_contents is not None and list_of_filenames is not None: # For each uploaded file, add a JV object to the global list of JV curves [jvdata.append(JVData(f)) for f in list_of_filenames]
    # Create a list of dicts with filenames of uploaded files
    data = [{'Filename': jv.filepath} for jv in jvdata]

    #debug
    print(data)

    # Return this list to file-table DataTable
    return data, [{'id': 'Filename', 'name': 'Filename'}]

return [], []

I am running into this same issue. The datatable is not rendered/populated upon upload. The callback function listens for content property. Here’s a semi-reproducible example/code:

Any help/pointers would be much appreciated.

# 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(
            id='emp-table',
            page_size = 14,
            sort_action='native',
            row_selectable='multi',
            filter_action='native',
            row_deletable=True,
            editable=True
        ),
    ])

])


# In[12]:

# 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


# 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)

@keval This seems to be related to an issue in dash-table that seemingly breaks a DataTable when filter_action is enabled along with one or more of the following:

  • row_selectable
  • row_deletable
  • editable

See issue on Github - https://github.com/plotly/dash-table/issues/436#issuecomment-615924723

A workaround would be to disable filter_action (deleting the following line from your example makes it work fine for me) -
filter_action='native',
Of course, this is not ideal because it means you cannot perform the filtering.

A more recent answer from @montovaneli on the Github issue page provides a fix -

Returning empty lists if there is no data is what causes the problem. Instead of
return [], []
use
return start_table_df.to_dict('records'), [{'id': '', 'name': ''}]
and it should work

1 Like