Bring Drag & Drop to Dash with Dashboard Engine. 💫 Learn how at our next webinar!

Problem with chained callbacks and dictionaries

I am having a problem when chaining callbacks together. The problem occurs in the attached code. Running the app the drop downs populate fine on load. If I select a second option for the scenario drop down again the app continues working properly. However with those two scenarios selected and changing the the city (which does not have the second scenario) I get a keyword error but the the app continues on the and keeps working. This is not a problem in this example but in my complete app this creates problems because I am trying to select a set of figures from a dictionary of figures and this keyword error prevents the figures from loading properly and resets the app.

It appears that the app still sees the two initial scenarios in the drop down list and proceeds with the code (You can see this with the print statements), then the scenario drop down list gets updated and the code runs again. Is there anything I can do to prevent the code in the third callback from running until the scenario drop down list has been updated?

from jupyter_dash import JupyterDash
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Output, Input
import dash_bootstrap_components as dbc

import plotly.figure_factory as ff
import plotly
import plotly.express as px
import plotly.io as pio
import plotly.graph_objects as go

import io
import os
import sys
cityDict = {
    'Courtenay':{'scnName':['BAP','LC2'],},
    'Toronto':{'scnName':['BAU','BAP','LC2050','LC2040'],},
}
cityDict2 = {
    'Courtenay':{'BAP':'BAP','LC2':"LC2"},
    'Toronto':{'BAU':'BAU','BAP':'BAP','LC2050':'LC2050','LC2040':'LC2040'},
}

external_stylesheets = [dbc.themes.BOOTSTRAP]

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

# Create server variable with Flask server object for use with gunicorn
server = app.server

app.layout = html.Div(
    [
        html.Div([
            dbc.Row([
                dbc.Col(html.H4("City:"),md=1),
                dbc.Col(dcc.Dropdown(
                    id='city-selector',
                    options=[{'label': c, 'value': c} for c in cityDict],
                    value=list(cityDict.keys())[0],
                    multi=False,
                ),md=5),
                dbc.Col(html.H4("Scenario:"),md=1),
                dbc.Col(dcc.Dropdown(
                    id='scn-selector',
                    multi=True
                ),md=5),
            ]),
        ], className='topBar'),
        dbc.Tabs(
            [
                dbc.Tab(label='Demographics', tab_id='demoTab'),
            ], 
            id='tabs', active_tab='demoTab',
        ),
        html.Div(id="tab-content", className="data-content"),
    ]
)

@app.callback(Output('scn-selector', 'options'),
              [Input('city-selector', 'value')])
def set_scn_options(city):
    return [{'label': i, 'value': i} for i in cityDict[city]['scnName']]

@app.callback(Output('scn-selector', 'value'),
              [Input('scn-selector', 'options')])
def set_scn_value(scns):
    return [scns[0]['value']]

@app.callback(Output('tab-content', 'children'),
              [Input("tabs", "active_tab"),
               Input('city-selector', 'value'),
               Input('scn-selector', 'value')])
def render_tab_content(active_tab, c, scns):
    
    print("length of scenarios - " + str(len(scns)))
    print("city is - " + c)
    if active_tab:
        if active_tab == 'demoTab':
            if len(scns) > 1:
                temp1=scns[0]
                temp2=scns[1]
                print(scns[0])
                print(scns[1])
                return c + ' - ' + str(cityDict2[c][temp1]) + ' - ' + str(cityDict2[c][temp2])
                
            else:
                temp1=scns[0]
                print(scns[0])
                return c + ' - ' + temp1
                    
    return " "
        
app.run_server(host='0.0.0.0')