I’m developing a multi-page Dash application where I dynamically switch between different views, such as a login page and a script selection page, using a placeholder div to load the content dynamically based on button clicks.
However, I’m encountering an error when trying to load the login page after clicking the “Continue” button on the script selection page. The error reads:
A nonexistent object was used in an `Input` of a Dash callback. The id of this object is `botao-voltar` and the property is `n_clicks`.
This error happens because the “back” button (botao-voltar) does not exist when the app first loads, and only gets rendered after switching to the login page.
What I’ve tried:
- I used suppress_callback_exceptions=True in the app configuration to allow for dynamically loaded components.
- I combined the callback for both the “Continue” and “Back” buttons into one callback and used callback_context to identify which button was clicked.
- I added prevent_initial_call=True to the callback to prevent the callback from triggering when the page first loads.
- I attempted to handle multiple views by dynamically changing the content of the placeholder-content div.
Code:
Here’s the simplified version of my code:
from dash import Dash, html, dcc, Input, Output, State, callback_context
app = Dash(__name__)
app.config.suppress_callback_exceptions = True
def layout_base(content):
return html.Div([
html.Div([
html.Div('My Application Header', style={'display': 'inline-block', 'font-size': '26px'}),
], style={'text-align': 'center'}),
html.Div(id='placeholder-content', children=content),
html.Div('Footer Content', style={'text-align': 'center'}),
])
def script_selection_page():
return html.Div([
html.H2("Select a Script"),
dcc.Dropdown(
id='dropdown-script',
options=[{'label': f'Script {i}', 'value': f'Script {i}'} for i in range(1, 4)],
placeholder='Select a script'
),
html.Button('Continue', id='botao-continuar')
])
def login_page():
return html.Div([
html.H2("Login"),
dcc.Input(id='input-username', type='text', placeholder='Username'),
dcc.Input(id='input-password', type='password', placeholder='Password'),
dcc.Dropdown(id='dropdown-grau', options=[{'label': '1st Level', 'value': '1'}, {'label': '2nd Level', 'value': '2'}]),
html.Button('Login', id='botao-login'),
html.Button('Back', id='botao-voltar')
])
app.layout = layout_base(script_selection_page())
@app.callback(
Output('placeholder-content', 'children'),
[Input('botao-continuar', 'n_clicks'), Input('botao-voltar', 'n_clicks')],
[State('dropdown-script', 'value')],
prevent_initial_call=True
)
def switch_pages(n_clicks_continuar, n_clicks_voltar, script_selected):
ctx = callback_context
if not ctx.triggered:
return script_selection_page()
button_id = ctx.triggered[0]['prop_id'].split('.')[0]
if button_id == 'botao-continuar' and script_selected:
return login_page()
elif button_id == 'botao-voltar':
return script_selection_page()
return script_selection_page()
if __name__ == '__main__':
app.run_server(debug=True)
The issue:
Even though I used suppress_callback_exceptions=True, I still get the following error when switching to the login page after clicking “Continue”:
A nonexistent object was used in an `Input` of a Dash callback. The id of this object is `botao-voltar` and the property is `n_clicks`.
What I’m looking for:
How can I properly switch between pages/views in Dash without encountering this issue? What’s the correct way to manage dynamically loaded components that don’t exist at the initial render? Is there a way to register callbacks only when the components are present?