Multipage app without using suppress_ callback_ exceptions=True?

Hello everyone, first post here. I’m trying to build a simple multipage app where app.py contains the navigation bar and html.Div(id=‘page-content’) which will be filled with the layout of the different pages.

index.py:

import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State

from app import app
from apps import app0, app1, app2

navbar = dbc.Navbar(
    [
        dbc.Row(
            [
             dbc.Col(dbc.NavbarBrand("Security Solution Tuning Suite")),
                dbc.Col(dbc.NavLink("Main", href="/")),
                dbc.Col(dbc.NavLink("Toolbox", href="/app1")),
                dbc.Col(dbc.NavLink("Tuning", href="/app2")),
            ],
            align="center",
            no_gutters=True,
            style={'column-gap': '20px'}
        ),
    ],
)

app.layout = html.Div(
    [
        dcc.Location(id='url', refresh=False),
        navbar,
        html.Div(id='page-content')
    ],
)

@app.callback(
    Output('page-content', 'children'),
    Input('url', 'pathname'),
    prevent_initial_call=True
    )
def display_page(pathname):
    if pathname == '/':
        return app0.layout
    if pathname == '/app1':
        return app1.layout
    elif pathname == '/app2':
        return app2.layout
    else:
        return '404'

if __name__ == '__main__':
    app.run_server(
        debug=True,
        # threaded=True
    )

app.py:

import dash
import dash_bootstrap_components as dbc


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

server = app.server

app0.py

import dash_core_components as dcc
import dash_bootstrap_components as dbc
import dash_html_components as html
from dash.dependencies import Input, Output

from app import app

layout = html.Div(
    [
        dbc.InputGroup(
            [
                dbc.InputGroupAddon("@", addon_type="prepend"),
                dbc.Input(placeholder="Username"),
            ],
            className="mb-3",
        ),
        dbc.InputGroup(
            [
                dbc.Input(placeholder="Recipient's username"),
                dbc.InputGroupAddon("@example.com", addon_type="append"),
            ],
            className="mb-3",
        ),
        dbc.InputGroup(
            [
                dbc.InputGroupAddon("$", addon_type="prepend"),
                dbc.Input(placeholder="Amount", type="number"),
                dbc.InputGroupAddon(".00", addon_type="append"),
            ],
            className="mb-3",
        ),
        dbc.InputGroup(
            [
                dbc.InputGroupAddon("With textarea", addon_type="prepend"),
                dbc.Textarea(),
            ],
            className="mb-3",
        ),
        dbc.InputGroup(
            [
                dbc.Select(
                    options=[
                        {"label": "Option 1", "value": 1},
                        {"label": "Option 2", "value": 2},
                    ]
                ),
                dbc.InputGroupAddon("With select", addon_type="append"),
            ]
        ),
    ]
)

app1.py

import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

from app import app

layout = html.Div([
    html.H3('App 1'),
    dcc.Dropdown(
        id='app-1-dropdown',
        options=[
            {'label': 'App 1 - {}'.format(i), 'value': i} for i in [
                'NYC', 'MTL', 'LA'
            ]
        ]
    ),
    html.Div(id='app-1-display-value'),
    dcc.Link('Go to App 2', href='/apps/app2')
])


@app.callback(
    Output('app-1-display-value', 'children'),
    Input('app-1-dropdown', 'value'),
    prevent_initial_call=True
    )
def display_value(value):
    return 'You have selected "{}"'.format(value)

app2.py

import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

from app import app

layout = html.Div([
    html.H3('App 2'),
    dcc.Dropdown(
        id='app-2-dropdown',
        options=[
            {'label': 'App 2 - {}'.format(i), 'value': i} for i in [
                'NYC', 'MTL', 'LA'
            ]
        ]
    ),
    html.Div(id='app-2-display-value'),
    dcc.Link('Go to App 2', href='/apps/app2')
])


@app.callback(
    Output('app-2-display-value', 'children'),
    Input('app-2-dropdown', 'value'),
    prevent_initial_call=True
    )
def display_value(value):
    return 'You have selected "{}"'.format(value)

My issue is that I get 4 callback error coming from the display_value function both in app1 & app2:

:stop_sign: Errors (4)
×
:rescue_worker_helmet:
ID not found in layout
18:15:11
Attempting to connect a callback Input item to component:
“app-2-dropdown”
but no components with that id exist in the layout.

If you are assigning callbacks to components that are
generated by other callbacks (and therefore not in the
initial layout), you can suppress this exception by setting
suppress_callback_exceptions=True.
This ID was used in the callback(s) for Output(s):
app-2-display-value.children
:rescue_worker_helmet:
ID not found in layout
18:15:11
Attempting to connect a callback Input item to component:
“app-1-dropdown”
but no components with that id exist in the layout.

If you are assigning callbacks to components that are
generated by other callbacks (and therefore not in the
initial layout), you can suppress this exception by setting
suppress_callback_exceptions=True.
This ID was used in the callback(s) for Output(s):
app-1-display-value.children
:rescue_worker_helmet:
ID not found in layout
18:15:11
Attempting to connect a callback Output item to component:
“app-2-display-value”
but no components with that id exist in the layout.

If you are assigning callbacks to components that are
generated by other callbacks (and therefore not in the
initial layout), you can suppress this exception by setting
suppress_callback_exceptions=True.
This ID was used in the callback(s) for Output(s):
app-2-display-value.children
:rescue_worker_helmet:
ID not found in layout
18:15:11
Attempting to connect a callback Output item to component:
“app-1-display-value”
but no components with that id exist in the layout.

If you are assigning callbacks to components that are
generated by other callbacks (and therefore not in the
initial layout), you can suppress this exception by setting
suppress_callback_exceptions=True.
This ID was used in the callback(s) for Output(s):
app-1-display-value.children

At the moment I’m deliberately NOT using suppress_callback_exceptions=True (you can see it commented out) as I want to see callback errors and suppressing those would make it much harder to debug, moreover, to me it does look more like a hack than a definitive solution.

I had a read online and I noticed how someone proposed using flask.has_request_context(): to circumvent the issue, but I can’t figure out how to make it work, any chance someone can guide me?

Hi,

The best approach for this is to define the app.validation_layout. There you can add the layout for all your pages and components dynamically generated, so Dash will check it instead of your current layout.Here is the link to the docs, and this is explained under “Dynamically Create a Layout for Multi-Page App Validation”