emilhe / dash-extensions

The dash-extensions package is a collection of utility functions, syntax extensions, and Dash components that aim to improve the Dash development experience
https://www.dash-extensions.com/
MIT License
409 stars 58 forks source link

`PrefixIdTransform` and `callback_context.triggered_id` #248

Open wuyuanyi135 opened 1 year ago

wuyuanyi135 commented 1 year ago

PrefixIdTransform will prefix all components' id and the Input, Output, State in callbacks. However, when callback_context is used to determine which Input fires, the triggered_id will be fixed, therefore not matching the id written in the callback function.

For example:

from dash_extensions.enrich import Output, DashProxy, Input, html, PrefixIdTransform, DashBlueprint, ALL, dcc, State, \
    callback_context

bp = DashBlueprint(transforms=[PrefixIdTransform('prefix')])
bp.layout = html.Div(
    [
        html.Button("Click me", id="btn-0"),
        html.Button("Click me", id="btn-1"),
        html.Button("Click me", id="btn-2"),
        html.Div(id="log")
    ]
)

@bp.callback(
    Output('log', 'children'),
    Input('btn-0', 'n_clicks'),
    Input('btn-1', 'n_clicks'),
    Input('btn-2', 'n_clicks'),
)
def func(nc0, nc1, nc2):
    if callback_context.triggered_id == 'btn-0' or callback_context.triggered_id == 'btn-1' or callback_context.triggered_id == 'btn-2':
        return "Button was triggered correctly"
    else:
        return f"callback_context.triggered_id = {callback_context.triggered_id}, therefore not triggered"

if __name__ == "__main__":
    app = DashProxy(__name__)
    app.layout = lambda: html.Div(
        [
            html.H1("Test"),
            bp.embed(app)
        ]
    )
    app.run_server(debug=True)

Is it possible to

  1. provide de-prefixed callback_context.triggered_id to the callback
  2. Or, provide some function if the PrefixTransform was used, that will automatically prefix the id strings used in the callback. For example
    @callback(
    ...,
    use_id_transformer=True
    )
    def cb(..., id_transformer):
    if callback_context.triggered_id == id_transformer("btn-0"):
        ......

The problem with manual prefixing the callback: if the blueprint is nested, like the example shown in the docs, we could not get the full prefix beforeahead.