-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Determining Which Input Has Been Fired #291
Comments
Just spitballing here, not actually sure how much I'm into this. One approach could be to use a request object that is passed into the callback functions, as is common with many web frameworks, and then retrieve the component registrations from the request using app.layout = html.Div([
html.Div(id='target'),
dcc.Input(id='my-input', type='text', value=''),
html.Button(id='submit1', n_clicks=0, children='Submit 1')
html.Button(id='submit2', n_clicks=0, children='Submit 2')
])
@app.callback([Output('target', 'children')],
[Input('submit', 'n_clicks'), Input('submit2', 'n_clicks')],
[State('my-input', 'value')])
def callback(request):
my_input = request.state['my-input.value']
if my_input.has_changed:
result = f"Input {request.trigger.id} triggered callback; {my_input.id} changed value from {my_input.prev_value} to {my_input.value}"
else:
result = f"Input {request.trigger.id} triggered callback; {my_input.id} did not change value."
return result This also solves the problem of managing unwieldy lists of Input/State that you need to align with the callback function arguments, as I describe as being an issue in #159. However this would likely mean either a non-backwards compatible change to callback function signatures, or we have two callback functions, the previous simple list of argument values alongside the the new request-based one. |
One option at our disposal is checking the number or even the type of arguments in the That is, roughly: def callback(output, inputs, states):
def scoped_wrapper(func):
def wrapper(*args, **kwargs):
if len(args) == 1 and (len(inputs) + len(states) > 1)
# e.g. callback signature type 1
request = {
'inputs': inputs,
'states': states
}
func(request)
else:
# e.g. existing callback signature
return func(*(inputs + states)) In your example, we'd some way to differentiate between a callback with a single input and a callback with a single input that uses the |
Why not make 2 options available for the user to choose from? With @app.callback(output, inputs, states, as_request=True) assume signature It may be also good idea to allow configuring |
Ah, good point @chriddyp. Polymorphism through decorators! If we went down the path of a request-like context object, this could well be a good approach to supporting it alongside the original callback signature. |
Note that in general, I'm looking for solutions that are unified and ideally backwards compatible. As in the zen of python, there should be one way to do things. |
You could add another optional parameter to the callback decorator: |
Got a prototype working... import dash
import dash_html_components as html
from dash.dependencies import Output, Input
from dash.exceptions import PreventUpdate
app = dash.Dash(__name__)
BUTTONS = ['btn-{}'.format(x) for x in range(1, 6)]
app.layout = html.Div([
html.Div([
html.Button(x, id=x) for x in BUTTONS
]),
html.Div(id='output'),
])
@app.callback(Output('output', 'children'),
[Input(x, 'n_clicks') for x in BUTTONS])
def on_click(*args):
if not dash.callback.triggered:
raise PreventUpdate
trigger = dash.callback.triggered[0]
input_value = dash.callback.inputs.get(trigger)
return 'Just clicked {} for the {} time!'.format(trigger, input_value)
if __name__ == '__main__':
app.run_server(debug=True, port=9091) |
Hi @T4rk1n, |
@hoangmt this solution has not been published to PyPI yet, it's a WIP at the two pull requests linked just above. |
@alexcjohnson my bad. Can't wait to see how it works. :D |
Should the above example by @T4rk1n replaced by this https://github.com/plotly/dash-docs/blob/87c7afd2267bc4b195a1c61ed2c422b043485502/tutorial/examples/faqs/last_clicked_button.py? |
You mean the change from |
This was helpful! Looks like it's been added to the "FAQs" here: https://dash.plot.ly/faqs |
It seems to be missing from the FAQs now. However, I do find this information here (https://dash.plotly.com/advanced-callbacks) under "Determining which Input has fired with dash.callback_context" |
We need a way to determine which input has changed. We've provided a temporary hack with
n_clicks_timestamp
but we need something that is more general.I'm not worried about the implementation, it fits into the
dash-renderer
architecture nicely.What's not as clear to me is what the
app.callback
decorated functions should look like. How does this interface scale when we want to add more things like:figure.layout.title
)So, let's use this thread to just propose different interfaces to the callbacks. Of course, doing this in a way that would be backwards compatible is preferred.
cc @plotly/dash
The text was updated successfully, but these errors were encountered: