Using Store component in multi pages app

Hello All,
Very new to dash…experiment multi pages app with usage of dcc.Store component.
Store component is loaded in one page, noticed ‘store’ looks empty in the other page.

Then, no dropdown displayed…

type -- <class 'dict'>
data.keys() -- dict_keys([])

I get following error message:

TypeError: fn_dropdown_continent() missing 1 required positional argument: 'continent'
dash.register_page(__name__, 
                   path='/',
                   location="sidebar")


layout = dbc.Container([
    dbc.Row([
        dbc.Col([
            html.H2('Full table'),
            html.Br(),
            html.Br(),
            html.Hr(),
            dash_table.DataTable(data=[] , id='full-table', fixed_columns={'headers':True, 'data':1})
            ]),
        ]),
    dbc.Row([
        dbc.Col([
            html.H2('dropdown'),
            dcc.Dropdown(id='dropdown-continent', options=[], value='Europe')
        ]),
    ]),
    ])

@callback(
    [Output('full-table', 'data'),
     Output('full-table', 'columns')],
    Input('store', 'data'))
def fn_full_table(data):
    print(f'type -- {type(data)}')
    print(f'data.keys() -- {data.keys()}')
    df = pd.DataFrame(data)
    return df.to_dict('records'), [{"name":i, "id": i} for i in df.columns]



@callback(
    Output('dropdown-continent', 'options'),
    [Input('store', 'data')
     ])
def fn_dropdown_continent(data, continent):
    df = pd.DataFrame(data)
    df_cont = df[df['continent'] == continent]
    return list(df_cont.unique())

Many thanks for your help on this,

Hey @Olivier_fr, that’s expected. If you need the dcc.Store() to be available on all pages, you need to put the component in your main file, most likely app.py

Thank you for your reply,

Yes, dcc.Store component is declared in app.py this way,

app = dash.Dash(__name__, 
                use_pages=True,
                external_stylesheets=[dbc.themes.SPACELAB])

sidebar = dbc.Nav(
    [dbc.NavLink(
        
        [html.Div(page['name'], className='ms-2')],
        
        href=page['path'],
        
        active='exact') for page in dash.page_registry.values()],
    
    vertical=True,
    pills=True,
    className='bg-light')




app.layout = dbc.Container([
    html.Div(
        dcc.Store(id='store', data={}, storage_type='local')),
    dbc.Row([...

And loaded in next page_1 this way,

layout = dbc.Row([
    dbc.Col([
        dcc.Store(id='store', data=df.to_dict('records')),

It works as expected,

But when trying to use it in page_2, it is not working (printing ‘data.keys()’ shows an empty dictionary)

Where am I wrong ?

Thank you for your time on this,

You do not need to add the component to the layout again. This should actually throw an error, if I’m not mistaken. Are you running the app with debug=True?

If the intention is to populate the store with data, you would have to do this via callback.

Thank you for taking time,

From what you say, it is not the correct way (in my context, that is to say, building a dash table from the store component) to load the store without activating callback

However, the page where I load the dcc.Store() directly (let’s call it page_1), without going through a callback, does not return an error. It is in the other page (page_2), where I call the ‘store’ via a callback that the store remains empty.
I will modify the code so as to load the store in page_1 via a callback, as you suggest,

Your callback has one input but you function is expecting two.

1 Like

I made changes but still not working,

Here where I am

app.py:

app.layout = dbc.Container([
    html.Div(
        dcc.Store(id='store', data={}, storage_type='local')),
    dbc.Row([
        dbc.Col(
            html.Div("Gdp -- Life exp Analysis",
            style={'fontsize':100, 'textAlign': 'center', 'font-weight': 'bold'}))]),
    html.Hr(),
...

page_1:

layout = dbc.Row([
    dbc.Col([
        html.H1('Full table', style={'textAlign': 'center'}),
        html.Br(),
        html.Hr(),
        dash_table.DataTable(data=[], id='table', page_size=10),
        html.Br(),
        html.Br(),
        ]),
    dbc.Row([
        dbc.Col([
            html.H2('Table from selected continent'),
            html.Br(),
            html.Br(),
            dcc.Dropdown(id='dropdown-continent', options=df['continent'].unique(), value='Europe'),
            dash_table.DataTable(
                id='table-continent',
                columns=[{"name":i, "id": i} for i in df.columns],
                fixed_columns={'headers': True, 'data': 1},
                page_size=15),
            ]),
        dbc.Col([
            html.H2('Graph from selected continent'),
            html.Br(),
            html.Br(),
            dcc.Graph(id='graph-from-selected-continent', figure={})]),
        ]),
    ])

**# Changes made**
**@callback(**
**    Output('store', 'data'),**
**    Input('store', 'data'))**
**def fn_store(data):**
**    if data == {}:**
**        data = df.to_dict('records')**
**    return data**

        
@callback(
    Output('table', 'data'),
    Input('store', 'data'))
def fn_table(data):
    return data


@callback(
    [Output('table-continent', 'data'),
      Output('table-continent', 'columns')],
    Input('dropdown-continent', 'value'))
def fn_dropdown(continent):
    df_c = df[df['continent'] == continent]
    return df_c.to_dict('records'), [{"name":i, "id": i} for i in df_c.columns]


@callback(
    Output('graph-from-selected-continent', 'figure'),
    Input('table-continent', 'data'))
def fn_graph(data):
    df = pd.DataFrame(data)
    fig = px.scatter(df,
                     x='gdpPercap',
                     y='lifeExp',
                     color='country',
                     hover_name='continent',
                     size='pop')
    return fig

page_2:

layout = dbc.Container([
    dbc.Row([
        dbc.Col([
            html.H2('Full table'),
            html.Br(),
            html.Br(),
            html.Hr(),
            dash_table.DataTable(data=[] , id='full-table', 
                                 fixed_columns={'headers':True, 'data':1},
                                 page_size=15)
            ]),
        ]),
    dbc.Row([
        dbc.Col([
            html.H2('dropdown'),
            dcc.Dropdown(id='dropdown-continent', options={}, value='Europe')
        ]),
    ]),
    ])


@callback(
    [Output('full-table', 'data'),
     Output('full-table', 'columns')],
    Input('store', 'data'))
def fn_full_table(data):
    #print(f'type -- {type(data)}')
    #print(f'data.keys() -- {data.keys()}')
    #print(f'data[0] -- {data[0]}')
    
    df = pd.DataFrame(data)
    return df.to_dict('records'), [{"name":i, "id": i} for i in df.columns]



@callback(
    Output('dropdown-continent', 'options'),
    [Input('store', 'data')
     ])
def fn_dropdown_continent(data, continent):
    df = pd.DataFrame(data)
    print(f'df.head() -- {df.head()}')
    df_cont = df[df['continent'] == continent]
    options = list(df_cont.unique())
    return options

So, the first callback works (in page_2), the second does not, returns error msg:

TypeError: fn_dropdown_continent() missing 1 required positional argument: 'continent'

Many thanks for your input, I really want to understand where I am wrong !

the function fn_dropdown_continent expects two arguments, but you have only one Input().

Delete the function parameter continent or use the State() of an other component.

There are actually two things to remember when working with callbacks:

  • The number function parameters has to equal the number of Input() and State() combined
  • The number of arguments returned from your function has to correspond to the number of Output()

Many thanks for your time, Code works, finally understood principles and reasons for not working !
Thanks again, :ok_hand:

1 Like