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

handle multiple Outputs in app.callback #149

Closed
sdementen opened this issue Oct 27, 2017 · 20 comments
Closed

handle multiple Outputs in app.callback #149

sdementen opened this issue Oct 27, 2017 · 20 comments

Comments

@sdementen
Copy link

It would be nice to have the ability to update multiple outputs at once by specifying a list of Outputs and returning a tuple with the same number of outputs like in

@app.callback(
     [Output('label1', 'children'),
      Output('label2', 'children'),
     ],
     [Input('dropdown', 'value'),
      ])
 def cb_test_multi(value):
     # some very complex calculation to be triggered only once when 'dropdown' changes
     ...
     # result of calculation need to be pushed partly on 'label1' and partly on 'label2'
     return "Hello", value

This would allow simplifying otherwise complex flow to update multiple components based on one change.

@havok2063
Copy link

I second this. This would be incredibly useful. I like the design and look of Dash over alternate programs but the complexity of implementing this is holding me back from fully jumping in. It makes cross-linking multiple plots incredibly difficult compared to Bokeh or Holoviews.

@chriddyp
Copy link
Member

chriddyp commented Oct 27, 2017

Makes sense to me!
The app.callback syntax and the HTTP API were designed in such a way to allow something like this. I left this out of the initial release because I thought that it would cause some confusion among users (mainly: when do you use multiple callbacks vs multiple outputs) and I wanted to keep the surface area as small as possible for the initial release. There are some other alternatives to "sharing computations" among multiple callbacks (see https://plot.ly/dash/sharing-data-between-callbacks) but none of them are as simple as this proposal.

By introducing multiple outputs, there will be 2 ways to do the same thing: 2 outputs in a single callback vs 2 callbacks with a single output. It really only makes to use multiple outputs if the callback is expensive.

I can also imagine that some users will end up using multiple outputs in cases where two callbacks that executed in parallel would end up resulting in faster UI updates, for example:

@app.callback([Output(...), Output(...)], ...)
def update_outputs(*inputs):
    val_1 = expensive_computation_1(*inputs)
    output_1 = make_output(val_1)

    # if this were a separate callback, the user could return output_1 at this point
    # instead, they have lumped together all of the outputs into one function, 
    # blocking the UI from updating until all outputs are ready
    val_2 = expensive_computation_2(*inputs)
    output_2 = make_output(val_2)
    return [output_1, output_2]

In any case, I'm 👍 with this now. It'll be a bit of work to implement, so if you would like to see this work please 👍 this issue. If you or your company needs this work expedited and can sponsor development, please reach out: https://plot.ly/products/consulting-and-oem/

@sdementen
Copy link
Author

btw, as much as Dash is a next-gen framework for full python UI on the browser, you may check APIStar (https://github.com/encode/apistar) that is a next-gen framework for Web API in python (after Django REST Framework, Flask, ...)
==> they took some very interesting way of crafting their API (type annotations instead of decorators to specify the arguments of the endpoint). As I discovered Dash and APIStar at the same time, I saw some possible cross-inspiration for Dash. Tell me what you think of the APIStar style and if you see some fit with Dash

@dskarbrevik
Copy link

dskarbrevik commented Jul 11, 2018

Was this idea ever implemented?

I have an app that has 200 buttons. I'd rather append a list of outputs as I make the buttons and pass that to a single callback instead of having to write out 200 callback functions (as this would make my code very long/ugly).

@chriddyp
Copy link
Member

chriddyp commented Jul 11, 2018

Was this idea ever implemented?

It hasn't been, but it's still a good idea and I think we'll end up implementing before the end of the year.

(as this would make my code very long/ugly).

What I've done in these types of situations is create a look that generates these functions:

def create_callback(id):
    def respond_to_button(*callback_args):
        ...

    return respond_to_button


# Dash callbacks have to be defined up-front and therefore must anticipate
# all of the elements that might exist on the page
for i in range(100):
    app.callback(
        Output('output-{}'.format(i), 'some-property'),
        [Input('button-{}'.format(i), 'n_clicks')])(
    create_callback(i))

@danigallo
Copy link

Hi @chriddyp, you mentioned the possibility of implementing this "next month" a few weeks ago. Do you have a specific date in mind?

@chriddyp
Copy link
Member

chriddyp commented Aug 6, 2018

you mentioned the possibility of implementing this "next month" a few weeks ago. Do you have a specific date in mind?

I do not. I will update this issue when we have a concrete date planned.

@mkhorton
Copy link

mkhorton commented Aug 7, 2018

With regards to surface area, it could be a config option, e.g. enable_multiple_outputs -- requiring the user to explicitly turn it on will hopefully signpost it's for more advanced use, while keeping things simple for new users.

(I'm currently using @cache.memoize() on an expensive computation that's called by two callbacks/updates two outputs, but it feels like a bit of a hack)

@FunmiKesa
Copy link

you mentioned the possibility of implementing this "next month" a few weeks ago. Do you have a specific date in mind?

I do not. I will update this issue when we have a concrete date planned.

@chriddyp
Do you have an update on this issue?

@cheak1974
Copy link

Is there any progress regarding this issue? I would like to start an app with a lot of text field that have to be updated after user interaction. Without multiple updates it would be really a reason to abandon dash due to very complicated and overloaded function usage.

@T4rk1n
Copy link
Contributor

T4rk1n commented Nov 15, 2018

@cheak1974 It's coming #436

@prasadovhal
Copy link

I have tried this multiple output but I got an error as
dash.exceptions.IncorrectTypeException: The output argument [<dash.dependencies .Output object at 0x000000000804F898>, <dash.dependencies.Output object at 0x000 000000B16D748>] is not of type dash.Output.

Anyone has same issue?

@T4rk1n
Copy link
Contributor

T4rk1n commented Dec 28, 2018

@prasadovhal You tried the versions in #436 ?

pip install dash==0.36.0rc1
pip install dash-renderer==0.17.0rc1

@alexcjohnson
Copy link
Collaborator

Merged - will be in the next release, expected early next week 🎉

@iQrus
Copy link

iQrus commented Oct 18, 2019

Is there any update on this? Do we have this feature now?

@mkhorton
Copy link

Yes! It’s in release :) I’ve been a happy user of this feature for some time

@alexcjohnson
Copy link
Collaborator

Check out the "Multiple Outputs" section of https://dash.plot.ly/getting-started-part-2

@iQrus
Copy link

iQrus commented Oct 22, 2019

@mkhorton @alexcjohnson Thanks a ton guys for the prompt response.

@ghost
Copy link

ghost commented May 22, 2020

How to get 0 outputs but only an eg Interval input? This errors:

    @app.callback([],[d_dep.Input('interval_1', 'n_intervals')])
    def dosome(input1):
        pass

i prefer dash clientside over jpnotebooks

@alexcjohnson
Copy link
Collaborator

0 outputs doesn't really fit with the dash concept of a stateless back end - that's why it's an error. It's not an error with pattern-matching callbacks that just happen to match zero items, but then the callback won't even fire. If you really want a callback like this to fire I would suggest a dummy element - a hidden div, or a dcc.Store that you never use, something like that, and end the callback with a raise PreventUpdate or return no_update to short-circuit any re-rendering for performance purposes.

AnnMarieW pushed a commit to AnnMarieW/dash that referenced this issue Jan 6, 2022
* Upgrade plotly.js to v1.33.0

* v0.18.0

* update links
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests