Search code examples
pythoncallbackdropdownplotly-dash

Update datatable with sub dropdowns (Dash Python)


I am desperately looking for a way to make sub dropdowns. I'll explain.

I have a datatable, and I want to have different display parameters (like full, simplified and custom) : they are all 3 selected in a dropdown. But when custom is selected, I want a second dropdown to appear just under the first one in which I can select the specific rows I want.

Everything works except the way to hide/show this second dropdown. Because if I use one additional callback that display it if 'custom' is chosen, I have an error telling me that this dynamic dropdown does not exists. Does someone knows how to do it ? Thanks in advance ! The pseudo code used to show you this scenario :

from dash import Dash, html, dcc, Input, Output, dash_table
from dash_bootstrap_components import themes
from tools.visual import colors
import pandas as pd


data = [['A', 10, 19, 21], ['B', 15, 54, 12], ['C', 14, 5, 9], ['D', 14, 5, 9], ['E', 14, 5, 9]]
dashboard = pd.DataFrame(data, columns=['Mat', '1', '2', '3'])


app = Dash(external_stylesheets=[themes.BOOTSTRAP])


app.layout = html.Div(style={'backgroundColor': colors['background']}, children=[
    html.Div(children=[
        dcc.Dropdown(id='chosen-mat', multi=False, value='All',
                     options=['All', 'Custom'])
    ]),

    html.Div(id='custom-mat', children=[
        dcc.Dropdown(id='chosen-custom-mat', multi=True, value=[],
                     options=dashboard.iloc[0:, 0].to_list())
    ]),

    html.Div([
        dash_table.DataTable(
            data=dashboard.to_dict('records'),
            columns=[{"name": i, "id": i} for i in dashboard.columns],
            id='tbl')
    ], style={'margin-left': '20px', 'margin-right': '20px'}),

    html.Div(id='datatable-interactivity-container', style={'text-align': 'center'})
])


@app.callback(Output('tbl', 'data'),
              Output('tbl', 'columns'),
              Input('chosen-mat', 'value'),
              Input('chosen-custom-mat', 'value'))
def actualize_db(type_layout, mat_list):
    if type_layout == 'All':
        db = dashboard
    else:
        if mat_list is None:
            listt = []
        else:
            listt = mat_list
        db = dashboard[dashboard['Mat'].isin(listt)]
    return db.to_dict('records'), [{"name": i, "id": i} for i in db.columns]


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

And what I need to do is this :

If CUSTOM selected : enter image description here

If another option (here ALL) is selected : enter image description here


Solution

  • You can do as below code to Hide second dropdown when you choose All and show when you choose Custom. As you see, we will return children based on dropdown.

    from dash import Dash, html, dcc, Input, Output, dash_table
    from dash_bootstrap_components import themes
    import pandas as pd
    
    
    data = [['A', 10, 19, 21], ['B', 15, 54, 12], ['C', 14, 5, 9], ['D', 14, 5, 9], ['E', 14, 5, 9]]
    dashboard = pd.DataFrame(data, columns=['Mat', '1', '2', '3'])
    
    
    app = Dash(external_stylesheets=[themes.BOOTSTRAP])
    
    
    app.layout = html.Div(children=[
        html.Div(children=[
            dcc.Dropdown(id='chosen-mat', multi=False, value='All',
                         options=['All', 'Custom'])
        ]),
    
        html.Div(id='custom-mat'),
    
        html.Div([
            dash_table.DataTable(
                data=dashboard.to_dict('records'),
                columns=[{"name": i, "id": i} for i in dashboard.columns],
                id='tbl')
        ], style={'margin-left': '20px', 'margin-right': '20px'}),
    
        html.Div(id='datatable-interactivity-container', style={'text-align': 'center'})
    ])
    
    @app.callback(Output('custom-mat', 'children'),
                  [Input('chosen-mat', 'value')])
    def hide_unhide(type_layout):
        if type_layout == 'Custom':    
            return dcc.Dropdown(id='chosen-custom-mat', multi=True, value=[],
                         options=dashboard.iloc[0:, 0].to_list())
        if type_layout == 'All': 
            return []
    
    @app.callback(Output('tbl', 'data'),
                  Output('tbl', 'columns'),
                  Input('chosen-mat', 'value'),
                  Input('chosen-custom-mat', 'value'))
    def actualize_db(type_layout, mat_list):
        if type_layout == 'All':
            db = dashboard
        else:
            if mat_list is None:
                listt = []
            else:
                listt = mat_list
            db = dashboard[dashboard['Mat'].isin(listt)]
        return db.to_dict('records'), [{"name": i, "id": i} for i in db.columns]
    
    
    if __name__ == '__main__':
        app.run_server(debug=False,port='8051')