Precog
January 11, 2022, 11:14am
1
Hi guys,
Was wondering if its possible to have a callback on a list group (generated dynamically) and know which ListGroupItem was clicked.
For example,
list_group = dbc.ListGroup(
[
dbc.ListGroupItem("Item 2", action=True),
dbc.ListGroupItem("Item 2", action=True),
dbc.ListGroupItem("Item 3", action=True"),
],
id="list-group",
)
Hi,
Here’s one possible approach using pattern-matching callbacks and callback_context:
listgroup = dbc.ListGroup(
[
dbc.ListGroupItem(
f"Item {i}",
id={"type": "list-group-item", "index": i},
action=True
)
for i in range(3)
],
id="list-group",
)
app.layout = html.Div(
[
listgroup,
html.Div(id="test")
]
)
@app.callback(
Output("test", "children"),
Input({'type': 'list-group-item', 'index': ALL}, 'n_clicks'), # from dash import ALL # in v2
State("list-group", "children"),
prevent_initial_call=True
)
def update(n_clicks_list, children):
# from dash import callback_context # in v2
clicked_id = callback_context.triggered[0]["prop_id"]
# Parsing it
clicked_id = int(json.loads(clicked_id.split(".")[0])["index"])
return f"Clicked on {children[clicked_id]}"
I adapted from an old example where I showed how the props in the specific child being clicked are available via State
, but you might not need it…
1 Like
Precog
January 11, 2022, 10:35pm
3
Great…that will do the job.
Is there a way to make the listgrouptitem active depending on which one was clicked?
I guess you can pass Output({'type': 'list-group-item', 'index': ALL}, 'active')
and return a list of booleans where the index clicked_id
is True
and the rest False
.
Precog
January 13, 2022, 1:42am
5
Thanks mate… I’ll look into it
dales
June 15, 2022, 12:21pm
6
this works for me
import json
import dash_bootstrap_components as dbc
from dash import html, Output, Input, State, ALL, callback_context
from app import app
data_selector = html.Div(
[
dbc.Label("Data Selector", html_for="input-data-selector"),
dbc.ListGroup([
dbc.ListGroupItem(name, active=False, action=True, id={"type": "list-group-item", "index": i})
for i, name in enumerate([
"Item 1",
"Item 2",
"Item 3",
"Item 4"
])],
)
],
id="input-data-selector",
className="mb-3",
)
view_selector = html.Div([
])
@app.callback(
Output({'type': 'list-group-item', 'index': ALL}, 'active'),
Input({'type': 'list-group-item', 'index': ALL}, 'n_clicks'), # from dash import ALL # in v2
State("input-data-selector", "children"),
prevent_initial_call=True
)
def update(n_clicks_list, children):
clicked_id = callback_context.triggered[0]["prop_id"]
clicked_id = json.loads(clicked_id.split(".")[0])["index"]
return [clicked_id == i for i in range(len(n_clicks_list))]
1 Like
These are two nice examples!
As of dash 2.4, it’s even easier to work with pattern matching dictionary id’s in a callback.
ctx.triggered_id
is the id of the component that triggerd the callback, and if it’s a pattern matching callback, it’s a dictionary.
Here is the ctx.triggered_id
If you click on the first item in the example apps:
{'index': 0, 'type': 'list-group-item'}
You can also access the dict with the dot notation, so here’s how to get the index:
ctx.triggered_id.index
Here’s a complete example:
import dash_bootstrap_components as dbc
from dash import Dash, html, Output, Input, ALL, ctx
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
listgroup = dbc.ListGroup(
[
dbc.ListGroupItem(
f"Item {i}",
id={"type": "list-group-item", "index": i},
action=True
)
for i in range(3)
],
id="list-group",
)
app.layout = html.Div(
[
listgroup,
html.Div(id="test")
]
)
@app.callback(
Output("test", "children"),
Input({'type': 'list-group-item', 'index': ALL}, 'n_clicks'),
prevent_initial_call=True
)
def update(_):
return f"Clicked on Item {ctx.triggered_id.index}"
if __name__ == "__main__":
app.run_server(debug=True)
1 Like