How do you combine Variable Paths and Multiple Pages within One File

I am using https://dash.plotly.com/urls and https://dash.plotly.com/urls#reference-for-dash.register_page as my references to create a Multi-Page app. I have Variable Paths working properly with all of my layouts in a /pages folder, but I would like to have everything in a single file. When I do so, I cannot figure out how get the URL argument passed to the layout function.

The documentation states: path_template: Add variables to a URL by marking sections with <variable_name>. The layout function then receives the <variable_name> as a keyword argument. e.g. path_template= “/asset/<asset_id>” then if pathname in browser is “/assets/a100” then layout will receive **{“asset_id”:“a100”}

but that doesn’t seem to be working when I define my own layout function as is necessary when using a single file. In the example below, kwargs is always null, even when a report path is set in the URL:

here is my code:

from dash import Dash, html, dcc, callback, Input, Output
import dash


def layout_analytics():
    return html.Div([
        html.H1('This is our Analytics page'),
        html.Div([
            "Select a city: ",
            dcc.RadioItems(
                options=['New York City', 'Montreal', 'San Francisco'],
                value='Montreal',
                id='analytics-input'
            )
        ]),
        html.Br(),
        html.Div(id='analytics-output'),
    ])


def layout_archive():
    return html.Div([
        html.H1('This is our Archive page'),
        html.Div('This is our Archive page content.')
    ])


def layout_home_page():
    return html.Div([
        html.H1('This is our Home page'),
        html.Div('This is our Home page content.'),
    ])


def layout_report(**kwargs):
    return html.Div([
        html.H1('This is our Reports page'),
        html.Div(f'Requested report: ? kwargs={kwargs}'),
    ])


app = Dash(__name__, use_pages=True, pages_folder="")


dash.register_page("home", path='/', layout=layout_home_page())
dash.register_page("analytics", layout=layout_analytics())
dash.register_page("archive", layout=layout_archive())
dash.register_page("report", path_template="/report/<report_id>",
                   layout=layout_report())

app.layout = html.Div([
    html.Div([
        html.Div(
            dcc.Link(f"{page['name']} - {page['path']}", href=page["relative_path"])
        ) for page in dash.page_registry.values()
    ]),
    dash.page_container,
])


@callback(
    Output('analytics-output', 'children'),
    Input('analytics-input', 'value')
)
def update_city_selected(input_value):
    return f'You selected: {input_value}'


if __name__ == '__main__':
    app.run(debug=True)

My layouts are quite complex and already defined within an OO structure so this is just an example to see whether I can plug everything in correctly or not. I have things working with URL parameters in a fishy way, but I really would prefer the pages working in this manner.

@marketemp

Thanks for the awesome minimal example :medal_sports: It makes helping with a solution so much easier.

Try changing this one line:


dash.register_page("report", path_template="/report/<report_id>",
                   layout=layout_report())

to:

dash.register_page("report", path_template="/report/<report_id>",
                   layout=layout_report)

Need to provide the function to the layout – but not call the function. More info here in the dash-docs

On a side note… why do you want to put all this into a single file?

oh, total my bad, I have this right in my actual program, but just missed it in the demo. I would have expected a harder error than just having it not get the kwargs right.

The reason I prefer single page is my program is huge. I can have all my Inputs, Outputs, callbacks in the app.py file for consistent edits. I have been using multi-page before this newer, cooler rev and my code is OO using the following structure:

Mdash:

  • mdash.py - all the generic defs and routines
  • callback.py - all the callbacks
  • layout.py - all the layouts
  • query.py - all the query calls needed by graph and table
  • graph.py - all the graphs
  • table.py - all the tables
  • msg.py - all of the strings (framework if translation is ever necessary)

for much better organization. I found a way to make a global object and get it passed and working in the /pages/page.py files, but still prefer it all in one file instead of spread out there.

Hmm, I’d recommend taking a look here:

In my own project at work, I have multiple pages in the pages folder. Splitting out callbacks, functions and common imports into a utils folder.

Mind you, I am converting from flask templates, which have to be separated.

With all this said, I prefer having the pages separated because it makes it easier for a team to maintain. (Less merge conflicts)