Callbacks in multipage app

Hello,
I am creating a multipage Dash app. For this, I have created a Sidebar which allows me to switch between pages with selecting the page in a dropdown menu where each option is a dcc.Link object (see code below). After changing to a specific page, the URL in the browser changes from localhost:8050 to, e.g. localhost:8050/adp and all the HTML blocks defined in the layout of this pages are displayed.
I have registered every page with the parameter

dash.register_page(
        __name__,
        path = '...'

In my main app.layout I have the component dash.page_container to display the selected page.
On the new page, I have many callbacks. These callbacks I have created in the following manner:

@callback(
        Output('workspaceDropdown', 'options'),
        Input('url', 'pathname'),
        prevent_initial_call=True
)

Before stating the function with this callback, I already have created the dcc.Dropdown object with the ID workspaceDropdown
Right at the beginning I have a simple print statement which tells me whether or not the function was executed. This statement never shows up in the terminal from which I assume that the function is not executed.
Do I need to add something to the layout of this page or to all the callbacks that they are recognized and executed properly?

sidebar = html.Div(
            [
                html.P('Select the page you want to load', className='lead'), #explanation of dropdown menue
                html.Div([
                    dcc.Dropdown(
                        options = [
                            {'label': dcc.Link(children='Hello World' , href='/' , style = LINK_STYLE), 'value': '/'},
                            {'label': dcc.Link(children='ADP' , href='/adp' , style = LINK_STYLE), 'value': '/adp'}
                        ],
                        id='pageSelected'),
                ]),
            ],
            style=SIDEBAR_STYLE, #use defined style of sidebar
        )
@callback(
        Output('workspaceDropdown', 'options'),
        Input('url', 'pathname'),
        prevent_initial_call=True
)
def getWorkspaces(path):
    print('path in getWorkspaces: ' + str(path))
    try:
        if path == '/adp':
            print('get_workspaces')
            workspaces = get_workspaces()
            workspacesList = list(workspaces.keys())

            return workspacesList
    except IndexError as e:
        None

    return []

This function is just one example. All the callbacks on the other pages are not executed as well.

I hope someone can help me,
All the best,
Clonky

Hello @clonky and welcome to the forum!

Where are your callbacks placed in your file structure? I was solving similiar issue where I placed all my callbacks into callbacks file and I did not imported the file with callbacks in my main file. Couldn’t something similliar be an issue here?

Hi @martin2097 thanks a lot!
All my callbacks are stored in the pages file. One page in the folder “pages” contains all the functions with the callback as well as the layout of the specific page. So one file (shortened) looks like this
adp.py:

dash.register_page(
        __name__,
        path = '/adp')

@callback(
        Output('workspaceDropdown', 'options'),
        Input('url', 'pathname'),
        prevent_initial_call=True
)
def getWorkspaces(path):
    print('path in getWorkspaces: ' + str(path))
    try:
        if path == '/adp':
            print('get_workspaces')
            workspaces = get_workspaces()
            workspacesList = list(workspaces.keys())

            return workspacesList
    except IndexError as e:
        None

    return []

layout = dbc.Container([
    dcc.Location(id='url', refresh=False), #nessesarry component to get URLs
    html.Div(children=[
        html.Tr([
            html.H2('Select a workspace:', style={'width': 550}), 
            dcc.Dropdown(
                # provide options as Output from callback function
                id='workspaceDropdown',
            ),
        ])
        ],
        style = CONTENT_STYLE)
])

This file gets imported in the main app.py in the manner from pages import adp
The whole project structure looks like this

--- app.py
--- pages (folder)
     |--- adp.py

This is the part of the file app.py

from pages import adp

sidebar = html.Div(
            [
                html.P('Select the page you want to load', className='lead'), #explanation of dropdown menue
                html.Div([
                    dcc.Dropdown(
                        options = [
                            {'label': dcc.Link(children='Hello World' , href='/' , style = LINK_STYLE), 'value': '/'},
                            {'label': dcc.Link(children='ADP' , href='/adp' , style = LINK_STYLE), 'value': '/adp'}
                        ],
                        id='pageSelected'),
                ]),
            ],
            style=SIDEBAR_STYLE, #use defined style of sidebar
        )

app = dash.Dash(__name__, use_pages=True)

app.layout = dbc.Container([
    dcc.Location(id='url', refresh=False), #nessesarry component to get URLs
    sidebar, #sidebare as defined above, sticked to left side

    dash.page_container

])

if __name__ == '__main__':
    app.run(host = 'localhost', port = '8050', debug=True)

Did you try to ommit the prevent_initial_call=True? I see that you are listening the pathname and I believe that every change of the pathname initiates the “initial call”.

1 Like

This solved my problem. Thanks a lot!
I have set prevent_initial_call=True as sort of default for every callback because I dont want to get any errors from the function or functions that are called inside of the callback. However, as you can also see above I have “secured” every callback function nevertheless with at least on if-Statement
Apparently in my setting this prevented every callback from ever getting executed.

Thanks a lot again!

Nice catch! Well sometimes you want some of your callbacks to run initially to populate your layout with a data :slight_smile: