Skip to content
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

[Feature Request] simplified syntax when several Inputs can trigger a callback #1054

Closed
emmanuelle opened this issue Dec 19, 2019 · 4 comments
Assignees
Labels
feature something new
Milestone

Comments

@emmanuelle
Copy link

With Dash, it is not possible to have multiple callbacks modifying a given Output (see #153). The reason is that if this were possible, one could imagine having several callback sharing as Input the same property and modifying the same Output, and one could not decide which one to fire.

Therefore, the solution is to group all the inputs in a big callback, and since a different behaviour is often expected depending on which Input fired the callback, one ends up with a quite verbose syntax such as

ctx = dash.callback_context
if not ctx.triggered:
    button_id = 'No clicks yet'
else:
    button_id = ctx.triggered[0]['prop_id'].split('.')[0]

if button_id == 'button1':
...
elif ....

@alexcjohnson suggested that we could introduce a Trigger object which would be a variant of Input. It would be called as Trigger("button1", "n_clicks") and would feed the callback True (if that was the trigger) or False. Therefore we could write callbacks like

@app.callback(...
)
def update(arg1_changed, arg2_changed)
    if arg1_changed:
          return update_output_arg1(...)
    elif arg2_changed:
          return update_output1_arg2(...)

As a side note it would be great to update our documentation to encourage more breaking callbacks into smaller functions like above, so that a multiple-input callback is more a router.

A variant of the Trigger would be to have a tuple (triggered_bool, prop_value) so that one would write arg1_changed[0] for the bool and arg1_changed[1] for the value. (Therefore we would not need to use callback_context at all).

@emilhe
Copy link
Contributor

emilhe commented Sep 10, 2020

I am considering making an attempt to implement a Trigger component. However, in terms of syntax, i was thinking about simply removing the argument from the callback, i.e. the syntax would be,

@app.callback(Output("output_id", "output_prop"), Trigger("button", "n_clicks"))
  def func():  # note that "n_clicks" is not included as an argument 

What do you think about this kind of syntax? I made a demo implementation in dash-extensions, you can try it out if you like :)

https://pypi.org/project/dash-extensions/

The problem about "unwrapping the trigger" being cumbersome could be (at least partly) solved by introducing a utility function, similar to get_triggered in dash-extensions. I am not sure where it should be located in Dash though...

@alexcjohnson
Copy link
Collaborator

removing the argument from the callback

That works if there's only one input - though in that case the existing Input would work just as well, wouldn't it? But it's very common to have multiple possible triggers, as in the pattern @emmanuelle showed at the beginning of this issue, and to support that case I think it's important to include the boolean as an argument to the function.

@emilhe
Copy link
Contributor

emilhe commented Sep 11, 2020

Multiple triggers (and triggers mixed with inputs) is supported. The only difference (in my current implementation) between trigger and inputs is that the argument (‘n_clicks’) is not passed to the function for s trigger, i.e. you still have to figur out which trigger/input triggered the callback.

@alexcjohnson
Copy link
Collaborator

We ended up deciding Trigger as an alternative to Input had too many complications - instead @AnnMarieW extended callback_context to be easier to use for these cases in #1952 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature something new
Projects
None yet
Development

No branches or pull requests

5 participants