solara
solara copied to clipboard
creating a multi-page app without forcing an appbar
Is it possible to create a multi-page app without forcing the creation of an app bar with tabbed pages? I effectively have two "single-page" apps, each with their own top-level Page
component, that I need to combine into a multi-page app, with each behind a route. I tried something like the following:
import solara
from sdss_solara.pages.jdaviz_embed import Page as Embed
from sdss_explorer.pages import Page as Dashboard
@solara.component
def Home():
solara.Markdown("Solara Home")
routes = [
solara.Route(path="/", component=Home, label="Home"),
solara.Route(path="embed", component=Embed, label="Embed"),
solara.Route(path="dashboard", component=Dashboard, label="Dashboard"),
]
but this forces an AppBar, which covers up components on the "embed" page. Additionally, navigating to the home page first correctly displays the "Solara Home" text, but then toggling to the embed page and back to home, the "Solara Home" content is missing.
In this case I don't need the page navigation, I just need to point to "site/solara/embed" and "site/solara/dashboard" separately and have them render as expected.
Also for reference, I am not running this with the solara server, the combined app will be sub-mounted into an existing FastAPI app. I think that just means that the SOLARA_APP
env variable should point to the top-level solara application module.
I tried disabling the layout by adding the following in the above top-level module but that didn't seem to change anything.
@solara.component
def Layout(children=[]):
# there will only be 1 child, which is the Page()
return children[0]
Hi Brian!
I think I found the issue - you manually define routes
in the top-level module, which overrides the default behaviour (Solara trying to pick up Page
and Layout
components automagically). What you have should work if you change your root route to manually define the layout
, like so:
routes = [
solara.Route(path="/", component=Home, label="Home", layout=Layout),
...
]
I also see that we used to warn about this before https://github.com/widgetti/solara/commit/ce4a3a0f2ed1eaddb74e0aaeebb81aefa6d15295. I'll reintroduce the warning for the next person to stumble into this!
@iisakkirotko Thanks, that almost does the trick! The remaining issue is the dashboard sub page I have has its own layout, with its own AppBar and SidePanel, that I'd like to preserve. I tried the following, which mostly works. It brings back its layout but also adds the tabs back to the "Home" and "Embed" pages, which I'd like to remove. I'd like the dashboard's original layout. Is that possible?
import solara
from sdss_solara.pages.jdaviz_embed import Page as Embed
from sdss_explorer.pages import Page as Dashboard, Layout as DashLayout
@solara.component
def Layout(children=[]):
# there will only be 1 child, which is the Page()
return children[0]
@solara.component
def Home():
solara.Markdown("Solara Home")
routes = [
solara.Route(path="/", component=Home, label="Home", layout=Layout),
solara.Route(path="embed", component=Embed, label="Embed"),
solara.Route(path="dashboard", component=Dashboard, label="Dashboard", layout=DashLayout),
]
The dashboard Page
and Layout
are roughly defined like
import solara as sl
import reacton.ipyvuetify as rv
from components import sidebar, ObjectGrid, LoginButton, AlertSystem, State
@sl.component
def Page():
sl.Title("Visboard")
with sl.AppBar():
# main title object
sl.AppBarTitle(children=[rv.Icon(children=["mdi-orbit"]), " SDSS"])
# dataset selection
btn = sl.Button(label=State.dataset.value,
icon_name="mdi-database",
text=True)
if State.dataset.value is not None:
with lab.Menu(activator=btn, close_on_content_click=True):
with sl.Column(gap="0px"):
[
sl.Button(
label=dataset,
on_click=create_callable(dataset),
) for dataset in State.datasets
if dataset != State.dataset.value
]
# appbar buttons
LoginButton()
# SIDEBAR
sidebar()
# MAIN GRID
ObjectGrid()
# snackbar
AlertSystem()
@sl.component
def Layout(children):
return sl.AppLayout(sidebar_open=False, children=children, color="purple")
If I leave out the layout in the base route definition solara.Route(path="dashboard", component=Dashboard, label="Dashboard")
, then only the dashboard's main content area from the Page
, the ObjectGrid, renders.
Hey again!
Indeed the tabs are added for sibling routes. You can avoid this happening by doing something like:
routes = [
solara.Route(path="/", component=Home, label="Home", layout=Layout),
solara.Route(path="dashboard", layout=DashLayout, children=[
solara.Route(path="/", component=Dashboard, label="Dashboard")
]),
...
]
Hmm, that didn't seem to work. Both the home and embed pages render correctly, but the dashboard page still displays the sibling tabs.
Ah, I see what I missed. I think solara.AppLayout
picks up the other routes, as you can see from
https://github.com/widgetti/solara/blob/fbd9192dc853c501840b3a5e3e0dce28dcf4c403/solara/components/applayout.py#L282-L286
I think for now you'd need to make a custom implementation of the AppLayout
, since navigation=False
removes the whole AppBar
from the layout. I'll take another look at this next week and get back to you, but I feel like perhaps the condition at
https://github.com/widgetti/solara/blob/fbd9192dc853c501840b3a5e3e0dce28dcf4c403/solara/components/applayout.py#L260
isn't the most optimal, and could be changed to allow for an AppBar
to still be displayed without the tabs.
Hey again @havok2063! I think you could achieve what you're after for the dashboard page by calling solara.use_route()
in DashLayout
to override the base route, and using solara.AppBarTitle
in the Dashboard
component to force the Appbar to display.
@iisakkirotko Just getting back to this. Has there been any updates on turning off the tabbed navigation elements in an appbar?
@havok2063 I haven't gotten around to it yet, but it is on my todo-list for this week and I hope to have the time to look at it tomorrow.
@iisakkirotko Great! I think having more flexibility and control over the AppBar would be great. And as an FYI, your suggestion of adding use_route
to the layout actually did the trick of removing the page navigation tabs.
@sl.component
def Layout(children):
# force remove the navigation tabs from solara app layout
route, routes = sl.use_route()
return sl.AppLayout(sidebar_open=True, children=children, color="#424242")