Skip to content

[Feature Request] Support pattern-matching/dict ids in dcc.Loading target_components #3348

@celia-lm

Description

@celia-lm

Desired behavior

This (or similar) would work:

dcc.Loading(
    html.Button(id={"type":"button", "index":"two"}, children='Button 2', disabled=True), 
    target_components={'{"type":"button","index":"two"}':'disabled'}
),

like this does:

dcc.Loading(
    html.Button(id="button_2", children='Button 2', disabled=True), 
    target_components={'button_2':'disabled'}
),

Current behavior

Screen.Recording.2025-06-26.at.13.49.48.mov

Code to reproduce and investigate the current behavior

from dash import callback, Dash, dcc, html, Input, Output, State, ctx
import time

app = Dash(__name__, suppress_callback_exceptions=True)

button_1 = html.Button(id={"type":"button", "index":"one"}, n_clicks=0, children='Trigger loading')
button_2 = html.Button(id={"type":"button", "index":"two"}, n_clicks=0, children='Button 2', disabled=True)
button_3 = html.Button(id="button_3", n_clicks=0, children='Button 3', disabled=True)

print({"type":"button", "index":"two"}) # {'type': 'button', 'index': 'two'} > option (7) should work
print({'type':'button', 'index':'two'}) # {'type': 'button', 'index': 'two'} > option (7) should work

radio_items = dcc.RadioItems(
    id="target_props",
    value="None",
    options=[
        {
            "label":"(0) No target components (default behaviot, two loading animations will appear)", 
            "value":"None"
            }, # None; any string would work 
        {
            "label":'(1) DOUBLE quotes WITH spaces after punctuation and INDEX first: target_components=\'{"index": "two", "type": "button"}\'', 
            "value":'{"index": "two", "type": "button"}'
            },
        {
            "label":'(2) DOUBLE quotes WITHOUT spaces after punctuation and INDEX first: target_components=\'{"index":"two","type":"button"}\'', 
            "value":'{"index":"two","type":"button"}'
            },
        {
            "label":'(3) DOUBLE quotes WITH spaces after punctuation and TYPE first: target_components=\'{"type": "button", "index": "two"}\'', 
            "value":'{"type": "button", "index": "two"}'
            },
        {
            "label":'(4) DOUBLE quotes WITHOUT spaces after punctuation and TYPE first: target_components=\'{"type": "button", "index": "two"}\'', 
            "value":'{"type":"button","index":"two"}'
            },
        {
            "label":"(5) SINGLE quotes WITH spaces after punctuation and INDEX first: target_components=\"{'index': 'two', 'type': 'button'}\"", 
            "value":"{'index': 'two', 'type': 'button'}"
            },
        {
            "label":"(6) SINGLE quotes WITHOUT spaces after punctuation and INDEX first: target_components=\"{'index':'two','type':'button'}\"", 
            "value":"{'index':'two','type':'button'}"
            },
        {
            "label":"(7) SINGLE quotes WITH spaces after punctuation and TYPE first: target_components=\"{'type': 'button', 'index': 'two'}\"", 
            "value":"{'type': 'button', 'index': 'two'}"
            },
        {
            "label":"(8) SINGLE quotes WITHOUT spaces after punctuation and TYPE first: target_components=\"{'type':'button','index':'two'}\"", 
            "value":"{'type':'button','index':'two'}"
            },
    ]
)

app.layout = html.Div([
    radio_items,
    html.Br(),
    dcc.Loading(button_1),
    dcc.Loading(button_2, id="loading_pm"),
    # target_components={{"type":"button", "index":"two"}:"disabled"}
    # this won't work because dictionaries can't be the keys of other dictionaries
    # it'll generate this error: TypeError: unhashable type: 'dict'
    dcc.Loading(button_3)
])

@app.callback(
    Output({"type":"button", "index":"two"}, 'disabled'),
    Output("button_3", 'disabled'),
    Input({"type":"button", "index":"one"}, 'n_clicks'),
    State("button_3", 'disabled'),
    prevent_initial_call=True)
def enable_button_filter(filter_query, disabled):
    print(ctx.outputs_list)
    # simulate a "long" process
    time.sleep(1)
    new_state = not disabled
    return new_state, new_state

@app.callback(
    Output("loading_pm", 'target_components'),
    Input('target_props', 'value'),
    prevent_initial_call=True
    )
def update_target_components(val):
    print({val:"disabled"})
    return {val:"disabled"}

if __name__ == '__main__':
    print(print(app.callback_map))
    # fragment of output for the first callback: 
    # '..{"index":"two","type":"button"}.disabled...button_3.disabled..' > option (2) should work
    # `{"index":"two","type":"button"}.disabled` > option (2) should work
    app.run(debug=True)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions