Connecting dcc.link() and dcc.store() capabilities

@app.callback(
    Output('dds_stock', 'value'),
    Input('hidden_stock_trigger', 'n_clicks'),
    State('storeStock', 'data')
)
def stored_stock(v, stock):
    if not stock:
        raise PreventUpdate
    print(f'stored_stock({stock})')
    return stock

Adding the PreventUpdate call helps a little but things still get cleared when the app.clientside_callback( gets called again.

Now I see what you are after.

Ok, I think I got it. Had to do some changes because you wanted to take multiple inputs, so I mimicked the persistence by the dropdown selection triggering storing the value in the store.

from dash import dcc, html, no_update, Dash, Output, Input, State, page_container, ALL, ctx
import dash
import dash_bootstrap_components as dbc
#from dash_bootstrap_templates import load_figure_template

app = Dash(__name__, use_pages=True,
           external_stylesheets=[dbc.themes.LITERA], pages_folder='',
           suppress_callback_exceptions=True)
#load_figure_template("bootstrap")


app.layout = html.Div([
    html.Div(dcc.Link('Home', href='/')),
    html.Div(dcc.Link('Stocks', href='/stocks')),
    html.Div(dcc.Store(id='storeStock', storage_type='local')),
    page_container]
)

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


def layout_home():
    layout = html.Table([
        html.Tbody([
            html.Tr([html.Td(
                dcc.Link(sym, href='/stocks'),
                # dbc.Button(sym, href='/stocks', color='dark', className='text-white'),
                id={'index': sym, 'type': 'stock'}, n_clicks=0)]) for sym in stockList])
    ])
    return layout


def layout_stocks():
    layout = html.Div([
        html.H4('Stocks'),
        html.Div(dcc.Dropdown(id={'index':'dds_stock', 'type':'stock_dropdown'},
                              options=stockList,
                              style={'width': '100px'})),
        html.Div(id='hidden_stock_trigger'),
        html.Div(id='selected_stock')
    ])
    return layout


app.clientside_callback(
    """
        function (i, v, d) {
            console.log(dash_clientside.callback_context.triggered[0])
            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
            }
            else if (v) {
                try{
                    if (dash_clientside.callback_context.triggered[0].value != 0 && dash_clientside.callback_context.triggered[0].value) {
                        return String(dash_clientside.callback_context.triggered[0].value)
                    }
                } catch {}
            }
            return d
        }
    """,
    Output('storeStock', 'data'),
    Input({'index': ALL, 'type': 'stock'}, 'n_clicks'),
    Input({'index': ALL, 'type': 'stock_dropdown'}, 'value'),
    State('storeStock', 'data'),
    prevent_intial_call=True
)

dash.register_page('Home', path='/', layout=layout_home())
dash.register_page('Stock Details', path='/stocks', layout=layout_stocks())


@app.callback(
    Output('selected_stock', 'children'),
    Input({'index': ALL, 'type': 'stock_dropdown'}, 'value'),
)
def selected_stock(stock):
    return stock


@app.callback(
    Output({'index': ALL, 'type': 'stock_dropdown'}, 'value'),
    Input('hidden_stock_trigger', 'n_clicks'),
    State('storeStock', 'data')
)
def stored_stock(v, stock):
    print(f'stored_stock({stock})')
    return [stock]


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

1000 likes! Should have this in and working in my app tonight!

Something I noticed that wasn’t working on that other set of data that I was trying to use was that it was linking to the same page. So if I click on a link to href=/stocks/AAPL or href=/stocks?stock=AAPL the page refreshes but if I click on href=/stocks with AAPL set in the dcc.Store Dash says "I’m already on the stock page, I don’t need to go anywhere. On this page (which isn’t stocks) I am just going to go back to a dropdownMenu instead of the links.

Could open up the triggers to also be dcc store getting a new value or something along those lines.

1 Like

:disappointed: I was really hoping this would just plug and play as well as the first rev, but after a couple hours, I believe I must let this go now. The final edit I missed was id={'index': 'dds_stock', 'type': 'stock_dropdown'} in the dropdown but after that still didn’t work, I believe it is because I am not using stockList, but a callback that dynamically loads the option list from 8000 stocks based on what the user has typed and that might not be syncing with your implementation using ALL and the dcc.Store.

My code is a little cleaner for the effort, but now I have to spend time reverting back to URL oriented loading and not using Dash Pages until I can get the dynamic page loading going. At least I’ll have ThemeSwitchAIO implemented.

Thanks again for your help with these two. I hope this works for someone because the ability to link and load a value, as well as a control setting without anything in the URL should be useful.

1 Like

Pages can work with variables in the link.

Ie

stock/<ticker>

And function, stock(ticker=None).