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

Enhancing the callback function signature #159

Closed
ned2 opened this issue Nov 16, 2017 · 4 comments
Closed

Enhancing the callback function signature #159

ned2 opened this issue Nov 16, 2017 · 4 comments

Comments

@ned2
Copy link
Contributor

ned2 commented Nov 16, 2017

When creating more complex Dash apps, I've found that the function signatures of callbacks is somewhat limiting. More specifically:

Inputs and States are all combined together into the one list of positional parameters (ie *args) and you have to keep track of how many there of each in order to identify them from the args list. For a handful of Inputs/States, this is manageable, however if you're programmatically generating a large number of them (eg I recently made an app with 1600 Inputs -- granted this was more of a form than a reactive-viz) then this becomes trickier.

In addition to keeping tabs on how many Inputs/States are being provided, in a larger app, you need to start managing the logic of how they are handled, as this can't be done explicitly for each input. My solution to this was to create a helper Store class which allowed me to to associate---for each input component that can be targeted by an Input/State---some of the handling logic while I'm constructing the layout, then retrieving the handling logic from this Store within the callback. The friction involved in this is again the simple positional arguments of callback functions, and having to associate them with the element IDs that have been added to the Store. I worked around this by having a method in the store to generate a list of Inputs for the callback signature and one to generate list of corresponding metadata dicts, then iterating over a those two lists zipped together:

@app.callback(Output('target', 'children'), store.get_inputs(), [Event('submit', 'click')])
def submit_form(*values):
    # do stuff
    for value, data in zip(values, store.get_data()):
        # do stuff with each value, using the corresponding information in data on how to handle it 
    return "Done"

If the callback signatures contained more information about each Input/State/Event, and in a manner more accessible, then these difficulties could be much lessened. For example, something along the lines of callback(states, inputs, events) where each positional argument is a dictionary---keyed perhaps by element_id.property---with values containing dicts which hold the component registration information for the relevant components involved in the callback eg: element_id, property, value.

The important thing is that you be able to iterate over the callback's Inputs, States, and Events, and be able to readily access the associated element ID so that you can look it up in your own data structure. So those positional args could still just be lists of component registrations with the needed metadata, but the advantage of using dicts is that you could quickly index the registration you need without having to worry about where it was defined in the @app.callback decorator. (I guess that might would prompt the question of why the decorator needs to be lists then also)

I do however like the simplicity of the current callback signature, and for many simple apps it could be preferable. One option might be to create a more expressive callback function alongside the simple one that people can choose to use only when the situation calls for. Another compromise I see is to keep using only positional parameters, but have a flag in the callback decorator which toggles whether the arguments are a list of values or a list of {element_id, property, value} dicts.

See also this discussion in the community forums, where someone was running into the limitation of not being able to ascertain which element was the source of a click Event.

@radumas
Copy link

radumas commented Nov 30, 2017

Could something like enabling keyword arguments in @app.callback() which could then be passed to the decorated function as keyword arguments as well, e.g.:

@app.callback(Output('target', 'children'),
                         dropdowns = [Input(dropdown_id, 'value') for dropdown_id in DROPDOWN_IDS],
                         buttons = [Input(button_id, 'value') for button_id in BUTTON_IDS])
def func(dropdowns = [], buttons = []):
      #etc...

@jbweston
Copy link

jbweston commented Dec 8, 2017

Could something like enabling keyword arguments in @app.callback() which could then be passed to the decorated function as keyword arguments as well

How would this work? As I understand it, Dash distinguishes between 2 cases for inputs: those that trigger the callback (inputs) and those that do not (state). This distinction is not made in the above proposal.

@akhmerov
Copy link

I am also interested in figuring out which event triggered the callback. My use case is navigation buttons, and right now the only way to figure it out is to store the vector of n_clicks for all buttons, and to figure out which one has changed.

A possible improvement would be to include the event type that triggered the callback in the callback and the event emitter function signature. The latter would require also a change in dash-renderer.

@chriddyp chriddyp added this to Wishlist and In-Discussion - Seeking Sponsorship in Dash - Next Generation - Architectural Changes via automation Jun 8, 2018
byronz added a commit that referenced this issue Apr 23, 2019
@alexcjohnson
Copy link
Collaborator

callback_context should address a lot of this. If there are still ways it could be better, I suspect it has at least shifted the discussion enough to warrant a new issue. So I'll close this one but if anyone would like to discuss further please start a new issue.

Dash - Next Generation - Architectural Changes automation moved this from Wishlist and In-Discussion - Seeking Sponsorship to Done Jun 27, 2019
HammadTheOne pushed a commit to HammadTheOne/dash that referenced this issue May 22, 2021
HammadTheOne pushed a commit to HammadTheOne/dash that referenced this issue May 28, 2021
HammadTheOne pushed a commit to HammadTheOne/dash that referenced this issue May 28, 2021
HammadTheOne pushed a commit that referenced this issue Jul 23, 2021
HammadTheOne pushed a commit that referenced this issue Jul 23, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

5 participants