Pattern-matching callbacks
ALL and MATCH keywords
In plotly dash, if components are dynamically generated, or there are too many input/output components,
one neat way to trigger from and update to those components in the callback is using pattern-matching,
which can deal with a list of components of the same type.
ALL keyword
An element can be specified with a composite id as in below.
The id is a dictionary with a 'type', and an 'index' (or 'id' works too), to identify the element
html.Button("Button A", id={'type': 'demo-btn', 'index': 'A'}, n_clicks=0),
html.Button("Button B", id={'type': 'demo-btn', 'index': 'B'}, n_clicks=0),
In the callback, it can accept all buttons, example below.
The callback is triggered by any of the button clicks and input all buttons (clicked or not) using the ALL keyword.
It requires getting the trigger id from ctx to tell which button is clicked.
Then outputs the text to the text field.
import dash
from dash import html, dcc, Input, Output, ctx, ALL
app = dash.Dash(__name__)
app.layout = html.Div([
# Buttons with pattern-matching IDs
html.Button("Button A", id={'type': 'demo-btn', 'index': 'A'}, n_clicks=0),
html.Button("Button B", id={'type': 'demo-btn', 'index': 'B'}, n_clicks=0),
html.Button("Button C", id={'type': 'demo-btn', 'index': 'C'}, n_clicks=0),
html.Br(),
# Display field
dcc.Input(id='output', type='text', value='', readOnly=True)
])
@app.callback(
Output('output', 'value'),
Input({'type': 'demo-btn', 'index': ALL}, 'n_clicks')
)
def handle_click(n_clicks_list):
triggered_id = ctx.triggered_id
if triggered_id is None:
return "No button clicked yet."
print(triggered_id)
return f"{triggered_id} was clicked."
if __name__ == '__main__':
app.run(debug=True)
MATCH keyword
sometimes, you don't want to receive all buttons as input, but only the one that is clicked.
The MATCH keyword associated with the input selects only the clicked button.
However, as soon as using MATCH keyword in the input, all outputs must follow the same pattern to use the MATCH keyword as well.
It selects the corresponding output based on the 'index' / 'id' of the 'MATCH'ed input.
An example is using one output text field for each of the buttons.
clicking a button will update the text in the corresponding text field.
If there is only one text field for output, this pattern does NOT work. All inputs and outputs must use the MATCH keyword.
import dash
from dash import html, dcc, Input, Output, ctx, MATCH
app = dash.Dash(__name__)
app.layout = html.Div([
# Buttons with pattern-matching IDs
html.Button("Button A", id={'type': 'demo-btn', 'index': 'A'}, n_clicks=0),
html.Button("Button B", id={'type': 'demo-btn', 'index': 'B'}, n_clicks=0),
html.Button("Button C", id={'type': 'demo-btn', 'index': 'C'}, n_clicks=0),
html.Br(),
# Display fields
dcc.Input(id={'type': 'output', 'index': 'A'}, type='text', value='', readOnly=True),
dcc.Input(id={'type': 'output', 'index': 'B'}, type='text', value='', readOnly=True),
dcc.Input(id={'type': 'output', 'index': 'C'}, type='text', value='', readOnly=True)
])
@app.callback(
Output({'type': 'output', 'index': MATCH}, 'value'),
Input({'type': 'demo-btn', 'index': MATCH}, 'n_clicks')
)
def handle_click(n_clicks_list):
triggered_id = ctx.triggered_id
if triggered_id is None:
return "No button clicked yet."
print(triggered_id)
return f"{triggered_id} was clicked."
if __name__ == '__main__':
app.run(debug=True)