How can I assign callbacks that aren't guaranteed to exist?

I have a dash program that creates one to three buttons based on user input in a dropdown. The user can then click the button and the program will tell them which button was last pressed. Like so: three has been selected in the dropdown so three buttons were created. After clicking button-1 it says 'Last selected: button-1'

However an issue arises whenever I select a dropdown value that doesn’t create all 3 buttons. I can select 1 or 2, and 1 or 2 buttons are created, but trying to click button-1 or button-2 returns an error and does not properly run the callback.

The error I receive is: A nonexistent object was used in an `Input` of a Dash callback. The id of this object is `button-2` and the property is `n_clicks`. The string ids in the current layout are: [page-content, dropdown, button-row, last-selected, button-0, button-1]

I understand that this is occurring because my callback takes all three buttons as an input despite the fact that all three buttons might not exist when the callback is run, but I am unsure how to fix this issue.

Here is the code:


import dash
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
from dash import Dash, dcc, html, Input, Output, callback

app = dash.Dash(__name__, external_stylesheets=[dbc.themes.ZEPHYR], suppress_callback_exceptions=True)
server = app.server 

# Layout for the form
layout = dbc.Container(
    [
        dbc.Row( children =
        [
            html.Strong('How many buttons do you want?', style={'font-size': '20px', 'width': 'auto'}),
            dcc.Dropdown([1, 2, 3],  id='dropdown'),
        ], justify='center', align='center', style={'margin-top': '20px'}
        )
    ]
)
buttons = html.Div(
    [
        dbc.Col(
            [
                dbc.Row([], id='button-row'),
                html.Div([],id='last-selected')
            ]
        )
    ]
)
app.layout = html.Div(
    [
        html.Div(children = [layout, buttons], id='page-content'),
    ]
)



@callback(Output('button-row', 'children'),
                        Input('dropdown', 'value'))
def update_button_row(dropdown):
    children = []
    for each in range(dropdown):
        children.append(dbc.Button('Button {}'.format(each), id='button-{}'.format(each)))
    return children

@callback(Output('last-selected', 'children'),
                        Input('button-0', 'n_clicks'),
                        Input('button-1', 'n_clicks'),
                        Input('button-2', 'n_clicks'),      
                        prevent_initial_call = True)
def update_last_selected(button0, button1, button2):
    ctx = dash.callback_context
    if not ctx.triggered:
        return ''
    else:
        button_id = ctx.triggered[0]['prop_id'].split('.')[0]
        return 'Last selected: {}'.format(button_id)

# Launch the app with debug mode on
if __name__ == '__main__':
    app.run_server(debug=True)

I’d like a solution that allows all buttons created by the update-button-row function to have the ability to print their name in the ‘last-selected’ div when they have been clicked.

Thank you for reading!

Hello @DHutch00,

Welcome to the community!

It sounds like you could use pattern-matching for what you need to do. Check it out:

This would allow you to have an indefinite amount of buttons, and only look for one input. Using ctx, you can determine which button was clicked.

1 Like