Dash Pages and Custom Index

Hello all,

Not sure if this will help anyone, but I thought it was pretty cool and helps with my design.

This app’s design uses both Dash’s pages registry and the url pathname for custom function returns.

from dash import dcc, html, no_update, Dash, Output, Input, State, page_container, ALL, ctx
import dash

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

app.layout = html.Div(
    [
        dcc.Location(id='url'),
        dcc.Link('Home', href='/'),
        html.Br(),
        dcc.Link('My Custom Layout', href='/myCustomLayout/testing'),
        html.Br(),
        html.Br(),
        dcc.Store(id='selectedStock'),
        page_container,
        html.Div(id='page-content')]
)

stockList = ['META', 'AAPL', 'AMZN', 'NFLX', 'GOOG', 'TSLA']

def myCustomLayout(v):
    return html.Div(v)

def stocks():
    layout = html.Table([
        html.Tbody([
        html.Tr([html.Td(dcc.Link(i, href='/stocks'), id={'index':i, 'type':'stock'}, n_clicks=0)]) for i in stockList
        ])
    ])
    return layout

app.clientside_callback(
    """
        function (i, d) {
            if (i.reduce((a, b) => a + b, 0) > 0) {
                stock = JSON.parse(dash_clientside.callback_context.triggered[0].prop_id.split('.n_clicks')[0]).index
                return stock
            }
            return d
        }
    """,
    Output('selectedStock', 'data'),
    Input({'index':ALL, 'type':'stock'}, 'n_clicks'),
    State('selectedStock','data'),
    prevent_initial_call=True
)

dash.register_page('Home', path='/', layout=stocks())

dash.register_page('Stock Details', path='/stocks', layout=html.Div([html.Div(id='hiddenTrigger'),
                                                                     html.Div(id='stockInfo')]))


@app.callback(
    Output('page-content', 'children'),
    Output('_pages_content','style'),
    Input('url', 'pathname')
)
def caterPage(path):
    if 'myCustomLayout' in path:
        return myCustomLayout(path.split('/myCustomLayout/')[1]), {'display':'none'}
    else:
        return '', {'display':'initial'}


@app.callback(
    Output('stockInfo', 'children'),
    Input('hiddenTrigger','n_clicks'),
    State('selectedStock','data')
)
def caterStock(v, s):
    return s

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

Why it works.

The callback that determines which routing returns a display argument to the ‘_pages_content’ (the default container for pages) which either hides or shows the content depending whether or not a custom function is run.

Hi @jinnyzor

This looks interesting, but you may not be aware that it’s possible to use path variables with Pages.

Here is a version using path variables. It’ much simpler and lets Pages do the heavy lifting for you. No clientside callabacks to extract the path variable, no callback for routing - in fact no callbacks at all :tada:


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

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

stockList = ["META", "AAPL", "AMZN", "NFLX", "GOOG", "TSLA"]


def stocks():
    layout = html.Table(
        [
            html.Tbody(
                [
                    html.Tr([html.Td(dcc.Link(i, href=f"/stocks/{i}"), n_clicks=0)])
                    for i in stockList
                ]
            )
        ]
    )
    return layout


def stock_details(stock=None):
    return html.Div(stock)


def myCustomLayout(v=None):
    return html.Div(v)


dash.register_page("Home", path="/", layout=stocks())
dash.register_page("stock_details", path_template="/stocks/<stock>", layout=stock_details)
dash.register_page("myCustomLayout", path_template="/myCustomLayout/<v>",layout=myCustomLayout)


app.layout = html.Div(
    [
        dcc.Link("Home", href="/"),
        html.Br(),
        dcc.Link("My Custom Layout", href="/myCustomLayout/testing"),
        html.Hr(),
        page_container,
    ]
)

if __name__ == "__main__":
    app.run(debug=True)

Also, If you would like to see a similar app using the pages folder, you can see this example:

Ah. Yes, the pages registry can handle variables.

But that was just a simple application example where the url had the thing driving the alteration.

What about things that cannot be passed inside of the url, dcc.Store, chart variable, etc, that the developer does not want the url to house. ie (Connecting dcc.link() and dcc.store() capabilities - #11 by jinnyzor), and they want the page to be catered with the proper layout directly.

You can pass these directly in the app.callback and directly apply them to the function.

There are other ways to share data between callbacks in a multi-page app. You can use dcc.Store as long as it’s defined in the app.layout, then it’s accessible to any page.

But to trigger the update you’d have to use a callback once the layout is applied. This would alter the layout initially.