Reusable Dash components with callback functions

Hi everybody,

I just discovered that with this coding pattern, I can generate UI elements with their own callbacks, that can be reused in different contexts.

Based on the Dash intro example, I found that instead of coding the layout directly


app.layout = html.Div([
    html.H6("Change the value in the text box to see callbacks in action!"),
    html.Div(["Input: ",
              dcc.Input(id='my-input', value='initial value', type='text')]),
    html.Br(),
    html.Div(id='my-output'),

])

one can create a function that generates the layout and its callbacks:

def InputField(id):

    @app.callback(
        Output(component_id='my-output' + id, component_property='children'),
        Input(component_id='my-input' + id, component_property='value')
    )
    def update_output_div(input_value):
        return 'Output: {}'.format(input_value)

    return html.Div(["Input: ",
            dcc.Input(id='my-input' + id, value='new', type='text'),
            html.Br(),
            html.Div(id='my-output' + id)
            ])

and then use the function within the main layout:

app.layout = html.Div([
    html.H6("Change the value in the text box to see callbacks in action!"),
    im.InputField('feld1'),
    im.InputField('feld2'),
    im.InputField('feld3'),
])

So far, it is working fine for me. I can not extend the layout during runtime dynamically though, but apart from that this is working quite well.

Since I did not find this pattern proposed in the main guides (only here for the layout Part 2. Layout | Dash for Python Documentation | Plotly) I was wondering, whether there are any reasons not to use this pattern?

Best,
Martin

2 Likes

This looks like a nice way to organise your code to me! I’ve used some similar ideas before, I don’t see any reasons this would cause problems.

If you want to extend the layout dynamically, I suggest looking at pattern matching callbacks. In that case you would define the callback outside and make sure your factory functions generates components in a way that conforms to the pattern specified by the callback.

Yes, the “patter matching” pattern is certainly important for a dynamic extensibility of the app.

I find it curious that there is not much literature out there on how larger Dash apps should be designed in order to keep the project maintainable and comprehensible.

I came up with the same approach to create reusable components. I don’t understand how there’s no documentation or use cases covering this anywhere. How do people render the same HTML component multiple times, and then updates their respective UI depending on their respective events? It feels like I’m missing something…

Anyway, I spent the last 4 hours trying to make this work, and I was going nuts.

Make sure that for the callback decorator in your closure you don’t use the “callback” object you can import from dash (as in from dash import callback). If you do so, the callback in the closure will not be called (even if a callback that is not a closure will indeed be called - and obviously you need a closure so you have access to the component id dynamically). Much fun!

Bad:

from dash import callback

def render():
  @callback(...)
  def func(...)

Good:

def render(app: Dash):
  @app.callback(...)
  def func(...)

You need to pass around your “app” instance, which it’s quite inconvenient when you are using Dash Pages as there is no clear call tree to pass the app instance from one function to another, so you need to deal with globals variables, which is unfortunate.

1 Like

I have often used the DashBlueprint class for this purpose. In addition to enabling modular component development, it also incorporates functionality to add id prefixes to avoid id collisions, if the same component is used multiple times in the same app.

2 Likes

@Emil

That’s exactly it. What a gold mine those extensions are. Thanks for sharing.

3 Likes

Eventually, I had to go with DashBlueprints. The initial approach did not work out.