Hello! I am a new to Dash and I am trying to build an app where I can dynamically add graphs to my page and delete them. Each graph has a dropdown menu which one can use to select the data to be visualized. This works as it is.
I am now trying to add certain ‘predefined views’ in the dropdown menu, such that when one selects a predefined view the app automatically generates the required number of figures and pre-selects the data (from dropdown) which the user would have manually.
The problem here is that the function displayMultipleFigures( )
is being called twice when there is a change in the dropdown selection, which I believe is because of change in the state but also the dropdown input. And this is causing a deletion from the addOrRemoveContainers( )
function of the first graph element on the page because somehow, the returning of the children by the displayMultipleFigures( )
is triggering the ‘dynamic-delete’. I would appreciate any kind of help. I have omitted quite many details from the code but please let me know if more clarifications are needed.
Here’s my code:
from dash import Dash, dcc, html, Input, Output, State, MATCH, callback, ALL, callback_context
import dash
import plotly.graph_objects as go
import json
dropdownOptions = ['Input 1', 'Input 2', 'Input 3']
app = Dash(__name__)
app.layout = html.Div([
html.Hr(),
html.Button("Add Plot", id="add-plot-btn", n_clicks=0,style={'font-size':'125%'}),
html.Hr(),
html.Div(id='dynamic-container', children=[]),
])
@callback(
Output('dynamic-container', 'children'),
Input('add-plot-btn', 'n_clicks'),
Input({"type": "dynamic-delete", "index": ALL}, "n_clicks"),
State("dynamic-container",'children'),
)
def addOrRemoveContainers(n_clicks, _, children):
input_id = dash.callback_context.triggered[0]["prop_id"].split(".")[0]
if "index" in input_id:
delete_chart = json.loads(input_id)["index"]
children = [chart for chart in children if "'index': " + str(delete_chart) not in str(chart)]
else:
new_element = html.Div([
dcc.Dropdown(dropdownOptions,
id={'type': 'dynamic-dropdown','index': n_clicks},
value=0,multi=True,style={'font-size':'130%','height':'50px'},maxHeight=500),
html.Hr(),
html.Div(["Close plot >>", html.Button("X",id={"type": "dynamic-delete", "index": n_clicks},
n_clicks=0,style={"display": "block"},)],style={'font-size':'125%','display':'inline-flex'}),
html.Div([dcc.Graph(id={'type': 'dynamic-graph','index': n_clicks},figure=go.Figure())],)
])
children.append(new_element)
return children
@callback(
Output('dynamic-container', 'children', allow_duplicate=True),
Input({'type': 'dynamic-dropdown', 'index': ALL}, 'value'),
State('dynamic-container','children'),
prevent_initial_call = True,
)
def displayMultipleFigures(dropdownName,children):
# I have omitted the code that goes here but the problem stems regardless of it.
return children
Note that I have taken the help of this post to create the dynamic add/delete functionality (thanks AnnMarieW).