Three dependent dropdowns which updates Charts

Hi All,

I have three dropdowns. The second one depends on the first, the third on both first and second. And I have a submit button after the dropdowns, upon clicking it, the expected behaviour is to updated the chart. But, my code is updating forever and finally fails to load the components. Below if a pseudo code:

import dash
from dash.dependencies import Input, Output, State
import dash_html_components as html
import dash_core_components as dcc
from plotly import graph_objs as go
from plotly.graph_objs import *
import pandas as pd
import requests
import urllib3

#a dict converted into a df
dfDropDown = pd.DataFrame(output)
Topics = dfDropDown.Cat.unique()
app = dash.Dash()

app.layout = html.Div([
    
    html.Label('Drop-Downs'),
    dcc.Dropdown(
                                id = 'Criteria1',
                                options=[
                                   {'label':name, 'value':name} for name in Topics
           
                                ],
                                 value = list(Topics)[0]#
                        ),
    html.Div([
        dcc.Dropdown(
            id='opt-dropdown'#to be updated based on the selection of first DropDown
            ),
            ]
        ),
                                 html.Div([
        dcc.Dropdown(
            id='opt-dropdown1'#to be updated based on the selection of first and second DropDown
            ),
            ]
        ),

    html.Button('Click Me',id='button-2'), #update the chart below, based on the values from the drop down
    html.Div(dcc.Graph(id='bar_plot'))

    #html.Div(id='output')
])


@app.callback(
    dash.dependencies.Output('opt-dropdown', 'options'),
    [dash.dependencies.Input('Criteria1', 'value')]
)
def update_date_dropdown(name):
    dropDownCategory = []
    dfCategory = dfDropDown.loc[dfDropDown['Topic'] == name]
    dropDownCategory = dfCategory.Category.unique()
    return [ {'label':name, 'value':name} for name in dropDownCategory]            
  

@app.callback(
    dash.dependencies.Output('opt-dropdown1', 'options'),
    [dash.dependencies.Input('Criteria1', 'value'),
     dash.dependencies.Input('opt-dropdown', 'value')]
)
def update_date_dropdown1(name,level1):
    dropDownCatValue = []
    dfCategoryValue =  dfDropDown.loc[dfDropDown['Topic'] == name]
    dfCategoryValue =  dfDropDown.loc[dfDropDown['Category'] == level1]     
    dropDownCatValue = dfCategoryValue.CategoryValue.unique()
    dropDownCatValue = [i for i in dropDownCatValue if i]
    return [{'label': name, 'value':name} for name in dropDownCatValue]
    
@app.callback(
    Output('bar_plot', 'figure'),
    [Input('button-2', 'n_clicks')],
    state=[State('Criteria1', 'value'),
           State('opt-dropdown', 'value'),
           State('opt-dropdown1', 'value')
    ]
     )

def computeGr(n_clicks, input1,input2,input3):
    #code to get the datapoints from an API based on the selected values and return the figure - this code run for a long time and errors out. 
    return {
              'data':traces,
                               'layout':go.Layout(barmode='stack',hovermode='closest')
                               #style={'width':600}
           }
    

if __name__ == '__main__':
    app.run_server(debug=True)indent preformatted text by 4 spaces

Seems as though you don’t want the update_date_dropdown to return output until values for Criteria1 and opt-dropdown are set. I think you should pass the values of both as State inputs to this callback, then use the following to obtain the status of the callback parameters.

ctx = dash.callback_context

At these start of the callback definition, you can inspect ctx to ensure both Criteria1 and opt-dropdown values are populated; if not, return no_update.

Thank you for the reply! But, passing the values, without an Input event is not allowed if I understand right.

I wanted the chart to update, after I click a button (id=‘button-2’), so had made this as the input and the drop down values as states.

Also, I am quiet unfamiliar with dash.callback_context, could you give an example of how to use it?

You need to pass them as an Input in order for the callback to get triggered, that is correct; also passing the values as States allows for their values to be available as well. I have found that not passing them as States (even though they are also passed in as Inputs) resulted in undesirable behavior.

After looking at the CTX structure below you can tailor its use for you. In my case, I have a helper function in which I pass in ctx, and its output is 1) What dcc component was pressed, and 2) a bool if all the States are populated.

ctx = dash.callback_context

# Show CTX values
print(u''' ctx = {}'''.format(json.dumps({
    'states': ctx.states,
    'triggered': ctx.triggered,
    'inputs': ctx.inputs
    }, indent=2)))

# Determine which dcc component invoked callback
clicked = ctx.triggered[0]['prop_id'].split('.')[0]
print(u'''user pressed = {}'''.format(clicked))