Is there a way to synchronize dropdowns across tabs

I figured out how to synchronize dropdowns while still allowing for all tabs to be rendered at startup! This is a great benefit since it makes tab transitions much smoother, at the cost of a slightly longer initial load. The problem esentially came down to needing to know when a tab was changed. This all changed once I figured out that you can still control the value of tabs regardless of the tabs method you use.

Here is my generalized recipe for those who would like to implement this as well:


# layout portion 

dcc.Store(id='dropdown-cache', data='initial value'),

dcc.Tabs(
    id='tabs',
    value='tab-1',
    parent_className='custom-tabs',
    className='custom-tabs-container',
    children=[

    dcc.Tab(
        label='Tab 1',
        value='tab-1',
        className='custom-tab',
        selected_className='custom-tab--selected',
        children=[
            dcc.Dropdown(
                id='tab-1-dropdown',
            ),
        ]
    ),
    dcc.Tab(
        label='Tab 2',
        value='tab-2',
        className='custom-tab',
        selected_className='custom-tab--selected',
        children=[
            dcc.Dropdown(
                id='tab-2-dropdown',
            ),
        ]
    )
)

# callback portion for synchronizing dropdown across tabs. 

@app.callback(Output('dropdown-cache', 'data'),
              [Input('tab-1-dropdown', 'value'),
               Input('tab-2-dropdown', 'value')],
               [State('tabs', 'value')])
def store_dropdown_cache(tab_1_drodown_sel, tab_2_drodown_sel, tab):
    if tab == 'tab-1':
        return tab_1_drodown_sel
    elif tab == 'tab-2':
        return tab_2_drodown_sel


# Note that using drodowns-cache as an input to change the 
# dropdown value breaks the layout. I feel this has something 
# to do with circular reference, but using the state w/tab 
# value as the input callback trigger works!

@app.callback(Output('tab-1-dropdown', 'value'),
              [Input('tabs', 'value')],
              [State('dropdown-cache', 'data')])
def synchronize_dropdowns(_, cache):
    return cache

@app.callback(Output('tab-2-dropdown', 'value'),
              [Input('tabs', 'value')],
              [State('dropdown-cache', 'data')])
def synchronize_dropdowns(_, cache):
    return cache


Just as a follow up note to the comment above,

The below approach seems more intuitive, but like I mentioned earlier, it won’t work. You can’t have a circular set of callbacks. The dropdown are updating the dcc.Store components, so we can’t have that component’s value change also update the dropdown:


@app.callback(Output('tab_1_drodown', 'value'),
              [Input('dropdown-cache', 'data')])
def synchronize_dropdowns(cache):
    return cache

@app.callback(Output('tab_2_drodown', 'value'),
              [Input('dropdown-cache', 'data')])
def synchronize_dropdowns(cache):
    return cache
5 Likes