Why can an output only have a single callback function?

For now, see Dash Documentation & User Guide | Plotly for some strategies to store the result of the computations instead of re-recomputing it.

Otherwise, I’m hoping we’ll find room in our roadmap to add support for multiple outputs in the next few months.

Thanks. That would be greatly appreciated. I like to allow users to click on charts and propagate that information to other graphs, quite often n-to-1, so keeping track of the history to determine which clickdata is updated is a bit tedious and would be extremely elegant and simple wiht multiple outputs

1 Like

I ran into the same problem. I have background in native app development and am new to web development. I have this use case where there are two views I want to toggle in the same nested html.Div. When user clicks a button in the first view, the Div switches to contain the second view with different set of buttons, and disables a bunch of other components on the same page. When user clicks a button in that second view, the Div reverts to the first view and re-enables those those disabled components. (Reason for this: It enforces certain user input flow and greatly reduces the amount of logic it takes to handle weird input flow that users always manage to come-up with.) Due to the one-callback-per-output rule, I tried consolidating all inputs and outputs from both views into one callback, but because only one view is loaded at any time, the components in the not-loaded view cannot be used as inputs and causes the callback to not run at all. I ended up working around this with further nested Divs with different id’s so that I can split them up into two callbacks, one updating the inner Div, the other updating the outer Div. This too is unintuitive and tedious. It would be nice to have a more elegant solution to it for sure. I am still trying to figure out how I would disable and re-enable those other components on the same page?

Ok, just read through the FAQ and Gotchas page https://dash.plot.ly/faqs. So looks like the workaround for me is to disable callback validation, and I’ll pretty much end up with a big callback with a big list of inputs and update a big number of components unnecessarily, and hiding information in hidden Divs for the purpose of making a dynamic web form. Not ideal but other Dash features are so helpful that I’ll suck it up for now until I become a Javascript an React expert myself :slight_smile: Thanks

I agree it will be a complete game changer. Because now it forces you to use some hacks instead of correct design patterns.

1 Like

is this available now?

3 Likes

Instead of assigning the same output object to different callbacks you can create a single callback with multiple inputs.

Inside of that callback you can determine which control triggered the callback as is described in this FAQ: https://dash.plot.ly/faqs (i.e. dash.callback_context.ctx.triggered[0][‘prop_id’].split(’.’)[0])

1 Like

exactly :+1: this is the recommended pattern right now.

I would like to use this functionality to create a kind of ‘event bus’, where many callbacks would be able to make calculations, send the results to many outputs (which is currently possible) and also to a single output which is the hub responsible for displaying feedback to the user.

More practically speaking, many callback functions should able to send data to a single place (dcc.Store would be a great candidate) then using a property change of this component (modified_timestamp of the dcc.Store component) as the input of the actual ‘event hub’ callback function.

Right now, I solved my issue creating several outputs, one for each sender (‘login’, ‘admin’, ‘security’, some ‘background jobs’, etc.) and using this many component’s children/values props as input of a callback function to display a toast message in a div (a toast container). Every time I create a new sender, I need a new ‘dumb’ component to serve as output and add this ‘dumb’ component as input of the toast container.

Why not pass this scattered data to a single component and having a single callback function listening to changes on this? It would be great and would help to keep the callback graph more rational.

1 Like

I agree that callbacks with shared outputs would be a giant step forward for Dash. Combining all inputs affecting to one output results in messy code, and is certainly more error prone than dividing the code into smaller logical blocks.

This issue has been discussed in #153 by @chriddyp, @alexcjohnson and others. I think that in this case the python rule “there should be one obvious way to do things” would mean shared outputs instead of digging triggered inputs from callback_context :slightly_smiling_face:.

There was question what if there is multiple callbacks with both a shared input and a shared output. This could definitely be an error, but could most of the errors be prevented if findDuplicateOutputs in dependencies.js was modified to something that just prevents having both a same input and an output in two or more callbacks?

Separating the triggered inputs is not the only problem of the CantHaveMultipleOutputs limit. For example, one of my Dash-based projects has currently over 60 callbacks, and I would like to wrap them all in a try-except block and inform the user if something went wrong in a callback.

I was able to implement this behavior by appending a dummy output to each callback (using data-* attributes), collecting all these outputs in one callback and redirecting the messages to a ConfirmDialog. However, this method gives 60+ missing input warnings if the callback exceptions are not suppressed and results in a messy callback graph.

As an alternative approach, I modified the original Dash callback decorator to something like shown below. I was able to redirect errors into a single ConfirmDialog from all the callbacks.

    def callback(self, output, inputs, state=()):
        callback_id = self._insert_callback(output, inputs, state)
        multi = isinstance(output, (list, tuple))

        def wrap_func(func):
            @wraps(func)
            def add_context(*args, **kwargs):
                output_spec = kwargs.pop("outputs_list")

                # ADDED: output value and error message initially set to none
                output_value = None
                error_message = None

                # ADDED; try-except block
                try:
                    # don't touch the comment on the next line - used by debugger
                    output_value = func(*args, **kwargs)  # %% callback invoked %%
                except PreventUpdate:
                    raise PreventUpdate
                #except UserError as e:
                    # UserErrors are raised intentionally inside callbacks to inform the user about wrong input etc.
                #    error_message = str(e)
                except Exception as e:
                    error_message = f'There was an unexcepted error! {e.__class__.__name__}'

                # MOVED component_ids initialisation here
                component_ids = collections.defaultdict(dict)

                # ADDED: error message output to a ConfirmDialog
                if error_message:
                    component_ids['error-output']['message'] = error_message
                    component_ids['error-output']['displayed'] = True

                # ADDED this check as output_value may be None due to an exception
                if output_value:

                    if isinstance(output_value, _NoUpdate):
                        raise PreventUpdate

                    # wrap single outputs so we can treat them all the same
                    # for validation and response creation
                    if not multi:
                        output_value, output_spec = [output_value], [output_spec]

                    _validate.validate_multi_return(output_spec, output_value, callback_id)


                    has_update = False
                    for val, spec in zip(output_value, output_spec):
                        if isinstance(val, _NoUpdate):
                            continue
                        for vali, speci in (
                            zip(val, spec) if isinstance(spec, list) else [[val, spec]]
                        ):
                            if not isinstance(vali, _NoUpdate):
                                has_update = True
                                id_str = stringify_id(speci["id"])
                                component_ids[id_str][speci["property"]] = vali

                # MODIFIED to not raise PreventUpdate if there is error output
                # but no other outputs. Is `has_update´ required anyway?
                if len(component_ids) == 0:
                    raise PreventUpdate

                response = {"response": component_ids, "multi": True}

                try:
                    jsonResponse = json.dumps(
                        response, cls=plotly.utils.PlotlyJSONEncoder
                    )
                except TypeError:
                    _validate.fail_callback_output(output_value, output)

                return jsonResponse

            self.callback_map[callback_id]["callback"] = add_context

            return add_context

        return wrap_func

Similarly, I implemented a simple method to give app users some feedback: adding a method like below to Dash class and calling app.inform('It worked!') inside a callback appends the message to a list in flask.g, and after each callback, the messages can be collected and prompted to the user.

def inform(self, message):
    if not 'info' in flask.g:
        flask.g.info = []
    flask.g.info.append(message)

So it seems that sharing an output between callbacks is already possible with very minor changes. However, callbacks chained with these additional outputs are not working just like that :slight_smile:

I have a vision that with a similar method, from any Dash callback, one could update any component visible in the DOM. It could work by renaming callback_ids to something more descriptive (response_data?) and making it available through flask.g (or callback_context) before executing the wrapped callback code. This way, one could call something like app.response_data['my-desired-output']['value'] = 100 or app.set_output('my-desired-output', 'value', 100) in a callback, and the component should be updated (if present).

Is there any fundamental reason why callback outputs should even be known beforehand? Isn’t that just some response json from which dash-render deduces which component states should be updated by React? After an update, all changed properties are collected and all callbacks having these properties as inputs are eventually called. So, the inputs are the ones which define the callbacks, not the outputs.

Anyhow, thank you for your great work! We use Dash to visualize and analyze measurement data, and it really spares us repeating ourselves in all kinds of excels :relaxed:

2 Likes

@chriddyp are there some updates regarding this point/feature? Thank a lot!

1 Like

There are not.

This still stands.

Some more discussion on related items here: Duplicate Callback Outputs - Solution & API Discussion

Has there been any progress in making it possible to share an output between different callbacks since this was created in 4 years ago?
I have started to look at Dash as one of several possible frameworks for implementing a web-based GUI, but this limitation seems to introduce quite a bit of unnecessary complexity, and also, if many inputs need to share the same output(s), sometimes also quite a lot of unnecessary data transfer.

For example, I might have quite a number of unrelated callbacks which all do their own thing, but all of them want to show some kind of status message in a div somewhere on the page. Is there any way to do this without having to merge all those callbacks into basically a single one?

Hello @johann.petrak,

They have been working on this. :slight_smile:

They announced it in our most recent community meeting!

1 Like

So does this mean that support for it might get released sometime in the (unknown) future or is there are concrete roadmap for when this will work?

There more I think about this, the more I dislike this restriction, since I already found situations where more than one output would need to get updated by more than one callback handler.
Which basically means that almost all processing has to be done in one huge callback which always receives all data and always has to decide what to use, hand over to some actual processing function and finally which output to actually update.
Which in my view renders the whole Input/Output declaration option useless except in the most simplistic situations.

But maybe I see it like this because Dash is not actually meant for the kind of GUI interaction I have in mind and I am trying to abuse the wrong tool for my purposes. Sorry … I am currently at a stage where I try to figure that out :slight_smile:

This is definitely a downside to its current state.

However, coming from a flask / javascript background myself, there are many ways to attain the level of interaction I think we are used to.

One of the ways is to use Pattern-Matching, which gives tons of customization and flexibility.

Another way is to use Dash-Extensions.


Another thing you can do, through JS is manipulate the element if necessary and then have a sync function which syncs with the server and React components.

1 Like

I would recommend the MultiplexerTransform from dash-extensions for now. When/if a native solution becomes available, you’ll only need to change a single line of code.

1 Like

Thanks, I had a quick look at that extension and it looked like something I had already been wondering about whether it could be done that way: have several callbacks all send to a Store (and in addition to output exclusive to them directly) and then have the Store all send to the shared output(s).

Doing this explicitly like that may actually make it possible to prevent at least some of the unnecessary couplings, if I understand correctly (still pretty much in the plan-it-in-my-head phase right now).

Yes, it is possible to do it manually. I did that for a few simple cases :smile: . But it is a lot simpler to auto generate the code, which dash-extensions can do for you. It will also make your code more readable/maintainable, and when/if Plotly are ready with a solution, you won’t have to do extensive refactoring.

1 Like