Dynamic Form/Page creation using Dash Pages

I have an authentication system working within my app and have some form adjustments between registered users and unregistered users. Using the render_page_content oriented system before Dash Pages, this was easy, but I have just converted to Dash Pages. All the page layouts are rendered once when the app starts and seem fixed from that point on. How do I reload the layouts using Dash Pages whenever the users registers or unregisters or any other reason?

Layouts don’t have to be rendered upon spin up. Convert your layouts to functions layout() to account for the current_user.is_authenticated and return your layout with it.

I have all my layouts as functions. With Dash pages, everything is rendered on spin-up and the conditionals run as current_user.is_authenticated == False before authentication is properly registered. How do I get the layouts to re-register whenever registration changes?

This is an issue with having pages registered in the app file.

If you split them out into separate pages files, the layout is dynamic.

This isn’t good. This should be documented in Multi-Page Apps and URL Support | Dash for Python Documentation | Plotly, not that I would have caught it and not proceeded.

There are two cases where I need to adjust the layout. The first is to modify the forms based on authentication. The second one is I am using ThemeSwitchAIO and still need to set coloring vars and a few CSS classes to accentuate the dark/light theming. The graphs get created properly but some CSS settings stay stuck to the original and don’t flip without the layout refresh.

The problem with multiple pages files is that I have everything built in a well structured Class with one callbacks.py and one layouts.py file. Spreading the callbacks across 10+ pages files and importing the class var from the main app in each file will be a lot of work and ugly.

And I’ve just created a /pages/mypage2.py using my code for mypage and the layout was not dynamic. :unamused: Removing all usage of dash.register_page didn’t help either. I will test this with a dummy example later.

I was hoping adding a conditionalized register_page(page) call into the callbacks could possibly work but it errors with:

raise PageError(
dash.exceptions.PageError: dash.register_page() can’t be called within a callback as it updates dash.page_registry, which is a global variable.
 For more details, see https://dash.plotly.com/sharing-data-between-callbacks#why-global-variables-will-break-your-app

According to documentation it should be dynamic, but when I was testing yesterday, it wasn’t.

I like that it’s static, for the most part, but there needs to be a method to refresh the layouts when desired, or be completely dynamic

Any other advice here before rolling back from using Dash Pages? I love the new structure and have cleaned up a ton along the way but have too many moving parts to live without the dynamic loading.

@marketemp

I’m not sure I understand - Try running this code - the time updates when you navigate to that page:

import datetime
from dash import Dash, dcc, html, register_page, page_container

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

def serve_layout():
    return html.H1('The time is: ' + str(datetime.datetime.now()))

register_page("time", layout=serve_layout)
register_page("home", path="/", layout=html.Div("home page"))

app.layout = html.Div(
    [
        dcc.Link("home", href="/"),
        html.Br(),
        dcc.Link("time", href="/time"),
        html.Hr(),
        page_container

    ]
)


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

Weird.

It didn’t work for me when I was trying to login in, it never updated the users state to be logged in.

Even though the header area was logged in.

Unless it has to do with the parentheses?

Check out the note on this page: Live Updates | Dash for Python Documentation | Plotly

Heads up! You need to write app.layout = serve_layout not app.layout = serve_layout() . That is, define app.layout to the actual function instance.

2 Likes

@marketemp,

Give that a test.

That was it! How did you know I made that mistake, as I don’t see signs of it in this thread, or was it just an experienced guess?

Now here is one more problem-solver question. Whenever I change or refresh the page, the layout updates as the demo proves. But if you click on any of the form fields in the function, the callback operates as intended without a layout update, which makes sense. What I need is a layout update whenever ThemeSwitchAIO is switched because I have my own set of light|dark className values to accentuate the light and dark theming. Without this update, a few of my Div backgrounds are stuck on whatever things were set to on the last layout update. Is this possible, or should I just remove this and stick with the BootStrap theming 100%?

So thankful this was figured out!

1 Like

I believe my original design had the parentheses. Lol.

Can you just do the styling through css and classes?

I am doing the styling through css classes but they are my classes and not bootstrap classes which would reverse with the ThemeSwitch. Therefore they get set to light|dark when the layout is last updated and not changed after hitting ThemeSwitchAIO.

I can survive without this, but the pages look much better if this is working.

How are you triggering the swap? If you piggyback on the same trigger, you should be able to alter your classes to the toggled design.

I have a var that sets my Div classNames to:

'g-0 border rounded overflow-hidden shadow-sm h-md-250 '\
'position-relative px-2 pb-2 my-2 ' + my_light_or_dark_classname

so even if I switch the var after a ThemeSwitchAIO update, the background is set with the prior layout update. Everything else nicely reverses except this background and another one I have for the fonts as the natural Slate font is a tweaky gray and I want white.

please let me know if there’s a fancier way to get these CSS settings implemented.

On the css sheet you can do this:

.mycustomclass {
  <place light mode here>
}

.slate .mycustomclass {
 <place dark mode here>
}

I don’t know what class the templates change to, but I think that should give you an idea.

I’ve defined

.mkt-paper {background-color: #F8F8F8;}
.slate .mkt-paper {background-color: #222222;}

and the light mode operates for both light and dark

You need to look at the class of the body when it gets applied in the dark mode. I don’t know what that class is.