Hi,
I have a multi page app that is much larger than the given code snippet, I have reduced it to its simplest building blocks as a copy paste workable piece of code that shows my issue.
import panel as pn
import param
from panel.theme import DefaultTheme
class NavigationBar(param.Parameterized):
def __init__(self, app_routes_titles, **params):
super().__init__(**params)
self.app_routes_titles = app_routes_titles
self.base_urls = list(self.app_routes_titles.keys())
self.url_parameters = {}
# Create the buttons
self.buttons = self._create_buttons()
# The panel row object to show the navigation buttons
self.nav_row = pn.Row(*self.buttons, width_policy="max")
def _create_buttons(self):
buttons = []
button_type = "primary" # first button
for url, title in self.app_routes_titles.items():
button = pn.widgets.Button(
name=title,
button_type=button_type,
height=50,
width=150,
margin=(0, 0, 0, 0),
)
button_type = "light" # all other buttons
button.js_on_click(code=f"window.location.href = '{url}';")
# Attach the route as a tag for identifying later
button.tags = [url]
buttons.append(button)
return buttons
def colour_btn(self, active_page_route):
for btn in self.buttons:
tag = next(iter(btn.tags), None)
btn.button_type = "primary" if tag == active_page_route else "light"
class Sidebar(param.Parameterized):
def __init__(self, page_route, **params):
super().__init__(**params)
self.page = page_route
self.selector = pn.widgets.Select(options=["1", "2", "3"])
def get_sidebar_col(self):
"""Return the panel column of sidebar objects."""
# Append sidebar objects
sidebar_col = pn.Column(
self.selector,
self.page
)
return sidebar_col
class BasePage(param.Parameterized):
def __init__(self, template, sidebar, navigation_bar, **params):
super().__init__(**params)
self.navigation_bar = navigation_bar
self.template = template
self.template.sidebar.append(sidebar.get_sidebar_col())
# Other common set up for both pages
self.template.main.append(navigation_bar.nav_row)
class Page1(BasePage):
def __init__(self, template, sidebar, navigation_bar):
self.template = template
self.main_area = pn.Column("This is the main content of page 1")
self.template.main.append(self.main_area)
super().__init__(self.template, sidebar, navigation_bar)
class Page2(BasePage):
def __init__(self, template, sidebar, navigation_bar):
self.template = template
self.main_area = pn.Column("This is the main content of page 2")
self.template.main.append(self.main_area)
super().__init__(self.template, sidebar, navigation_bar)
def create_route(page_route, page_class, app_titles):
print("CREATING ROUTE")
def route_function():
print("EXECUTING ROUTE FUNCTION")
print(f"curdoc session id: {pn.state.curdoc.session_context.id}")
print(f"Session info: {pn.state.session_info}")
nav_bar_cls = NavigationBar(app_titles)
nav_bar_cls.colour_btn(page_route)
template = pn.template.MaterialTemplate(
title="Multi Page App", theme=DefaultTheme, busy_indicator=None
)
sidebar = Sidebar(page_route)
page = page_class(template, sidebar, nav_bar_cls)
return page.template
return route_function
def main():
app_routes_titles = {
"/": (Page1, "Page 1"),
"/2": (Page2, "Page 2")
}
app_titles = {
page_route: page_tuple[1]
for (page_route, page_tuple) in app_routes_titles.items()
}
app_routes = {
page_route: create_route(page_route, page_tuple[0], app_titles)
for (page_route, page_tuple) in app_routes_titles.items()
}
pn.serve(
app_routes,
title="Multi Page App",
port=8000,
admin=True,
location=True,
show=True
)
if __name__ == "__main__":
main()
The app is going to be hosted on an azure web app service. The issue I have is that the each time you navigate to a different page, it is a new session. This means that the sidebar, nav bar and template are recreated on load of every single page. What I would like is that the user keeps the same sidebar instance across every page. I thought this would be possible because I mistakenly thought that the user would be in the same session (rather than a new one on load of every page).
Is there any way to adapt this code so that a user will recieve the same sidebar across every page? I am thinking I may have to link all the values that I need in the sidebar into the URL and then pass them to the new page. If I make the sidebar creation outside of route_function, then it is shared by all sessions and so one user selecting a value in the selector will cause another user to see those changes (not what I want).
@Marc I know you did the multi page app documentation - and I tried to follow this by making the pn.serve use routes of urls and functions (not instances) that look like:
{"": route_function, “\2”: route_function}