📣 Introducing Dash `/pages` - A Dash 2.x Feature Preview

Hi @johnkangw

hmm… looks right to me. In your home.py file do you have:

dash.register_page( __name__, path="/")

One way to help with debugging is to include the code below in app.py. Place it right before the layout. (it will show what’s included in dash.page_registry:


import json
registry = {a:{c:d for c, d in b.items() if c != 'layout'} for a, b in dash.page_registry.items()}
print(json.dumps(registry, indent=4))

I haven’t used persistence with the datatable. Does the dropdown work for you?

Ann,
Thanks so much for the help. I checked the home.py and it does have that:

import dash
from dash import html


dash.register_page(
	__name__,
	path='/',
	name='Capacity Model homepage',
	description='Welcome to my app',
	order=0,
	redirect_from=['/old-home-page', '/v2'],
	extra_template_stuff='yup'
)

layout = html.Div([
    html.H1('Homepage for the app'),
    html.H2('Notes to be populated soon')
])

The JSON dump looks about right:

{
    "pages.home": {
        "module": "pages.home",
        "supplied_path": "/",
        "path": "/",
        "supplied_name": "Capacity Model homepage",
        "name": "Capacity Model homepage",
        "supplied_title": null,
        "title": "Capacity Model homepage",
        "supplied_description": "Welcome to my app",
        "description": "Welcome to my app",
        "supplied_order": 0,
        "supplied_layout": null,
        "extra_template_stuff": "yup",
        "image": "home.jpeg",
        "supplied_image": null,
        "redirect_from": [
            "/old-home-page",
            "/v2"
        ],
        "order": 0
    },
    "pages.not_found_404": {
        "module": "pages.not_found_404",
        "supplied_path": "/404",
        "path": "/404",
        "supplied_name": null,
        "name": "Not found 404",
        "supplied_title": null,
        "title": "Not found 404",
        "supplied_description": null,
        "description": "Not found 404",
        "supplied_order": null,
        "supplied_layout": null,
        "image": "app.jpeg",
        "supplied_image": null,
        "redirect_from": null
    },
    "pages.production_tracker": {
        "module": "pages.production_tracker",
        "supplied_path": "/production-tracker",
        "path": "/production-tracker",
        "supplied_name": "Production Tracker",
        "name": "Production Tracker",
        "supplied_title": "Production Tracker",
        "title": "Production Tracker",
        "supplied_description": "This is the Production Tracker",
        "description": "This is the Production Tracker",
        "supplied_order": 2,
        "supplied_layout": null,
        "image": "birds.jpeg",
        "supplied_image": "birds.jpeg",
        "redirect_from": null
    },
    "pages.rvp_view": {
        "module": "pages.rvp_view",
        "supplied_path": "/regional",
        "path": "/regional",
        "supplied_name": "Regional View",
        "name": "Regional View",
        "supplied_title": "Regional View",
        "title": "Regional View",
        "supplied_description": "This is the Regional view",
        "description": "This is the Regional view",
        "supplied_order": 1,
        "supplied_layout": null,
        "image": "birds.jpeg",
        "supplied_image": "birds.jpeg",
        "redirect_from": null
    }
}
Dash is running on http://localhost:8010/
 * Serving Flask app 'app' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on

@AnnMarieW that is a great question about the dropdown! That persistence actually works! It stays in between pages and works.

dcc.Dropdown(
        id='auction_dropdown',
        options=[{'label': auction, 'value': auction}
                 for auction in ['AAAW', 'FAAO', 'SVAA', 'MAA', 'BWAE', 'GCAA', 'PXAA', 'DFWA', 'BCAA', 'GOAA']],
        value=['AAAW', 'FAAO', 'SVAA', 'MAA', 'BWAE', 'GCAA', 'PXAA', 'DFWA', 'BCAA', 'GOAA'],  # Default value to show
        persistence=True,
        multi=True,
        searchable=False
    )

The above dropdown code is on two different pages with the same id. If I update it and navigate to the any page it persists. Maybe the persistence is an issue with the datatable? Thanks so much for your help with persistence and with looking into why the dash_labs doesn’t work (have to use pages_plugin.py).

OK, good to know that the dropdown works. It looks like the DataTable needs a little more investigation.

The dash-labs version should work. Can you try adding an __init__py file to the root of the pages folder and see if that fixes it?

@AnnMarieW Thanks for looking into the DataTable. Is there anything I need to add to the:

__init__.py

file? I just have a blank file in the pages folder. I added that in and re-ran the app but received the same error

app = Dash(__name__, plugins=[dl.plugins.pages], external_stylesheets=[dbc.themes.BOOTSTRAP, dbc_css])

error:

  File "C:/Users/Jkang1/Cox Automotive/Recon Industrial Engineering Team - General/Capacity Model/main_python_files/app.py", line 26, in <module>
    dcc.Link('Go back home', href=dash.page_registry['pages.home']['path'])
KeyError: 'pages.home'```

@AnnMarieW Oh by the way I got the ThemeChangerAIO to work for my pages and graphs! Looks super cool. The only thing that I am not able to update are the dropdown theme (I don’t think they have a theme). Would be super cool if there was a way to do that. I think from looking at the documentation the dropdowns only take in ‘style’ and not a theme. Maybe something to consider adding in as an option for the future (theme)?

Hey @johnkangw Glad you got the ThemeChangerAIO working! You just made my day! :grin:

There is also a ThemeSwitchAIO to toggle between two themes. See this post for more info: Dash Bootstrap Templates V1.0.0 New: Theme Switch Components

I’m working on a stylesheet to style Dash Core Components and the DataTable with a Bootstrap theme. The first version is now available. The initial goal is to make sure the components are usable in both light and dark themes (ie the text is visible) and to use the Bootstrap “primary” color as the accent color.

To give it a try, add this stylesheet to your external_stylesheets


dbc_css = (
    "https://cdn.jsdelivr.net/gh/AnnMarieW/dash-bootstrap-templates@V1.0.1/dbc.min.css"
)
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP, dbc_css])

Then in the layout, add the dbc class to either certain components or the entire layout like this:

app.layout = dbc.Container(
    [
        ...
    ],
    className="dbc",
    fluid=True,
)

Currently, the dropdown doesn’t have any accent colors - it’s just styled generically so the text is visible in all themes. If you have suggestions to improve the style, let me know :slight_smile:

@AnnMarieW Wow! I had to make some slight modifications because I’m already importing over the dash_bootstrap_components as dbc. I basically had your dbc as dbc_css. The below code works! Wow, this actually makes the dropdown be transparent and change font with the theme! This is super neat! Thanks so much for sharing!

Also, let me know if I setup the init.py correctly. When i tried that with dash_labs I still get the keyerror. Thanks for the help!

import dash
import dash_bootstrap_components as dbc
import pages_plugin
from dash import Dash, dcc, html
from dash_bootstrap_templates import ThemeChangerAIO
import dash_labs as dl

# load_figure_template("bootstrap")

dbc_css = (
    "https://cdn.jsdelivr.net/gh/AnnMarieW/dash-bootstrap-templates@V1.0.1/dbc.min.css"
)

app = Dash(__name__, plugins=[pages_plugin], external_stylesheets=[dbc.themes.BOOTSTRAP, dbc_css])
# app = Dash(__name__, plugins=[dl.plugins.pages], external_stylesheets=[dbc.themes.BOOTSTRAP, dbc_css])

app.layout = dbc.Container(
    html.Div([
    html.H1('Manheim Capacity Model - Work in progress'),
    html.H2('Updated as of 11/17/2021 by John Kang'),
    dbc.Row(
        [
            dbc.Col(ThemeChangerAIO(aio_id="theme", radio_props={"value": dbc.themes.DARKLY}), width=2, ),
        ]
    ),
    html.Div(
        dcc.Link('Go back home', href=dash.page_registry['pages.home']['path'])
    ),

    html.Div([
        html.Div(dcc.Link(
            f"{page['name']} - {page['path']}",
            href=page['path']
        ))
        for page in dash.page_registry.values()
        if page['module'] != 'pages.not_found_404'
    ]),

    pages_plugin.page_container

]),
    className='dbc',
    fluid=True
)

if __name__ == '__main__':
    # app.run_server(debug=True)
    app.run_server(debug=True, use_reloader=False, host="localhost", port=8051)

Hey @johnkangw

Glad the dbc_css works for you (actually, that’s how I recommend doing it -I’ll update my previous post - which was wrong)

I’m mystified about the key error. Can you provide a minimal example that reproduces the error?

Or one more thing to try… when you use the dash-labs version, also delete this line:

import pages_plugin

@AnnMarieW Hi Ann, thanks for the suggestion. I modified the code to comment out the pages_plugin and it still gives me a keyerror even when I comment out the import pages_plugin (first try) and then commennt out the keyerror and the container (second try)
First try
Code:

import dash
import dash_bootstrap_components as dbc
# import pages_plugin
from dash import Dash, dcc, html
from dash_bootstrap_templates import ThemeChangerAIO
import dash_labs as dl

# load_figure_template("bootstrap")

dbc_css = (
    "https://cdn.jsdelivr.net/gh/AnnMarieW/dash-bootstrap-templates@V1.0.1/dbc.min.css"
)

# app = Dash(__name__, plugins=[pages_plugin], external_stylesheets=[dbc.themes.BOOTSTRAP, dbc_css])
app = Dash(__name__, plugins=[dl.plugins.pages], external_stylesheets=[dbc.themes.BOOTSTRAP, dbc_css])

app.layout = dbc.Container(
    html.Div([
    html.H1('Manheim Capacity Model - Work in progress'),
    html.H2('Updated as of 11/17/2021 by John Kang'),
    dbc.Row(
        [
            dbc.Col(ThemeChangerAIO(aio_id="theme", radio_props={"value": dbc.themes.DARKLY}), width=2, ),
        ]
    ),
    html.Div(
        dcc.Link('Go back home', href=dash.page_registry['pages.home']['path'])
    ),

    html.Div([
        html.Div(dcc.Link(
            f"{page['name']} - {page['path']}",
            href=page['path']
        ))
        for page in dash.page_registry.values()
        if page['module'] != 'pages.not_found_404'
    ]),

    pages_plugin.page_container

]),
    className='dbc',
    fluid=True
)

if __name__ == '__main__':
    # app.run_server(debug=True)
    app.run_server(debug=True, use_reloader=False, host="localhost", port=8007)

first try error:

Python 3.8.12 (default, Oct 12 2021, 03:01:40) [MSC v.1916 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.29.0 -- An enhanced Interactive Python. Type '?' for help.
PyDev console: using IPython 7.29.0
Python 3.8.12 (default, Oct 12 2021, 03:01:40) [MSC v.1916 64 bit (AMD64)] on win32
runfile('C:/Users/Jkang1/Cox Automotive/Recon Industrial Engineering Team - General/Capacity Model/main_python_files/app.py', wdir='C:/Users/Jkang1/Cox Automotive/Recon Industrial Engineering Team - General/Capacity Model/main_python_files')
Traceback (most recent call last):
  File "C:\Anaconda3\envs\Capacity_venv\Lib\site-packages\IPython\core\interactiveshell.py", line 3444, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-2-a2b8dda0f18e>", line 1, in <module>
    runfile('C:/Users/Jkang1/Cox Automotive/Recon Industrial Engineering Team - General/Capacity Model/main_python_files/app.py', wdir='C:/Users/Jkang1/Cox Automotive/Recon Industrial Engineering Team - General/Capacity Model/main_python_files')
  File "C:\Program Files\JetBrains\PyCharm 2021.2.3\plugins\python\helpers\pydev\_pydev_bundle\pydev_umd.py", line 198, in runfile
    pydev_imports.execfile(filename, global_vars, local_vars)  # execute the script
  File "C:\Program Files\JetBrains\PyCharm 2021.2.3\plugins\python\helpers\pydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "C:/Users/Jkang1/Cox Automotive/Recon Industrial Engineering Team - General/Capacity Model/main_python_files/app.py", line 27, in <module>
    dcc.Link('Go back home', href=dash.page_registry['pages.home']['path'])
KeyError: 'pages.home' ```

I noticed that if I comment out the dcc.Link and the container (see code below) the dash app will run but will be in an error state:
Second try

import dash
import dash_bootstrap_components as dbc
# import pages_plugin
from dash import Dash, dcc, html
from dash_bootstrap_templates import ThemeChangerAIO
import dash_labs as dl

# load_figure_template("bootstrap")

dbc_css = (
    "https://cdn.jsdelivr.net/gh/AnnMarieW/dash-bootstrap-templates@V1.0.1/dbc.min.css"
)

# app = Dash(__name__, plugins=[pages_plugin], external_stylesheets=[dbc.themes.BOOTSTRAP, dbc_css])
app = Dash(__name__, plugins=[dl.plugins.pages], external_stylesheets=[dbc.themes.BOOTSTRAP, dbc_css])

app.layout = dbc.Container(
    html.Div([
    html.H1('Manheim Capacity Model - Work in progress'),
    html.H2('Updated as of 11/17/2021 by John Kang'),
    dbc.Row(
        [
            dbc.Col(ThemeChangerAIO(aio_id="theme", radio_props={"value": dbc.themes.DARKLY}), width=2, ),
        ]
    ),
    # html.Div(
    #     dcc.Link('Go back home', href=dash.page_registry['pages.home']['path'])
    # ),

    html.Div([
        html.Div(dcc.Link(
            f"{page['name']} - {page['path']}",
            href=page['path']
        ))
        for page in dash.page_registry.values()
        if page['module'] != 'pages.not_found_404'
    ]),

    # pages_plugin.page_container

]),
    className='dbc',
    fluid=True
)

if __name__ == '__main__':
    # app.run_server(debug=True)
    app.run_server(debug=True, use_reloader=False, host="localhost", port=8007)

The error I receive when I comment out the container (when I can load the app):
Second try error

⛑️
ID not found in layout
10:28:19 AM
Attempting to connect a callback Input item to component:
  "_pages_plugin_location"
but no components with that id exist in the layout.

If you are assigning callbacks to components that are
generated by other callbacks (and therefore not in the
initial layout), you can suppress this exception by setting
`suppress_callback_exceptions=True`.
This ID was used in the callback(s) for Output(s):
  _pages_plugin_dummy.children
  _pages_plugin_content.children
  _pages_plugin_content.children

My home.py code is standard:

import dash
from dash import html

dash.register_page(
	__name__,
	path='/',
	name='Capacity Model homepage',
	description='Welcome to my app',
	order=0,
	redirect_from=['/old-home-page', '/v2'],
	extra_template_stuff='yup'
)

layout = html.Div([
    html.H1('Homepage for the app'),
    html.H2('Notes to be populated soon')
])type or paste code here```

@johnkangw

Sorry I didn’t see this before, but when you use dash-labs , instead of pages_plugin.page_container, you need to use:

dl.plugins.page_container

@AnnMarieW Thanks so much! I made the changes but i still receive the key error.

  File "C:/Users/Jkang1/Cox Automotive/Recon Industrial Engineering Team - General/Capacity Model/main_python_files/app.py", line 26, in <module>
    dcc.Link('Go back home', href=dash.page_registry['pages.home']['path'])
KeyError: 'pages.home'

I tried commenting out line 25-27 to get rid of the error, and it goes away but then the app just loads to a 404 page. Maybe something is going on with my folder structure such that the dash_labs code can’t find my folder?

import dash
import dash_bootstrap_components as dbc
import pages_plugin
from dash import Dash, dcc, html
from dash_bootstrap_templates import ThemeChangerAIO
import dash_labs as dl
# load_figure_template("bootstrap")

dbc_css = (
    "https://cdn.jsdelivr.net/gh/AnnMarieW/dash-bootstrap-templates@V1.0.1/dbc.min.css"
)

# app = Dash(__name__, plugins=[pages_plugin], external_stylesheets=[dbc.themes.BOOTSTRAP, dbc_css])
app = Dash(__name__, plugins=[dl.plugins.pages], external_stylesheets=[dbc.themes.BOOTSTRAP, dbc_css])

app.layout = dbc.Container(
    html.Div([
    html.H1('Manheim Capacity Model - Work in progress'),
    html.H2('Updated as of 11/17/2021 by John Kang'),
    dbc.Row(
        [
            dbc.Col(ThemeChangerAIO(aio_id="theme", radio_props={"value": dbc.themes.DARKLY}), width=2, ),
        ]
    ),
    # html.Div(
    #     dcc.Link('Go back home', href=dash.page_registry['pages.home']['path'])
    # ),

    html.Div([
        html.Div(dcc.Link(
            f"{page['name']} - {page['path']}",
            href=page['path']
        ))
        for page in dash.page_registry.values()
        if page['module'] != 'pages.not_found_404'
    ]),

    # pages_plugin.page_container
    dl.plugins.page_container



]),
    className='dbc',
    fluid=True
)

if __name__ == '__main__':
    # app.run_server(debug=True)
    app.run_server(debug=True, use_reloader=False, host="localhost", port=8009)

project folder structure
A snapshot of my project folder structure (I’m using PyCharm).
The bottom of my project folder (shows the pages plugin file):
rest of project folder

This looks to be an awesome upcoming addition to Dash. Quick question, is there a way to get this to work with long_callback or some form of flask-caching since that is still bound to app? I’m guessing you would need to go back to having both an index.py and app.py file to avoid the circular imports?

2 Likes

Hi @bigmike

It’s true that @callback does not yet support long_callback.

Here’s one workaround I tried: I put the layouts that include the long_callbacks in pages/ and the @app.long_callback s in the app.py file.

It makes the index.py file unnecessary and all the new functionality of Dash pages/ still worked.

@chriddyp is there a better way?

2 Likes

That’s probably the best way for now. The ultimate solution is to have another form of long_callback that doesn’t require the app object.

Would love if this feature was included in the 2.1 release as well (plus support for even more than just one layer deep as well if possible because I have a pretty massive application that I’m trying to convert to Dash and it currently has 3 levels of pages and the additional hierarchy would be very much appreciated).

Hey @dash-beginner - I’m glad you like this feature, and I hope this will be ready for Dash 2.1, but in the meantime it’s available in Dash Labs. See more info here: 📣 Dash Labs 1.0.0: Dash `pages` - An easier way to make multi-page apps

The nested folders feature is available in Dash-Labs. If you get a chance to try it, please let us know how it works for you. It will be faster to make fixes and enhancements in Dash Labs rather than wait for the official Dash release schedule.

3 Likes

This solved my issue. Really appreciate the reply!

1 Like

:mega: I’m pleased to announce the release of Dash Labs V1.0.1

pip install dash-labs -U

This release includes two bug fixes:

  • #59 Fixed bug that prevented order prop from changing the order of the modules in dash.page_registry
  • #55 Fixed bug that prevented multipage apps from working in windows

Big thanks to @johnkangw for reporting the Windows bug and for doing the pull request to fix it! :medal_sports:

See more about how to use this feature in Dash Labs here: 📣 Dash Labs 1.0.0: Dash `pages` - An easier way to make multi-page apps

1 Like

If a page needs a static asset like an image in the layout, how would you recommend doing this when the asset is in app’s assets folder?

i.e. replacing the following:

html.Img(id=“page_icon”, src=app.get_asset_url(“page_icon.png”))