Hello,
I am trying to create a multipage app with two countries in a navigation bar at the top, and for each country two regions (east and west, as a sidebar), and for each region two cities (as tabs).
All my app code can be found on the github link at the end of this post.
In the reproductible example below, I have the following countries/regions/cities:
At the root/landing of the app, the content of the USA/East/New York tab should be displayed.
The navigation bar with the countries should always be visible.
The app root/landing page should ultimately look like this (with the USA/East Coast/ New York tab showing that they are selected):
When user try to access the app root, they should automatically be redirected to the page above, which url should read: http://127.0.0.1:8050/usa/east
If they click on Germany, they should navigate to http://127.0.0.1:8050/deu/east , which is displayed below:
I have organised my app the following way:
- app.py
- styles_info.py
- init.py
- pages
-
USA
|-- usa_index.py
|-- usa_east_ny_tab.py
|-- usa_east_philly_tab.py
|-- usa_west_la_tab.py
|-- usa_west_sa_tab.py -
DEU
|-- deu_index.py
|-- deu_east_berlin_tab.py
|-- deu_east_leipzig_tab.py
|-- deu_west_koln_tab.py
|-- deu_west_frankfurt_tab.py
-
The .py files ending with “_tab.py” in their names contain the plotly objects and related dash components that get displayed on each page. I’m just using random dash/plotly objects from examples on the web.
The styles_info.py just contain formatting data for the sidebar menu.
There is a lot not working on that app but the firs things I would like to fix are:
- how to redirect the landing/root page as explain above (USA/East Coast/New York, with url http://127.0.0.1:8050/usa/east)
Here is my main app.py script generating the pages:
import dash
from dash import Dash, html
import dash_bootstrap_components as dbc
app = Dash(__name__, use_pages=True, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = html.Div([
html.H1('World App'),
dbc.Navbar(
[
dbc.Button(f"{page['name']}",
href=page["relative_path"],
color="primary",
className="me-1")
for page in dash.page_registry.values()
],
),
dash.page_container,
])
if __name__ == '__main__':
app.run_server()
Here is the script for the page that should be the landing page when navigating to the app root (http://127.0.0.1:8050), located in usa_index.py :
import dash
import dash_bootstrap_components as dbc
from dash import Input, Output, dcc, html, callback
from pages.USA.usa_east_ny_tab import get_usa_east_ny_content
from pages.USA.usa_east_philly_tab import get_usa_east_philly_content
from pages.USA.usa_west_la_tab import get_usa_west_la_content
from pages.USA.usa_west_sa_tab import get_usa_west_sa_content
from styles_info import get_styles_info
dash.register_page(__name__, name='USA', path = '/usa')
SIDEBAR_STYLE, CONTENT_STYLE = get_styles_info()
sidebar = html.Div(
[
html.H2("USA Cities", className="display-8"),
html.Hr(),
html.P(
"Information on USA cities", className="lead"
),
dbc.Nav(
[
dbc.NavLink("East Coast", href=f"usa/east/ny", active="exact"),
dbc.NavLink("West Coast", href=f"usa/west/la", active="exact"),
],
vertical=True,
pills=True,
),
],
style=SIDEBAR_STYLE,
)
usa_east = html.Div([
dbc.Tabs(
[
dbc.Tab(label="NEW YORK", tab_id="usa_east_ny_id"),
dbc.Tab(label="PHILADELPHIA", tab_id="usa_east_philly_id"),
],
id="tabs",
active_tab="usa_east_ny_id",
),
html.Div(id="usa_tab-content", className="p-4"),
])
usa_west = html.Div([
dbc.Tabs(
[
dbc.Tab(label="LOS ANGELES", tab_id="usa_west_la_id"),
dbc.Tab(label="SAN FRANCISCO", tab_id="usa_west_sa_id"),
],
id="tabs",
active_tab="usa_west_la_id",
),
html.Div(id="usa_tab-content", className="p-4"),
])
content = html.Div(id="usa_page-content", style=CONTENT_STYLE)
layout = html.Div([dcc.Location(id="url_usa"), sidebar, content])
usa_east_ny_content = get_usa_east_ny_content()
usa_east_philly_content = get_usa_east_philly_content()
usa_west_la_content = get_usa_west_la_content()
usa_west_sa_content = get_usa_west_sa_content()
@callback(
Output("usa_page-content", "children"),
[Input("url_usa", "pathname")])
def render_page_content(pathname):
if pathname == f"/" or pathname == f"/usa" or pathname == f"/usa/east" or pathname == f"/usa/east/ny":
return usa_east
elif pathname == f"/usa/west" or pathname == f"/usa/west/la":
return usa_west
@callback(
Output("usa_tab-content", "children"),
[Input("tabs", "active_tab")],
)
def render_tab_content(active_tab):
if active_tab:
if active_tab == "usa_east_ny_id":
return usa_east_ny_content
elif active_tab == "usa_east_philly_id":
return usa_east_philly_content
elif active_tab == "usa_west_la_id":
return usa_west_la_content
elif active_tab == "usa_west_sa_id":
return usa_west_sa_content
Here is the code of the default tab that is open when on that page (returned in the page by calling the function get_usa_east_ny_content and imported into the usa_index.py page from the file usa_east_ny_tab.py :
def get_usa_east_ny_content():
from dash import dcc, html, Input, Output, callback
import plotly.express as px
import pandas as pd
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv')
layout = html.Div([
dcc.Graph(id='usa_east_ny_graph'),
dcc.Slider(
df['year'].min(),
df['year'].max(),
step=None,
value=df['year'].min(),
marks={str(year): str(year) for year in df['year'].unique()},
id='year-slider-ny'
)
])
@callback(
Output('usa_east_ny_graph', 'figure'),
Input('year-slider-ny', 'value')
)
def update_figure(selected_year):
filtered_df = df[df.year == selected_year]
fig = px.scatter(filtered_df, x="gdpPercap", y="lifeExp",
size="pop", color="continent", hover_name="country",
log_x=True, size_max=55)
fig.update_layout(transition_duration=500)
return fig
return layout
- how to set the urls properly so clicking on DEU/East returns the content of the BERLIN tab on this url: http://127.0.0.1:8050/DEU/east/berlin (and the same logic for all other links in the sidebar).
The page for DEU is defined in deu_index.py like this:
import dash
import dash_bootstrap_components as dbc
from dash import Input, Output, dcc, html, callback
from pages.DEU.deu_east_berlin_tab import get_deu_east_berlin_content
from pages.DEU.deu_east_leipzig_tab import get_deu_east_leipzig_content
from pages.DEU.deu_west_koln_tab import get_deu_west_koln_content
from pages.DEU.deu_west_frankfurt_tab import get_deu_west_frankfurt_content
from styles_info import get_styles_info
dash.register_page(__name__, name='DEU', path = '/DEU')
SIDEBAR_STYLE, CONTENT_STYLE = get_styles_info()
sidebar = html.Div(
[
html.H2("GERMANY Cities", className="display-8"),
html.Hr(),
html.P("Information on GERMANY cities", className="lead"),
dbc.Nav(
[dbc.NavLink("East", href=f"/DEU/east/berlin", active="exact"),
dbc.NavLink("West", href=f"/DEU/west/koln", active="exact"),
],
vertical=True,
pills=True,
),
],
style=SIDEBAR_STYLE,
)
deu_east = html.Div([
dbc.Tabs(
[
dbc.Tab(label="BERLIN", tab_id="deu_east_berlin_id"),
dbc.Tab(label="LEIPZIG", tab_id="deu_east_leipzig_id"),
],
id="tabs",
active_tab="deu_east_berlin_id",
),
html.Div(id="deu_tab-content", className="p-4"),
])
deu_west = html.Div([
dbc.Tabs(
[
dbc.Tab(label="KOLN", tab_id="deu_west_koln_id"),
dbc.Tab(label="FRANKFURT", tab_id="deu_west_frankfurt_id"),
],
id="tabs",
active_tab="deu_west_koln_id",
),
html.Div(id="deu_tab-content", className="p-4"),
])
content = html.Div(id="deu_page_content", style=CONTENT_STYLE)
layout = html.Div([dcc.Location(id="deu_url"), sidebar, content])
deu_east_berlin_content = get_deu_east_berlin_content()
deu_east_leipzig_content = get_deu_east_leipzig_content()
deu_west_koln_content = get_deu_west_koln_content()
deu_west_frankfurt_content = get_deu_west_frankfurt_content()
@callback(
Output("deu_page_content", "children"),
[Input("deu_url", "pathname")])
def render_page_content(pathname):
if pathname == f"/DEU" or pathname == f"/DEU/east" or pathname == f"/DEU/east/berlin":
return deu_east
elif pathname == f"/DEU/west" or pathname == f"/DEU/west/koln":
return deu_west
@callback(
Output("deu_tab-content", "children"),
[Input("tabs", "active_tab")])
def render_tab_content(active_tab):
if active_tab:
if active_tab == "deu_east_berlin_id":
return deu_east_berlin_content
elif active_tab == "deu_east_leipzig_id":
return deu_east_leipzig_content
elif active_tab == "deu_west_koln_id":
return deu_west_koln_content
elif active_tab == "deu_west_frankfurt_id":
return deu_west_frankfurt_content
- how to get the main Navbar to stay at the top of the page when looking at individual sidebar page / tabs? Right now the buttons in the Navbar disappear when a tab gets displayed:
All the .py files and folders for my app above can be found here: