dash
dash copied to clipboard
Remove Callback Wildcard Restrictions (MATCH)
Is your feature request related to a problem? Please describe. In trying to make my applications as modular as possible, I tend to use pattern-matching callbacks just about everywhere. There are many instances, however, where I need to be able to detect which component was triggered (via MATCH) and update a universal component.
Describe the solution you'd like
I would love for the restriction on the MATCH wildcard that requires the same number of MATCH values in the Input ID's and Output ID's to be lifted, allowing for more wildcards in the input than the output. It's my understanding that today's release of allow_duplicate
in Dash's Output component should open the door to adding in the aforementioned solution.
Describe alternatives you've considered The current solution for this use case is to use the ALL wildcard along with the callback context to filter down to the content that I'm ultimately interested in, but that solution does not scale especially well as it requires a lot of information to pass through the network, slowing things down.
Thanks @milind
@T4rk1n I think "any MATCH
present in the inputs must be present in all the outputs too" is another flavor of the restriction "every output can be connected to only one callback" - it's just that these duplicates come from the same function. So in the same way as we do for multiple callback functions we should be able to relax that restriction when the output that's missing a MATCH
specifies allow_duplicate
. Seem reasonable?
To make this concrete, I guess we're talking about callbacks that would look like:
@callback(
Output("out", "children"),
Input({"id": MATCH}, "n_clicks"),
State({"id": MATCH}, "id"),
prevent_initial_call=True
)
def cb(_, _id):
return f"you clicked the button with id {_id['id']}"
I think the validation should be lifted entirely for Output, it makes MATCH
awkward to use and I remember removing the validation without any breakage.
It would have exactly the same kind of breakage scenarios as other duplicate outputs, an ambiguity when two callbacks result from the same stimulus. Which is why I think allowing it the same way (with allow_duplicate
) makes most sense.
I would also like this restriction to be lifted. I'm trying out a convention where callbacks which modify database state triggers a re-render using a "trigger" output. This does not work with this restriction in place.
For searchability - this is the error message:
`Input` / `State` wildcards not in `Output`s
or
Output X
does not have MATCH wildcards on the same keys as
Output Y
MATCH wildcards must be on the same keys for all Outputs. ALL wildcards need not match, only MATCH
Someone else mentioned that they would like to do partial update based on a MATCH callback. On the surface this also sounds like a reasonable use-case (with the disclaimer that I haven't used partial update enough to really know it makes sense).
EDIT: Thinking a bit further - any scenerio where doing something to a component[1] should trigger a not-local-to-that-component update become cumbersome to implement. Only workaround (other than the one mentioned by OP) I can see is to add dummy outputs per component and route these to the callback implementing the global update.
[1] Which there can be an arbitrary number of
A current workaround to avoid the network cost of passing ALL to the callback would be:
- to use ALL in a clientside callback, storing the information about the triggered input(s) at a level with as many MATCH as the desired Output
- use the above store in a serverside callback to update the desired Output
Something like:
clientside_callback(
"""() => {
return dash_clientside.callback_context.triggered.map(t => JSON.parse(t.prop_id.split(".")[0]))
}"""
Output("input_store", "data"),
Input({"type": "btn", "id": ALL}, "n_clicks"),
)
@callback(
Output("output", "children"),
Input("input_store", "data"),
)
def myfunc(triggered_inputs_data):
# ...
I would appreciate this restriction lifted too, as it would simplify my callbacks. I was so surprised it doesnt work when I tried to write similiar callback as you did.
I second the request, as this limitation if often problematic for our use cases.
I agree. The current behavior is counterintuitive, as you can have the same output in multiple callbacks, and this would simplify many patterns.
Any updates on this?
Any updates on this?
Plotly team is looking into this as part of a future Dash release!
Another example use case:
@callback(
Output("notifications_container", "children", allow_duplicate=True),
Output({"id": MATCH, "type": "grid"}, "rowData", allow_duplicate=True),
Input({"id": MATCH, "type": "grid"}, "cellValueChanged")
)
def on_edit_grid_callback(event):
return on_edit_grid(event)
A current workaround to avoid the network cost of passing ALL to the callback would be:
- to use ALL in a clientside callback, storing the information about the triggered input(s) at a level with as many MATCH as the desired Output
- use the above store in a serverside callback to update the desired Output
Something like:
clientside_callback( """() => { return dash_clientside.callback_context.triggered.map(t => JSON.parse(t.prop_id.split(".")[0])) }""" Output("input_store", "data"), Input({"type": "btn", "id": ALL}, "n_clicks"), ) @callback( Output("output", "children"), Input("input_store", "data"), ) def myfunc(triggered_inputs_data): # ...
Actually, this works really well for me! I just want to know the ID of the button that's clicked, in an efficient way. Awesome 👍👍
There is a new workaround for outputting with MATCH callback to unmatched ids with the new set_props
.
@callback(
Output({"id": MATCH, "type": "grid"}, "rowData", allow_duplicate=True),
Input({"id": MATCH, "type": "grid"}, "cellValueChanged")
)
def on_edit_grid_callback(event):
set_props("notifications_container", {"children": f"edited: {ctx.triggered_id}"})
return on_edit_grid(event)