Hi,
I’m encountering what seems to be a bug with EventListener or perhaps I’m just not understanding it well enough.
tl;dr
I explicitly set the EventListener for clicks in the layout and specify the children to be a dropdown component which is already in the initial loading of the app, I then use a separate callback to dynamically update the EventListener children component - to my surprise - (1) it doesn’t update the children (seemingly because clicks to dropdown1 are still triggering eventlistener) and 2 all clicks on the page are firing the callbacks. Documentation says lack of child specification would cause this but … not sure why it would work out to this if it’s been specified.
See the MRE below and context why I can’t just bypass the EventListener and feed Input directly
import dash
from dash import html, dcc, Input, Output
from dash_extensions import EventListener
from dash.exceptions import PreventUpdate
app = dash.Dash(__name__, suppress_callback_exceptions=True)
event = {"event": "click","props": ["srcElement.className", "srcElement.innerText"]}
# Layout with the initial dropdown and EventListener
app.layout = html.Div([
dcc.Dropdown(id='dropdown1', options=[{'label': i, 'value': i} for i in ['1dropdown', '2dropdown']], value='Option 1'),
html.Div(id='dd2container'),
html.Div(EventListener(id='listener1',events = [event],logging=True, children = 'dropdown1')),
html.Div(id='log')
])
# Callback to dynamically update the EventListener children based on the selection in dropdown1
@app.callback(
Output('dd2container', 'children'),
Input('dropdown1', 'value')
)
def update_event_listener(value):
if value == '2dropdown':
# If Option 1 is selected, return dropdown2
return html.Div(dcc.Dropdown(id='dropdown2', options=[{'label': i, 'value': i} for i in ['A', 'B']], value='A'))
else:
# If no option is selected, return an empty div
return html.Div()
# Callback to handle events in dropdown2
@app.callback(
Output('listener1', 'children'),
Input('dropdown1', 'value')
)
def update_eventlistener(value):
print("FIRED")
if value:
return "dropdown2"
else:
return "dropdown1"
@app.callback(
Output('log', 'children'),
Input("listener1", "n_events"),
)
def eventlistenertester(value):
print("FIRED")
if value:
return "Number of Clicks{}".format(value)
else:
return "No Clicks"
if __name__ == '__main__':
app.run_server(debug=True)
Here’s the situation:
I have a dash app where I have 3 dropdowns - though the page initializes with just 1, selecting a value for dropdown 1 creates a 2nd dropdown and so on and so forth this is because the dropdown options are chained so to speak. These dropdowns feed callbacks that query databases and generate figures. I need to make them flexible enough such that they can generate the plots as soon as 1 of the dropdowns is selected.
Because so much of the layout is dynamically created, there’s a ton of div nesting and one consequence is that I can’t specify an input that is yet to be created. I have to rely on EventListener because for some reason one of my div containers isn’t firing a callback when the contents of the div container (a dcc.Dropdown selection) changes.
Thanks!