📣 Multiple outputs in Dash - Now Available!

:mega: Announcing multi output!

Update multiple component props from a single callback!

Try now:

$ pip install dash==0.39.0

See more features in Dash 0.39.0 release notes: 📣 Dash 0.39.0 released.

Features

  • Use a list/tuple of Output as output in callbacks.
  • Return a tuple/list of value from the callbacks
    • The returned list must have the same length as the list of output
    • The output props are applied in the order they were declared in the output list.

Example

import dash
import dash_core_components as dcc
import dash_table as dt
import dash_html_components as html

from dash.dependencies import Output, Input
from dash.exceptions import PreventUpdate

import plotly.graph_objs as go

sample_data = {
    'series': {
        'data': [
            {'title': 'Game of Thrones', 'score': 9.5},
            {'title': 'Stranger Things', 'score': 8.9},
            {'title': 'Vikings', 'score': 8.6}
        ],
        'style': {
            'backgroundColor': '#ff998a'
        }
    },
    'movies': {
        'data': [
            {'title': 'Rambo', 'score': 7.7},
            {'title': 'The Terminator', 'score': 8.0},
            {'title': 'Alien', 'score': 8.5}
        ],
        'style': {
            'backgroundColor': '#fff289'
        }
    }
}

app = dash.Dash(__name__)

app.layout = html.Div([
    html.H1('Multi output example'),
    dcc.Dropdown(id='data-dropdown', options=[
        {'label': 'Movies', 'value': 'movies'},
        {'label': 'Series', 'value': 'series'}
    ], value='movies'),
    html.Div([
        dcc.Graph(id='graph'),
        dt.DataTable(id='data-table', columns=[
            {'name': 'Title', 'id': 'title'},
            {'name': 'Score', 'id': 'score'}
        ])
    ])
], id='container')


@app.callback([
    Output('graph', 'figure'),
    Output('data-table', 'data'),
    Output('data-table', 'columns'),
    Output('container', 'style')
], [Input('data-dropdown', 'value')])
def multi_output(value):
    if value is None:
        raise PreventUpdate

    selected = sample_data[value]
    data = selected['data']
    columns = [
        {'name': k.capitalize(), 'id': k}
        for k in data[0].keys()
    ]
    figure = go.Figure(
        data=[
            go.Bar(x=[x['score']], text=x['title'], name=x['title'])
            for x in data
        ]
    )

    return figure, data, columns, selected['style']


if __name__ == '__main__':
    app.run_server(debug=True, port=8056)

Feedback welcome!

51 Likes

Thanks Philippe!!

This is going to make a big impact on a lot of apps I have where 1 input is used in many callbacks where the output components aren’t close to each other in the layout or updating the parent layout messes with user state in an unacceptable way.

2 Likes

Oh my god thank you so much. This is a game changer

2 Likes

This is awesome! Much appreciated for all the hard work that went into this!

2 Likes

wow nice!! this is indeed game changer. In all my dash apps I’ve needed to do very complicated things with local storage in order to achieve multioutput. Thanks a lot Philippe!!

1 Like

This is great! I’ve been looking forward to this. Is 38rc1 a safe version? When is the release of 38?

Also, if I use conda instead of pip, will I be even further out from getting the release code? Seems like the conda packages are updated later? I’m relatively new to Python so curious if anyone knows how that works. I’m just shy about upgrading to a release candidate version as I have some things I want to make sure don’t break.

Awesome! Thanks, this is really helpful :muscle:

I’m very excited about this coming change, and it’ll dramatically streamline some of the work we’re doing. I see it didn’t make it into 0.38.0. Any ideas on when we can hope to see it released?

2 Likes

Yes would be great to use and the dash callback and the multiple output features. Would it be possible to create a rc version of this?

This is the best possible update imaginable! Right now I have to create a lot of repetitive functions that SHOULD be executed at the same time - i.e. when one thing changes, a lot of things need to update simultaneously - but right now they update sequentially, which looks bad as some of the operations take a second or so.

This is also huge for minimizing memory and processing - if you have to pull the same data over and over and do the same conversions, it’s a drag to copy and paste or even to pull from a Store every time.

Great update, thanks!

This is awesome. I’m also interested in the inverse; multiple callbacks changing the same output. I often have multiple things that could change the state of some component and putting the logic for all of them in a single callback makes code development and maintenance awkward, since they aren’t logically related actions. I have no idea the technical complexity permitting that would imply, but is that something plausible in the future, or am I just doing something wrong in how I think about my code architecture?

This is already possible isn’t it? Multiple inputs for single output.

Sorry I wasn’t clear enough. I mean that I want two different callbacks to have the same output. Each callback handles a different kind of event that should trigger a change in some component. At the moment I have to put all the inputs into the same callback, put a bunch of logic in to figure out which input it was that fired the callback and then execute different update code accordingly.

We now have callback_context (just released!) that will tell you which input changed. See “How do I determine which input changed” in the faqs section: FAQs | Dash for Python Documentation | Plotly

6 Likes

Also a conda user and wondering how long it usually takes before updates are available through conda forge?

I haven’t seen many people talk about examples of using this new feature so I thought I would chime in: I have found this particularly useful for when I’m working with dependent dropdowns. Before, I had to create one callback to update the options and another to update the value of the dependent dropdown, now I can do this all in one callback! I wouldn’t say that this is a radical change, but it does allow for more elegant and cleaner code :slight_smile:

1 Like

awesome! ! ! :heart_eyes:

2 Likes

Oh this is a god send. I was searching for “Multiple Output in Python Dash” and all previous searches result in negative findings and suddenly I see this one. Immediately improved my coding life.

3 Likes

I get an error when I try it:
File “app.py”, line 114, in <module>

], [Input(‘data-dropdown’, ‘value’)])

File “/anaconda3/lib/python3.7/site-packages/dash/dash.py”, line 886, in callback

self._validate_callback(output, inputs, state)

File “/anaconda3/lib/python3.7/site-packages/dash/dash.py”, line 676, in _validate_callback

name.lower(), str(arg), name

dash.exceptions.IncorrectTypeException: The output argument [&lt;Outputgraph.figure&gt;, &lt;Outputdata-table.data&gt;, &lt;Outputdata-table.columns&gt;, &lt;Outputcontainer.style&gt;] is not of type dash.Output.

3 Likes

the multiple outputs is great but i face some issue.
if i have a lots of outputs, it need a way to control which output i don’t want to update .
the code maybe like as follows:

@app.callback([
    Output('graph', 'figure'),
    Output('data-table', 'data'),
    Output('data-table', 'columns'),
    Output('container', 'style')
], [Input('data-dropdown', 'value')])
def multi_output(value):
    if condition_1:
        ...
        return [preventupdate, b, c, d]
    elif condition_2:
       ...
       return [a, preventupdate, c, d]
    else:
       ...
       return [a, b, c, preventupdate]