ListGroup Callback - Possible?

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

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.

Thanks mate… I’ll look into it

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. :tada:

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)