Skip to content

handle multiple Outputs in app.callback #149

Closed

Description

@sdementen

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.

Activity

  1. havok2063 commented on Oct 27, 2017

    @havok2063

    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.

  2. chriddyp commented on Oct 27, 2017

    @chriddyp
    Member

    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/

  3. sdementen commented on Oct 29, 2017

    @sdementen
    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

  4. dskarbrevik commented on Jul 11, 2018

    @dskarbrevik

    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).

  5. chriddyp commented on Jul 11, 2018

    @chriddyp
    Member

    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))
  6. danigallo commented on Aug 6, 2018

    @danigallo

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

  7. chriddyp commented on Aug 6, 2018

    @chriddyp
    Member

    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.

  8. mkhorton commented on Aug 7, 2018

    @mkhorton

    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)

  9. FunmiKesa commented on Oct 12, 2018

    @FunmiKesa

    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?

  10. cheak1974 commented on Nov 15, 2018

    @cheak1974

    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.

  11. T4rk1n commented on Nov 15, 2018

    @T4rk1n
    Contributor

    @cheak1974 It's coming #436

  12. prasadovhal commented on Dec 28, 2018

    @prasadovhal

    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?

  13. T4rk1n commented on Dec 28, 2018

    @T4rk1n
    Contributor

    @prasadovhal You tried the versions in #436 ?

    pip install dash==0.36.0rc1
    pip install dash-renderer==0.17.0rc1
    
  14. alexcjohnson commented on Mar 1, 2019

    @alexcjohnson
    Collaborator

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

  15. iQrus commented on Oct 18, 2019

    @iQrus

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

  16. mkhorton commented on Oct 18, 2019

    @mkhorton

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

  17. alexcjohnson commented on Oct 18, 2019

    @alexcjohnson
    Collaborator

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

  18. iQrus commented on Oct 22, 2019

    @iQrus

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

  19. alexcjohnson commented on May 22, 2020

    @alexcjohnson
    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions